1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.car.app;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.RequiresPermission;
22 import android.app.Activity;
23 import android.car.Car;
24 import android.car.CarManagerBase;
25 import android.car.user.CarUserManager;
26 import android.content.ActivityNotFoundException;
27 import android.content.ComponentName;
28 import android.os.IBinder;
29 import android.os.RemoteException;
30 import android.os.ServiceSpecificException;
31 
32 import com.android.internal.annotations.VisibleForTesting;
33 
34 import java.lang.annotation.ElementType;
35 import java.lang.annotation.Retention;
36 import java.lang.annotation.RetentionPolicy;
37 import java.lang.annotation.Target;
38 
39 /**
40  * API to manage {@link android.app.Activity} in Car.
41  *
42  * @hide
43  */
44 public final class CarActivityManager extends CarManagerBase {
45     private static final String TAG = CarUserManager.class.getSimpleName();
46 
47     /** Indicates that the operation was successful. */
48     public static final int RESULT_SUCCESS = 0;
49     /** Indicates that the operation was failed with the unknown reason. */
50     public static final int RESULT_FAILURE = -1;
51     /**
52      * Indicates that the operation was failed because the requester isn't the current user or
53      * the system user
54      */
55     public static final int RESULT_INVALID_USER = -2;
56 
57     /** @hide */
58     @Retention(RetentionPolicy.SOURCE)
59     @IntDef(prefix = "RESULT_", value = {
60             RESULT_SUCCESS,
61             RESULT_FAILURE,
62             RESULT_INVALID_USER,
63     })
64     @Target({ElementType.TYPE_USE})
65     public @interface ResultTypeEnum {}
66 
67     /**
68      * Internal error code for throwing {@link ActivityNotFoundException} from service.
69      * @hide
70      */
71     public static final int ERROR_CODE_ACTIVITY_NOT_FOUND = -101;
72 
73     private final ICarActivityService mService;
74 
75     /**
76      * @hide
77      */
CarActivityManager(@onNull Car car, @NonNull IBinder service)78     public CarActivityManager(@NonNull Car car, @NonNull IBinder service) {
79         this(car, ICarActivityService.Stub.asInterface(service));
80     }
81 
82     /**
83      * @hide
84      */
85     @VisibleForTesting
CarActivityManager(@onNull Car car, @NonNull ICarActivityService service)86     public CarActivityManager(@NonNull Car car, @NonNull ICarActivityService service) {
87         super(car);
88 
89         mService = service;
90     }
91 
92     /**
93      * Designates the given {@code activity} to be launched in {@code TaskDisplayArea} of
94      * {@code featureId} in the display of {@code displayId}.
95      * <p>Note: this will not affect the existing {@link Activity}.
96      * Note: You can map assign {@code Activity} to one {@code TaskDisplayArea} only. If
97      * you assign it to the multiple {@code TaskDisplayArea}s, then the last one wins.
98      * Note: The requester should be the current user or the system user, if not, the operation will
99      * be failed with {@code RESULT_INVALID_USER}.
100      *
101      * @param activity {@link Activity} to designate
102      * @param displayId {@code Display} where {@code TaskDisplayArea} is located in
103      * @param featureId {@code TaskDisplayArea} where {@link Activity} is launched in, if it is
104      *         {@code DisplayAreaOrganizer.FEATURE_UNDEFINED}, then it'll remove the existing one.
105      * @return {@code ResultTypeEnum}. {@code RESULT_SUCCESS} if the operation is successful,
106      *         otherwise, {@code RESULT_XXX} depending on the type of the error.
107      * @throws {@link IllegalArgumentException} if {@code displayId} or {@code featureId} is
108      *         invalid. {@link ActivityNotFoundException} if {@code activity} is not found
109      *         when it tries to remove.
110      */
111     @RequiresPermission(Car.PERMISSION_CONTROL_CAR_APP_LAUNCH)
112     @ResultTypeEnum
setPersistentActivity( @onNull ComponentName activity, int displayId, int featureId)113     public int setPersistentActivity(
114             @NonNull ComponentName activity, int displayId, int featureId) {
115         try {
116             return mService.setPersistentActivity(activity, displayId, featureId);
117         } catch (IllegalArgumentException | IllegalStateException | SecurityException e) {
118             throw e;
119         } catch (ServiceSpecificException e) {
120             return handleServiceSpecificFromCarService(e);
121         } catch (RemoteException | RuntimeException e) {
122             return handleExceptionFromCarService(e, RESULT_FAILURE);
123         }
124     }
125 
126     /** @hide */
127     @Override
onCarDisconnected()128     protected void onCarDisconnected() {
129         // nothing to do
130     }
131 
handleServiceSpecificFromCarService(ServiceSpecificException e)132     private int handleServiceSpecificFromCarService(ServiceSpecificException e)
133             throws ActivityNotFoundException {
134         if (e.errorCode == ERROR_CODE_ACTIVITY_NOT_FOUND) {
135             throw new ActivityNotFoundException(e.getMessage());
136         }
137         // don't know what this is
138         throw new IllegalStateException(e);
139     }
140 }
141