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