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.builtin.app; 18 19 import android.Manifest; 20 import android.annotation.NonNull; 21 import android.annotation.RequiresApi; 22 import android.annotation.RequiresPermission; 23 import android.annotation.SystemApi; 24 import android.annotation.UserIdInt; 25 import android.app.Activity; 26 import android.app.ActivityManager; 27 import android.app.ActivityOptions; 28 import android.app.ActivityTaskManager; 29 import android.app.ActivityTaskManager.RootTaskInfo; 30 import android.app.IActivityManager; 31 import android.app.IProcessObserver; 32 import android.car.builtin.annotation.AddedIn; 33 import android.car.builtin.annotation.PlatformVersion; 34 import android.car.builtin.util.Slogf; 35 import android.os.Build; 36 import android.os.Bundle; 37 import android.os.IBinder; 38 import android.os.RemoteException; 39 40 import java.util.List; 41 import java.util.concurrent.Callable; 42 43 /** 44 * Provide access to {@code android.app.IActivityManager} calls. 45 * @hide 46 */ 47 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) 48 public final class ActivityManagerHelper { 49 50 /** Invalid task ID. */ 51 @AddedIn(PlatformVersion.TIRAMISU_0) 52 public static final int INVALID_TASK_ID = ActivityTaskManager.INVALID_TASK_ID; 53 54 private static final String TAG = "CAR.AM"; // CarLog.TAG_AM 55 56 // Lazy initialization holder class idiom for static fields; See go/ej3e-83 for the detail. 57 private static class ActivityManagerHolder { 58 static final IActivityManager sAm = ActivityManager.getService(); 59 } 60 getActivityManager()61 private static IActivityManager getActivityManager() { 62 return ActivityManagerHolder.sAm; 63 } 64 ActivityManagerHelper()65 private ActivityManagerHelper() { 66 throw new UnsupportedOperationException("contains only static members"); 67 } 68 69 /** 70 * See {@code android.app.IActivityManager.startUserInBackground}. 71 * 72 * @throws IllegalStateException if ActivityManager binder throws RemoteException 73 */ 74 @AddedIn(PlatformVersion.TIRAMISU_0) startUserInBackground(@serIdInt int userId)75 public static boolean startUserInBackground(@UserIdInt int userId) { 76 return runRemotely(() -> getActivityManager().startUserInBackground(userId), 77 "error while startUserInBackground %d", userId); 78 } 79 80 /** 81 * See {@code android.app.IActivityManager.startUserInBackgroundVisibleOnDisplay}. 82 * 83 * @throws IllegalStateException if ActivityManager binder throws RemoteException 84 */ 85 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 86 @AddedIn(PlatformVersion.UPSIDE_DOWN_CAKE_0) startUserInBackgroundVisibleOnDisplay(@serIdInt int userId, int displayId)87 public static boolean startUserInBackgroundVisibleOnDisplay(@UserIdInt int userId, 88 int displayId) { 89 return runRemotely(() -> getActivityManager().startUserInBackgroundVisibleOnDisplay( 90 userId, displayId, /* unlockProgressListener= */ null), 91 "error while startUserInBackgroundVisibleOnDisplay userId:%d displayId:%d", 92 userId, displayId); 93 } 94 95 /** 96 * See {@code android.app.IActivityManager.startUserInForegroundWithListener}. 97 * 98 * @throws IllegalStateException if ActivityManager binder throws RemoteException 99 */ 100 @AddedIn(PlatformVersion.TIRAMISU_0) startUserInForeground(@serIdInt int userId)101 public static boolean startUserInForeground(@UserIdInt int userId) { 102 return runRemotely( 103 () -> getActivityManager().startUserInForegroundWithListener( 104 userId, /* listener= */ null), 105 "error while startUserInForeground %d", userId); 106 } 107 108 /** 109 * See {@code android.app.IActivityManager.stopUser}. 110 * 111 * @throws IllegalStateException if ActivityManager binder throws RemoteException 112 */ 113 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 114 @AddedIn(PlatformVersion.UPSIDE_DOWN_CAKE_0) stopUser(@serIdInt int userId, boolean force)115 public static int stopUser(@UserIdInt int userId, boolean force) { 116 return runRemotely( 117 () -> getActivityManager().stopUser(userId, force, /* callback= */ null), 118 "error while stopUser userId:%d force:%b", userId, force); 119 } 120 121 /** 122 * See {@code android.app.IActivityManager.stopUserWithDelayedLocking}. 123 * 124 * @throws IllegalStateException if ActivityManager binder throws RemoteException 125 */ 126 @AddedIn(PlatformVersion.TIRAMISU_0) stopUserWithDelayedLocking(@serIdInt int userId, boolean force)127 public static int stopUserWithDelayedLocking(@UserIdInt int userId, boolean force) { 128 return runRemotely( 129 () -> getActivityManager().stopUserWithDelayedLocking( 130 userId, force, /* callback= */ null), 131 "error while stopUserWithDelayedLocking userId:%d force:%b", userId, force); 132 } 133 134 /** 135 * Check {@code android.app.IActivityManager.unlockUser}. 136 * 137 * @throws IllegalStateException if ActivityManager binder throws RemoteException 138 */ 139 @AddedIn(PlatformVersion.TIRAMISU_0) unlockUser(@serIdInt int userId)140 public static boolean unlockUser(@UserIdInt int userId) { 141 return runRemotely(() -> getActivityManager().unlockUser2(userId, /* listener= */ null), 142 "error while unlocking user %d", userId); 143 } 144 145 /** 146 * Stops all task for the user. 147 * 148 * @throws IllegalStateException if ActivityManager binder throws RemoteException 149 */ 150 @AddedIn(PlatformVersion.TIRAMISU_0) stopAllTasksForUser(@serIdInt int userId)151 public static void stopAllTasksForUser(@UserIdInt int userId) { 152 try { 153 IActivityManager am = getActivityManager(); 154 for (RootTaskInfo info : am.getAllRootTaskInfos()) { 155 for (int i = 0; i < info.childTaskIds.length; i++) { 156 if (info.childTaskUserIds[i] == userId) { 157 int taskId = info.childTaskIds[i]; 158 if (!am.removeTask(taskId)) { 159 Slogf.w(TAG, "could not remove task " + taskId); 160 } 161 } 162 } 163 } 164 } catch (RemoteException e) { 165 throw logAndReThrow(e, "could not get stack info for user %d", userId); 166 } 167 } 168 169 /** 170 * Creates an ActivityOptions from the Bundle generated from ActivityOptions. 171 */ 172 @NonNull 173 @AddedIn(PlatformVersion.TIRAMISU_0) createActivityOptions(@onNull Bundle bOptions)174 public static ActivityOptions createActivityOptions(@NonNull Bundle bOptions) { 175 return new ActivityOptions(bOptions); 176 } 177 runRemotely(Callable<T> callable, String format, Object...args)178 private static <T> T runRemotely(Callable<T> callable, String format, Object...args) { 179 try { 180 return callable.call(); 181 } catch (Exception e) { 182 throw logAndReThrow(e, format, args); 183 } 184 } 185 186 @SuppressWarnings("AnnotateFormatMethod") logAndReThrow(Exception e, String format, Object...args)187 private static RuntimeException logAndReThrow(Exception e, String format, Object...args) { 188 String msg = String.format(format, args); 189 Slogf.e(TAG, msg, e); 190 return new IllegalStateException(msg, e); 191 } 192 193 /** 194 * Makes the root task of the given taskId focused. 195 */ 196 @AddedIn(PlatformVersion.TIRAMISU_0) setFocusedRootTask(int taskId)197 public static void setFocusedRootTask(int taskId) { 198 try { 199 getActivityManager().setFocusedRootTask(taskId); 200 } catch (RemoteException e) { 201 Slogf.e(TAG, "Failed to setFocusedRootTask", e); 202 } 203 } 204 205 /** 206 * Removes the given task. 207 */ 208 @AddedIn(PlatformVersion.TIRAMISU_0) removeTask(int taskId)209 public static boolean removeTask(int taskId) { 210 try { 211 return getActivityManager().removeTask(taskId); 212 } catch (RemoteException e) { 213 Slogf.e(TAG, "Failed to removeTask", e); 214 } 215 return false; 216 } 217 218 /** 219 * Callback to monitor Processes in the system 220 */ 221 public abstract static class ProcessObserverCallback { 222 /** Called when the foreground Activities are changed. */ 223 @AddedIn(PlatformVersion.TIRAMISU_0) onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities)224 public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) { 225 } 226 /** Called when the Process is died. */ 227 @AddedIn(PlatformVersion.TIRAMISU_0) onProcessDied(int pid, int uid)228 public void onProcessDied(int pid, int uid) {} 229 230 final IProcessObserver.Stub mIProcessObserver = new IProcessObserver.Stub() { 231 @Override 232 public void onForegroundActivitiesChanged( 233 int pid, int uid, boolean foregroundActivities) throws RemoteException { 234 ProcessObserverCallback.this.onForegroundActivitiesChanged( 235 pid, uid, foregroundActivities); 236 } 237 238 @Override 239 public void onForegroundServicesChanged(int pid, int uid, int fgServiceTypes) 240 throws RemoteException { 241 // Not used 242 } 243 244 @Override 245 public void onProcessDied(int pid, int uid) throws RemoteException { 246 ProcessObserverCallback.this.onProcessDied(pid, uid); 247 } 248 }; 249 } 250 251 /** 252 * Registers a callback to be invoked when the process states are changed. 253 * @param callback a callback to register 254 */ 255 @AddedIn(PlatformVersion.TIRAMISU_0) registerProcessObserverCallback(ProcessObserverCallback callback)256 public static void registerProcessObserverCallback(ProcessObserverCallback callback) { 257 try { 258 getActivityManager().registerProcessObserver(callback.mIProcessObserver); 259 } catch (RemoteException e) { 260 Slogf.e(TAG, "Failed to register ProcessObserver", e); 261 throw new RuntimeException(e); 262 } 263 } 264 265 /** 266 * Unregisters the given callback. 267 * @param callback a callback to unregister 268 */ 269 @AddedIn(PlatformVersion.TIRAMISU_0) unregisterProcessObserverCallback(ProcessObserverCallback callback)270 public static void unregisterProcessObserverCallback(ProcessObserverCallback callback) { 271 try { 272 getActivityManager().unregisterProcessObserver(callback.mIProcessObserver); 273 } catch (RemoteException e) { 274 Slogf.e(TAG, "Failed to unregister listener", e); 275 throw new RuntimeException(e); 276 } 277 } 278 279 /** 280 * Same as {@link ActivityManager#checkComponentPermission(String, int, int, boolean). 281 */ 282 @AddedIn(PlatformVersion.TIRAMISU_0) checkComponentPermission(@onNull String permission, int uid, int owningUid, boolean exported)283 public static int checkComponentPermission(@NonNull String permission, int uid, int owningUid, 284 boolean exported) { 285 return ActivityManager.checkComponentPermission(permission, uid, owningUid, exported); 286 } 287 288 /** See {@link android.app.ActivityTaskManager#getTasks(int, boolean, boolean, int)} */ 289 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 290 @AddedIn(PlatformVersion.UPSIDE_DOWN_CAKE_0) getTasks(int maxNum, boolean filterOnlyVisibleRecents, boolean keepIntentExtra, int displayId)291 public static List<ActivityManager.RunningTaskInfo> getTasks(int maxNum, 292 boolean filterOnlyVisibleRecents, boolean keepIntentExtra, int displayId) { 293 return ActivityTaskManager.getInstance().getTasks(maxNum, filterOnlyVisibleRecents, 294 keepIntentExtra, displayId); 295 } 296 297 /** 298 * Same as {@link ActivityManager#killAllBackgroundProcesses()} 299 */ 300 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 301 @AddedIn(PlatformVersion.UPSIDE_DOWN_CAKE_0) killAllBackgroundProcesses()302 public static void killAllBackgroundProcesses() { 303 try { 304 getActivityManager().killAllBackgroundProcesses(); 305 } catch (RemoteException e) { 306 Slogf.e(TAG, "Failed to kill background apps", e); 307 throw new RuntimeException(e); 308 } 309 } 310 311 /** 312 * Same as {@link ActivityManager#killUid()} 313 */ 314 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 315 @AddedIn(PlatformVersion.UPSIDE_DOWN_CAKE_0) killUid(int appId, int userId, String reason)316 public static void killUid(int appId, int userId, String reason) { 317 try { 318 getActivityManager().killUid(appId, userId, reason); 319 } catch (RemoteException e) { 320 Slogf.e(TAG, "Failed to call app : %d , userId: %d, kill reason: %s", appId, userId, 321 reason); 322 throw new RuntimeException(e); 323 } 324 } 325 326 /** See {@link Activity#getActivityToken()} */ 327 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 328 @AddedIn(PlatformVersion.UPSIDE_DOWN_CAKE_0) getActivityToken(Activity activity)329 public static IBinder getActivityToken(Activity activity) { 330 return activity.getActivityToken(); 331 } 332 333 /** See {@link Activity#isVisibleForAutofill()} */ 334 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 335 @AddedIn(PlatformVersion.UPSIDE_DOWN_CAKE_0) isVisible(Activity activity)336 public static boolean isVisible(Activity activity) { 337 return activity.isVisibleForAutofill(); 338 } 339 340 /** 341 * Moves the given {@code RootTask} to the specified {@code Display}. 342 * 343 * @param taskId the id of the target {@code RootTask} to move 344 * @param displayId the displayId to move the {@code RootTask} to 345 */ 346 @RequiresPermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW) 347 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 348 @AddedIn(PlatformVersion.UPSIDE_DOWN_CAKE_0) moveRootTaskToDisplay(int taskId, int displayId)349 public static void moveRootTaskToDisplay(int taskId, int displayId) { 350 try { 351 ActivityTaskManager.getService().moveRootTaskToDisplay(taskId, displayId); 352 } catch (RemoteException e) { 353 Slogf.e(TAG, "Error moving task %d to display %d", e, taskId, displayId); 354 throw new RuntimeException(e); 355 } 356 } 357 } 358