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.annotation.SystemApi; 23 import android.annotation.TestApi; 24 import android.app.Activity; 25 import android.app.ActivityManager; 26 import android.car.Car; 27 import android.car.CarManagerBase; 28 import android.car.annotation.AddedInOrBefore; 29 import android.car.annotation.ApiRequirements; 30 import android.car.user.CarUserManager; 31 import android.content.ActivityNotFoundException; 32 import android.content.ComponentName; 33 import android.os.Binder; 34 import android.os.IBinder; 35 import android.os.RemoteException; 36 import android.os.ServiceSpecificException; 37 import android.util.Log; 38 39 import com.android.internal.annotations.VisibleForTesting; 40 41 import java.lang.annotation.ElementType; 42 import java.lang.annotation.Retention; 43 import java.lang.annotation.RetentionPolicy; 44 import java.lang.annotation.Target; 45 import java.util.Collections; 46 import java.util.List; 47 48 /** 49 * API to manage {@link android.app.Activity} in Car. 50 * 51 * @hide 52 */ 53 @SystemApi 54 @TestApi 55 public final class CarActivityManager extends CarManagerBase { 56 private static final String TAG = CarUserManager.class.getSimpleName(); 57 58 /** Indicates that the operation was successful. */ 59 @AddedInOrBefore(majorVersion = 33) 60 public static final int RESULT_SUCCESS = 0; 61 /** Indicates that the operation was failed with the unknown reason. */ 62 @AddedInOrBefore(majorVersion = 33) 63 public static final int RESULT_FAILURE = -1; 64 /** 65 * Indicates that the operation was failed because the requester isn't the current user or 66 * the system user 67 */ 68 @AddedInOrBefore(majorVersion = 33) 69 public static final int RESULT_INVALID_USER = -2; 70 71 /** @hide */ 72 @Retention(RetentionPolicy.SOURCE) 73 @IntDef(prefix = "RESULT_", value = { 74 RESULT_SUCCESS, 75 RESULT_FAILURE, 76 RESULT_INVALID_USER, 77 }) 78 @Target({ElementType.TYPE_USE}) 79 public @interface ResultTypeEnum {} 80 81 /** 82 * Internal error code for throwing {@link ActivityNotFoundException} from service. 83 * @hide 84 */ 85 @AddedInOrBefore(majorVersion = 33) 86 public static final int ERROR_CODE_ACTIVITY_NOT_FOUND = -101; 87 88 private final ICarActivityService mService; 89 private IBinder mTaskMonitorToken; 90 91 /** 92 * @hide 93 */ CarActivityManager(@onNull Car car, @NonNull IBinder service)94 public CarActivityManager(@NonNull Car car, @NonNull IBinder service) { 95 this(car, ICarActivityService.Stub.asInterface(service)); 96 } 97 98 /** 99 * @hide 100 */ 101 @VisibleForTesting CarActivityManager(@onNull Car car, @NonNull ICarActivityService service)102 public CarActivityManager(@NonNull Car car, @NonNull ICarActivityService service) { 103 super(car); 104 105 mService = service; 106 } 107 108 /** 109 * Designates the given {@code activity} to be launched in {@code TaskDisplayArea} of 110 * {@code featureId} in the display of {@code displayId}. 111 * <p>Note: this will not affect the existing {@link Activity}. 112 * Note: You can map assign {@code Activity} to one {@code TaskDisplayArea} only. If 113 * you assign it to the multiple {@code TaskDisplayArea}s, then the last one wins. 114 * Note: The requester should be the current user or the system user, if not, the operation will 115 * be failed with {@code RESULT_INVALID_USER}. 116 * 117 * @param activity {@link Activity} to designate 118 * @param displayId {@code Display} where {@code TaskDisplayArea} is located in 119 * @param featureId {@code TaskDisplayArea} where {@link Activity} is launched in, if it is 120 * {@code DisplayAreaOrganizer.FEATURE_UNDEFINED}, then it'll remove the existing one. 121 * @return {@code ResultTypeEnum}. {@code RESULT_SUCCESS} if the operation is successful, 122 * otherwise, {@code RESULT_XXX} depending on the type of the error. 123 * @throws {@link IllegalArgumentException} if {@code displayId} or {@code featureId} is 124 * invalid. {@link ActivityNotFoundException} if {@code activity} is not found 125 * when it tries to remove. 126 */ 127 @RequiresPermission(Car.PERMISSION_CONTROL_CAR_APP_LAUNCH) 128 @ResultTypeEnum 129 @AddedInOrBefore(majorVersion = 33) setPersistentActivity( @onNull ComponentName activity, int displayId, int featureId)130 public int setPersistentActivity( 131 @NonNull ComponentName activity, int displayId, int featureId) { 132 try { 133 return mService.setPersistentActivity(activity, displayId, featureId); 134 } catch (IllegalArgumentException | IllegalStateException | SecurityException e) { 135 throw e; 136 } catch (ServiceSpecificException e) { 137 return handleServiceSpecificFromCarService(e); 138 } catch (RemoteException | RuntimeException e) { 139 return handleExceptionFromCarService(e, RESULT_FAILURE); 140 } 141 } 142 143 /** @hide */ 144 @Override 145 @AddedInOrBefore(majorVersion = 33) onCarDisconnected()146 protected void onCarDisconnected() { 147 mTaskMonitorToken = null; 148 } 149 handleServiceSpecificFromCarService(ServiceSpecificException e)150 private int handleServiceSpecificFromCarService(ServiceSpecificException e) 151 throws ActivityNotFoundException { 152 if (e.errorCode == ERROR_CODE_ACTIVITY_NOT_FOUND) { 153 throw new ActivityNotFoundException(e.getMessage()); 154 } 155 // don't know what this is 156 throw new IllegalStateException(e); 157 } 158 159 /** 160 * Registers the caller as TaskMonitor, which can provide Task lifecycle events to CarService. 161 * The caller should provide a binder token, which is used to check if the given TaskMonitor is 162 * live and the reported events are from the legitimate TaskMonitor. 163 * @hide 164 */ 165 @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) 166 @AddedInOrBefore(majorVersion = 33) registerTaskMonitor()167 public boolean registerTaskMonitor() { 168 IBinder token = new Binder(); 169 try { 170 mService.registerTaskMonitor(token); 171 mTaskMonitorToken = token; 172 return true; 173 } catch (RemoteException e) { 174 handleRemoteExceptionFromCarService(e); 175 } 176 return false; 177 } 178 179 /** 180 * Reports that a Task is created. 181 * @hide 182 */ 183 @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) 184 @AddedInOrBefore(majorVersion = 33) onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo)185 public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo) { 186 if (!hasValidToken()) return; 187 try { 188 mService.onTaskAppeared(mTaskMonitorToken, taskInfo); 189 } catch (RemoteException e) { 190 handleRemoteExceptionFromCarService(e); 191 } 192 } 193 194 /** 195 * Reports that a Task is vanished. 196 * @hide 197 */ 198 @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) 199 @AddedInOrBefore(majorVersion = 33) onTaskVanished(ActivityManager.RunningTaskInfo taskInfo)200 public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { 201 if (!hasValidToken()) return; 202 try { 203 mService.onTaskVanished(mTaskMonitorToken, taskInfo); 204 } catch (RemoteException e) { 205 handleRemoteExceptionFromCarService(e); 206 } 207 } 208 209 /** 210 * Reports that some Task's states are changed. 211 * @hide 212 */ 213 @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) 214 @AddedInOrBefore(majorVersion = 33) onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo)215 public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { 216 if (!hasValidToken()) return; 217 try { 218 mService.onTaskInfoChanged(mTaskMonitorToken, taskInfo); 219 } catch (RemoteException e) { 220 handleRemoteExceptionFromCarService(e); 221 } 222 } 223 224 /** 225 * Unregisters the caller from TaskMonitor. 226 * @hide 227 */ 228 @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) 229 @AddedInOrBefore(majorVersion = 33) unregisterTaskMonitor()230 public void unregisterTaskMonitor() { 231 if (!hasValidToken()) return; 232 try { 233 mService.unregisterTaskMonitor(mTaskMonitorToken); 234 mTaskMonitorToken = null; 235 } catch (RemoteException e) { 236 handleRemoteExceptionFromCarService(e); 237 } 238 } 239 240 /** 241 * Returns all the visible tasks. The order is not guaranteed. 242 * @hide 243 */ 244 @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) 245 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_1, 246 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 247 @NonNull getVisibleTasks()248 public List<ActivityManager.RunningTaskInfo> getVisibleTasks() { 249 try { 250 return mService.getVisibleTasks(); 251 } catch (RemoteException e) { 252 handleRemoteExceptionFromCarService(e); 253 } 254 return Collections.emptyList(); 255 } 256 hasValidToken()257 private boolean hasValidToken() { 258 boolean valid = mTaskMonitorToken != null; 259 if (!valid) { 260 Log.w(TAG, "Has invalid token, skip the operation: " 261 + new Throwable().getStackTrace()[1].getMethodName()); 262 } 263 return valid; 264 } 265 } 266