• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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 com.android.systemui.recents.misc;
18 
19 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
20 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
21 import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
22 import static android.app.ActivityManager.StackId.HOME_STACK_ID;
23 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
24 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
25 import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
26 import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
27 
28 import android.annotation.NonNull;
29 import android.annotation.Nullable;
30 import android.app.ActivityManager;
31 import android.app.ActivityManager.StackInfo;
32 import android.app.ActivityManager.TaskSnapshot;
33 import android.app.ActivityOptions;
34 import android.app.AppGlobals;
35 import android.app.IActivityManager;
36 import android.app.KeyguardManager;
37 import android.content.ComponentName;
38 import android.content.ContentResolver;
39 import android.content.Context;
40 import android.content.Intent;
41 import android.content.pm.ActivityInfo;
42 import android.content.pm.ApplicationInfo;
43 import android.content.pm.IPackageManager;
44 import android.content.pm.PackageManager;
45 import android.content.pm.ResolveInfo;
46 import android.content.res.Resources;
47 import android.graphics.Bitmap;
48 import android.graphics.BitmapFactory;
49 import android.graphics.Canvas;
50 import android.graphics.Color;
51 import android.graphics.Paint;
52 import android.graphics.Point;
53 import android.graphics.PorterDuff;
54 import android.graphics.PorterDuffXfermode;
55 import android.graphics.Rect;
56 import android.graphics.drawable.BitmapDrawable;
57 import android.graphics.drawable.ColorDrawable;
58 import android.graphics.drawable.Drawable;
59 import android.os.Handler;
60 import android.os.IRemoteCallback;
61 import android.os.Message;
62 import android.os.ParcelFileDescriptor;
63 import android.os.RemoteException;
64 import android.os.ServiceManager;
65 import android.os.SystemProperties;
66 import android.os.Trace;
67 import android.os.UserHandle;
68 import android.os.UserManager;
69 import android.provider.Settings;
70 import android.provider.Settings.Secure;
71 import android.service.dreams.DreamService;
72 import android.service.dreams.IDreamManager;
73 import android.util.ArraySet;
74 import android.util.IconDrawableFactory;
75 import android.util.Log;
76 import android.util.MutableBoolean;
77 import android.view.Display;
78 import android.view.IAppTransitionAnimationSpecsFuture;
79 import android.view.IDockedStackListener;
80 import android.view.IWindowManager;
81 import android.view.WindowManager;
82 import android.view.WindowManager.KeyboardShortcutsReceiver;
83 import android.view.WindowManagerGlobal;
84 import android.view.accessibility.AccessibilityManager;
85 
86 import com.android.internal.app.AssistUtils;
87 import com.android.internal.os.BackgroundThread;
88 import com.android.systemui.Dependency;
89 import com.android.systemui.R;
90 import com.android.systemui.UiOffloadThread;
91 import com.android.systemui.pip.tv.PipMenuActivity;
92 import com.android.systemui.recents.Recents;
93 import com.android.systemui.recents.RecentsDebugFlags;
94 import com.android.systemui.recents.RecentsImpl;
95 import com.android.systemui.recents.model.Task;
96 import com.android.systemui.recents.model.ThumbnailData;
97 import com.android.systemui.statusbar.policy.UserInfoController;
98 import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener;
99 
100 import java.io.IOException;
101 import java.util.ArrayList;
102 import java.util.Collections;
103 import java.util.Iterator;
104 import java.util.List;
105 import java.util.Random;
106 
107 /**
108  * Acts as a shim around the real system services that we need to access data from, and provides
109  * a point of injection when testing UI.
110  */
111 public class SystemServicesProxy {
112     final static String TAG = "SystemServicesProxy";
113 
114     final static BitmapFactory.Options sBitmapOptions;
115     static {
116         sBitmapOptions = new BitmapFactory.Options();
117         sBitmapOptions.inMutable = true;
118         sBitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565;
119     }
120 
121     final static List<String> sRecentsBlacklist;
122     static {
123         sRecentsBlacklist = new ArrayList<>();
PipMenuActivity.class.getName()124         sRecentsBlacklist.add(PipMenuActivity.class.getName());
125     }
126 
127     private static SystemServicesProxy sSystemServicesProxy;
128 
129     AccessibilityManager mAccm;
130     ActivityManager mAm;
131     IActivityManager mIam;
132     PackageManager mPm;
133     IconDrawableFactory mDrawableFactory;
134     IPackageManager mIpm;
135     private final IDreamManager mDreamManager;
136     private final Context mContext;
137     AssistUtils mAssistUtils;
138     WindowManager mWm;
139     IWindowManager mIwm;
140     KeyguardManager mKgm;
141     UserManager mUm;
142     Display mDisplay;
143     String mRecentsPackage;
144     ComponentName mAssistComponent;
145     private int mCurrentUserId;
146 
147     boolean mIsSafeMode;
148     boolean mHasFreeformWorkspaceSupport;
149 
150     Bitmap mDummyIcon;
151     int mDummyThumbnailWidth;
152     int mDummyThumbnailHeight;
153     Paint mBgProtectionPaint;
154     Canvas mBgProtectionCanvas;
155 
156     private final Handler mHandler = new H();
157     private final Runnable mGcRunnable = new Runnable() {
158         @Override
159         public void run() {
160             System.gc();
161             System.runFinalization();
162         }
163     };
164 
165     private final UiOffloadThread mUiOffloadThread = Dependency.get(UiOffloadThread.class);
166 
167     /**
168      * An abstract class to track task stack changes.
169      * Classes should implement this instead of {@link android.app.ITaskStackListener}
170      * to reduce IPC calls from system services. These callbacks will be called on the main thread.
171      */
172     public abstract static class TaskStackListener {
173         /**
174          * NOTE: This call is made of the thread that the binder call comes in on.
175          */
onTaskStackChangedBackground()176         public void onTaskStackChangedBackground() { }
onTaskStackChanged()177         public void onTaskStackChanged() { }
onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot)178         public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) { }
onActivityPinned(String packageName, int userId, int taskId)179         public void onActivityPinned(String packageName, int userId, int taskId) { }
onActivityUnpinned()180         public void onActivityUnpinned() { }
onPinnedActivityRestartAttempt(boolean clearedTask)181         public void onPinnedActivityRestartAttempt(boolean clearedTask) { }
onPinnedStackAnimationStarted()182         public void onPinnedStackAnimationStarted() { }
onPinnedStackAnimationEnded()183         public void onPinnedStackAnimationEnded() { }
onActivityForcedResizable(String packageName, int taskId, int reason)184         public void onActivityForcedResizable(String packageName, int taskId, int reason) { }
onActivityDismissingDockedStack()185         public void onActivityDismissingDockedStack() { }
onActivityLaunchOnSecondaryDisplayFailed()186         public void onActivityLaunchOnSecondaryDisplayFailed() { }
onTaskProfileLocked(int taskId, int userId)187         public void onTaskProfileLocked(int taskId, int userId) { }
188 
189         /**
190          * Checks that the current user matches the user's SystemUI process. Since
191          * {@link android.app.ITaskStackListener} is not multi-user aware, handlers of
192          * TaskStackListener should make this call to verify that we don't act on events from other
193          * user's processes.
194          */
checkCurrentUserId(Context context, boolean debug)195         protected final boolean checkCurrentUserId(Context context, boolean debug) {
196             int processUserId = UserHandle.myUserId();
197             int currentUserId = SystemServicesProxy.getInstance(context).getCurrentUser();
198             if (processUserId != currentUserId) {
199                 if (debug) {
200                     Log.d(TAG, "UID mismatch. SystemUI is running uid=" + processUserId
201                             + " and the current user is uid=" + currentUserId);
202                 }
203                 return false;
204             }
205             return true;
206         }
207     }
208 
209     /**
210      * Implementation of {@link android.app.ITaskStackListener} to listen task stack changes from
211      * ActivityManagerService.
212      * This simply passes callbacks to listeners through {@link H}.
213      * */
214     private android.app.TaskStackListener mTaskStackListener = new android.app.TaskStackListener() {
215 
216         private final List<SystemServicesProxy.TaskStackListener> mTmpListeners = new ArrayList<>();
217 
218         @Override
219         public void onTaskStackChanged() throws RemoteException {
220             // Call the task changed callback for the non-ui thread listeners first
221             synchronized (mTaskStackListeners) {
222                 mTmpListeners.clear();
223                 mTmpListeners.addAll(mTaskStackListeners);
224             }
225             for (int i = mTmpListeners.size() - 1; i >= 0; i--) {
226                 mTmpListeners.get(i).onTaskStackChangedBackground();
227             }
228 
229             mHandler.removeMessages(H.ON_TASK_STACK_CHANGED);
230             mHandler.sendEmptyMessage(H.ON_TASK_STACK_CHANGED);
231         }
232 
233         @Override
234         public void onActivityPinned(String packageName, int userId, int taskId)
235                 throws RemoteException {
236             mHandler.removeMessages(H.ON_ACTIVITY_PINNED);
237             mHandler.obtainMessage(H.ON_ACTIVITY_PINNED, userId, taskId, packageName).sendToTarget();
238         }
239 
240         @Override
241         public void onActivityUnpinned() throws RemoteException {
242             mHandler.removeMessages(H.ON_ACTIVITY_UNPINNED);
243             mHandler.sendEmptyMessage(H.ON_ACTIVITY_UNPINNED);
244         }
245 
246         @Override
247         public void onPinnedActivityRestartAttempt(boolean clearedTask)
248                 throws RemoteException{
249             mHandler.removeMessages(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT);
250             mHandler.obtainMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT, clearedTask ? 1 : 0, 0)
251                     .sendToTarget();
252         }
253 
254         @Override
255         public void onPinnedStackAnimationStarted() throws RemoteException {
256             mHandler.removeMessages(H.ON_PINNED_STACK_ANIMATION_STARTED);
257             mHandler.sendEmptyMessage(H.ON_PINNED_STACK_ANIMATION_STARTED);
258         }
259 
260         @Override
261         public void onPinnedStackAnimationEnded() throws RemoteException {
262             mHandler.removeMessages(H.ON_PINNED_STACK_ANIMATION_ENDED);
263             mHandler.sendEmptyMessage(H.ON_PINNED_STACK_ANIMATION_ENDED);
264         }
265 
266         @Override
267         public void onActivityForcedResizable(String packageName, int taskId, int reason)
268                 throws RemoteException {
269             mHandler.obtainMessage(H.ON_ACTIVITY_FORCED_RESIZABLE, taskId, reason, packageName)
270                     .sendToTarget();
271         }
272 
273         @Override
274         public void onActivityDismissingDockedStack() throws RemoteException {
275             mHandler.sendEmptyMessage(H.ON_ACTIVITY_DISMISSING_DOCKED_STACK);
276         }
277 
278         @Override
279         public void onActivityLaunchOnSecondaryDisplayFailed() throws RemoteException {
280             mHandler.sendEmptyMessage(H.ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED);
281         }
282 
283         @Override
284         public void onTaskProfileLocked(int taskId, int userId) {
285             mHandler.obtainMessage(H.ON_TASK_PROFILE_LOCKED, taskId, userId).sendToTarget();
286         }
287 
288         @Override
289         public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot)
290                 throws RemoteException {
291             mHandler.obtainMessage(H.ON_TASK_SNAPSHOT_CHANGED, taskId, 0, snapshot).sendToTarget();
292         }
293     };
294 
295     private final UserInfoController.OnUserInfoChangedListener mOnUserInfoChangedListener =
296             (String name, Drawable picture, String userAccount) ->
297                     mCurrentUserId = mAm.getCurrentUser();
298 
299     /**
300      * List of {@link TaskStackListener} registered from {@link #registerTaskStackListener}.
301      */
302     private List<TaskStackListener> mTaskStackListeners = new ArrayList<>();
303 
304     /** Private constructor */
SystemServicesProxy(Context context)305     private SystemServicesProxy(Context context) {
306         mContext = context.getApplicationContext();
307         mAccm = AccessibilityManager.getInstance(context);
308         mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
309         mIam = ActivityManager.getService();
310         mPm = context.getPackageManager();
311         mDrawableFactory = IconDrawableFactory.newInstance(context);
312         mIpm = AppGlobals.getPackageManager();
313         mAssistUtils = new AssistUtils(context);
314         mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
315         mIwm = WindowManagerGlobal.getWindowManagerService();
316         mKgm = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
317         mUm = UserManager.get(context);
318         mDreamManager = IDreamManager.Stub.asInterface(
319                 ServiceManager.checkService(DreamService.DREAM_SERVICE));
320         mDisplay = mWm.getDefaultDisplay();
321         mRecentsPackage = context.getPackageName();
322         mHasFreeformWorkspaceSupport =
323                 mPm.hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT) ||
324                         Settings.Global.getInt(context.getContentResolver(),
325                                 DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
326         mIsSafeMode = mPm.isSafeMode();
327         mCurrentUserId = mAm.getCurrentUser();
328 
329         // Get the dummy thumbnail width/heights
330         Resources res = context.getResources();
331         int wId = com.android.internal.R.dimen.thumbnail_width;
332         int hId = com.android.internal.R.dimen.thumbnail_height;
333         mDummyThumbnailWidth = res.getDimensionPixelSize(wId);
334         mDummyThumbnailHeight = res.getDimensionPixelSize(hId);
335 
336         // Create the protection paints
337         mBgProtectionPaint = new Paint();
338         mBgProtectionPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
339         mBgProtectionPaint.setColor(0xFFffffff);
340         mBgProtectionCanvas = new Canvas();
341 
342         // Resolve the assist intent
343         mAssistComponent = mAssistUtils.getAssistComponentForUser(UserHandle.myUserId());
344 
345         // Since SystemServicesProxy can be accessed from a per-SysUI process component, create a
346         // per-process listener to keep track of the current user id to reduce the number of binder
347         // calls to fetch it.
348         UserInfoController userInfoController = Dependency.get(UserInfoController.class);
349         userInfoController.addCallback(mOnUserInfoChangedListener);
350 
351         if (RecentsDebugFlags.Static.EnableMockTasks) {
352             // Create a dummy icon
353             mDummyIcon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
354             mDummyIcon.eraseColor(0xFF999999);
355         }
356 
357         Collections.addAll(sRecentsBlacklist,
358                 res.getStringArray(R.array.recents_blacklist_array));
359     }
360 
361     /**
362      * Returns the single instance of the {@link SystemServicesProxy}.
363      * This should only be called on the main thread.
364      */
getInstance(Context context)365     public static synchronized SystemServicesProxy getInstance(Context context) {
366         if (sSystemServicesProxy == null) {
367             sSystemServicesProxy = new SystemServicesProxy(context);
368         }
369         return sSystemServicesProxy;
370     }
371 
372     /**
373      * Requests a gc() from the background thread.
374      */
gc()375     public void gc() {
376         BackgroundThread.getHandler().post(mGcRunnable);
377     }
378 
379     /**
380      * @return whether the provided {@param className} is blacklisted
381      */
isBlackListedActivity(String className)382     public boolean isBlackListedActivity(String className) {
383         return sRecentsBlacklist.contains(className);
384     }
385 
386     /**
387      * Returns a list of the recents tasks.
388      *
389      * @param includeFrontMostExcludedTask if set, will ensure that the front most excluded task
390      *                                     will be visible, otherwise no excluded tasks will be
391      *                                     visible.
392      */
getRecentTasks(int numLatestTasks, int userId, boolean includeFrontMostExcludedTask, ArraySet<Integer> quietProfileIds)393     public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numLatestTasks, int userId,
394             boolean includeFrontMostExcludedTask, ArraySet<Integer> quietProfileIds) {
395         if (mAm == null) return null;
396 
397         // If we are mocking, then create some recent tasks
398         if (RecentsDebugFlags.Static.EnableMockTasks) {
399             ArrayList<ActivityManager.RecentTaskInfo> tasks =
400                     new ArrayList<ActivityManager.RecentTaskInfo>();
401             int count = Math.min(numLatestTasks, RecentsDebugFlags.Static.MockTaskCount);
402             for (int i = 0; i < count; i++) {
403                 // Create a dummy component name
404                 int packageIndex = i % RecentsDebugFlags.Static.MockTasksPackageCount;
405                 ComponentName cn = new ComponentName("com.android.test" + packageIndex,
406                         "com.android.test" + i + ".Activity");
407                 String description = "" + i + " - " +
408                         Long.toString(Math.abs(new Random().nextLong()), 36);
409                 // Create the recent task info
410                 ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
411                 rti.id = rti.persistentId = rti.affiliatedTaskId = i;
412                 rti.baseIntent = new Intent();
413                 rti.baseIntent.setComponent(cn);
414                 rti.description = description;
415                 rti.firstActiveTime = rti.lastActiveTime = i;
416                 if (i % 2 == 0) {
417                     rti.taskDescription = new ActivityManager.TaskDescription(description,
418                             Bitmap.createBitmap(mDummyIcon), null,
419                             0xFF000000 | (0xFFFFFF & new Random().nextInt()),
420                             0xFF000000 | (0xFFFFFF & new Random().nextInt()),
421                             0, 0);
422                 } else {
423                     rti.taskDescription = new ActivityManager.TaskDescription();
424                 }
425                 tasks.add(rti);
426             }
427             return tasks;
428         }
429 
430         // Remove home/recents/excluded tasks
431         int minNumTasksToQuery = 10;
432         int numTasksToQuery = Math.max(minNumTasksToQuery, numLatestTasks);
433         int flags = ActivityManager.RECENT_IGNORE_HOME_AND_RECENTS_STACK_TASKS |
434                 ActivityManager.RECENT_INGORE_DOCKED_STACK_TOP_TASK |
435                 ActivityManager.RECENT_INGORE_PINNED_STACK_TASKS |
436                 ActivityManager.RECENT_IGNORE_UNAVAILABLE |
437                 ActivityManager.RECENT_INCLUDE_PROFILES;
438         if (includeFrontMostExcludedTask) {
439             flags |= ActivityManager.RECENT_WITH_EXCLUDED;
440         }
441         List<ActivityManager.RecentTaskInfo> tasks = null;
442         try {
443             tasks = mAm.getRecentTasksForUser(numTasksToQuery, flags, userId);
444         } catch (Exception e) {
445             Log.e(TAG, "Failed to get recent tasks", e);
446         }
447 
448         // Break early if we can't get a valid set of tasks
449         if (tasks == null) {
450             return new ArrayList<>();
451         }
452 
453         boolean isFirstValidTask = true;
454         Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
455         while (iter.hasNext()) {
456             ActivityManager.RecentTaskInfo t = iter.next();
457 
458             // NOTE: The order of these checks happens in the expected order of the traversal of the
459             // tasks
460 
461             // Remove the task if it or it's package are blacklsited
462             if (sRecentsBlacklist.contains(t.realActivity.getClassName()) ||
463                     sRecentsBlacklist.contains(t.realActivity.getPackageName())) {
464                 iter.remove();
465                 continue;
466             }
467 
468             // Remove the task if it is marked as excluded, unless it is the first most task and we
469             // are requested to include it
470             boolean isExcluded = (t.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
471                     == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
472             isExcluded |= quietProfileIds.contains(t.userId);
473             if (isExcluded && (!isFirstValidTask || !includeFrontMostExcludedTask)) {
474                 iter.remove();
475             }
476 
477             isFirstValidTask = false;
478         }
479 
480         return tasks.subList(0, Math.min(tasks.size(), numLatestTasks));
481     }
482 
483     /**
484      * Returns the top running task.
485      */
getRunningTask()486     public ActivityManager.RunningTaskInfo getRunningTask() {
487         // Note: The set of running tasks from the system is ordered by recency
488         List<ActivityManager.RunningTaskInfo> tasks = mAm.getRunningTasks(10);
489         if (tasks != null && !tasks.isEmpty()) {
490             // Find the first task in a valid stack, we ignore everything from the Recents and PiP
491             // stacks
492             for (int i = 0; i < tasks.size(); i++) {
493                 ActivityManager.RunningTaskInfo task = tasks.get(i);
494                 int stackId = task.stackId;
495                 if (stackId != RECENTS_STACK_ID && stackId != PINNED_STACK_ID) {
496                     return task;
497                 }
498             }
499         }
500         return null;
501     }
502 
503     /**
504      * Returns whether the recents activity is currently visible.
505      */
isRecentsActivityVisible()506     public boolean isRecentsActivityVisible() {
507         return isRecentsActivityVisible(null);
508     }
509 
510     /**
511      * Returns whether the recents activity is currently visible.
512      *
513      * @param isHomeStackVisible if provided, will return whether the home stack is visible
514      *                           regardless of the recents visibility
515      */
isRecentsActivityVisible(MutableBoolean isHomeStackVisible)516     public boolean isRecentsActivityVisible(MutableBoolean isHomeStackVisible) {
517         if (mIam == null) return false;
518 
519         try {
520             List<StackInfo> stackInfos = mIam.getAllStackInfos();
521             ActivityManager.StackInfo homeStackInfo = null;
522             ActivityManager.StackInfo fullscreenStackInfo = null;
523             ActivityManager.StackInfo recentsStackInfo = null;
524             for (int i = 0; i < stackInfos.size(); i++) {
525                 StackInfo stackInfo = stackInfos.get(i);
526                 if (stackInfo.stackId == HOME_STACK_ID) {
527                     homeStackInfo = stackInfo;
528                 } else if (stackInfo.stackId == FULLSCREEN_WORKSPACE_STACK_ID) {
529                     fullscreenStackInfo = stackInfo;
530                 } else if (stackInfo.stackId == RECENTS_STACK_ID) {
531                     recentsStackInfo = stackInfo;
532                 }
533             }
534             boolean homeStackVisibleNotOccluded = isStackNotOccluded(homeStackInfo,
535                     fullscreenStackInfo);
536             boolean recentsStackVisibleNotOccluded = isStackNotOccluded(recentsStackInfo,
537                     fullscreenStackInfo);
538             if (isHomeStackVisible != null) {
539                 isHomeStackVisible.value = homeStackVisibleNotOccluded;
540             }
541             ComponentName topActivity = recentsStackInfo != null ?
542                     recentsStackInfo.topActivity : null;
543             return (recentsStackVisibleNotOccluded && topActivity != null
544                     && topActivity.getPackageName().equals(RecentsImpl.RECENTS_PACKAGE)
545                     && Recents.RECENTS_ACTIVITIES.contains(topActivity.getClassName()));
546         } catch (RemoteException e) {
547             e.printStackTrace();
548         }
549         return false;
550     }
551 
isStackNotOccluded(ActivityManager.StackInfo stackInfo, ActivityManager.StackInfo fullscreenStackInfo)552     private boolean isStackNotOccluded(ActivityManager.StackInfo stackInfo,
553             ActivityManager.StackInfo fullscreenStackInfo) {
554         boolean stackVisibleNotOccluded = stackInfo == null || stackInfo.visible;
555         if (fullscreenStackInfo != null && stackInfo != null) {
556             boolean isFullscreenStackOccludingg = fullscreenStackInfo.visible &&
557                     fullscreenStackInfo.position > stackInfo.position;
558             stackVisibleNotOccluded &= !isFullscreenStackOccludingg;
559         }
560         return stackVisibleNotOccluded;
561     }
562 
563     /**
564      * Returns whether this device has freeform workspaces.
565      */
hasFreeformWorkspaceSupport()566     public boolean hasFreeformWorkspaceSupport() {
567         return mHasFreeformWorkspaceSupport;
568     }
569 
570     /**
571      * Returns whether this device is in the safe mode.
572      */
isInSafeMode()573     public boolean isInSafeMode() {
574         return mIsSafeMode;
575     }
576 
577     /** Docks a task to the side of the screen and starts it. */
startTaskInDockedMode(int taskId, int createMode)578     public boolean startTaskInDockedMode(int taskId, int createMode) {
579         if (mIam == null) return false;
580 
581         try {
582             final ActivityOptions options = ActivityOptions.makeBasic();
583             options.setDockCreateMode(createMode);
584             options.setLaunchStackId(DOCKED_STACK_ID);
585             mIam.startActivityFromRecents(taskId, options.toBundle());
586             return true;
587         } catch (Exception e) {
588             Log.e(TAG, "Failed to dock task: " + taskId + " with createMode: " + createMode, e);
589         }
590         return false;
591     }
592 
593     /** Docks an already resumed task to the side of the screen. */
moveTaskToDockedStack(int taskId, int createMode, Rect initialBounds)594     public boolean moveTaskToDockedStack(int taskId, int createMode, Rect initialBounds) {
595         if (mIam == null) {
596             return false;
597         }
598 
599         try {
600             return mIam.moveTaskToDockedStack(taskId, createMode, true /* onTop */,
601                     false /* animate */, initialBounds);
602         } catch (RemoteException e) {
603             e.printStackTrace();
604         }
605         return false;
606     }
607 
608     /**
609      * Returns whether the given stack id is the home stack id.
610      */
isHomeStack(int stackId)611     public static boolean isHomeStack(int stackId) {
612         return stackId == HOME_STACK_ID;
613     }
614 
615     /**
616      * Returns whether the given stack id is the pinned stack id.
617      */
isPinnedStack(int stackId)618     public static boolean isPinnedStack(int stackId){
619         return stackId == PINNED_STACK_ID;
620     }
621 
622     /**
623      * Returns whether the given stack id is the docked stack id.
624      */
isDockedStack(int stackId)625     public static boolean isDockedStack(int stackId) {
626         return stackId == DOCKED_STACK_ID;
627     }
628 
629     /**
630      * Returns whether the given stack id is the freeform workspace stack id.
631      */
isFreeformStack(int stackId)632     public static boolean isFreeformStack(int stackId) {
633         return stackId == FREEFORM_WORKSPACE_STACK_ID;
634     }
635 
636     /**
637      * @return whether there are any docked tasks for the current user.
638      */
hasDockedTask()639     public boolean hasDockedTask() {
640         if (mIam == null) return false;
641 
642         ActivityManager.StackInfo stackInfo = null;
643         try {
644             stackInfo = mIam.getStackInfo(DOCKED_STACK_ID);
645         } catch (RemoteException e) {
646             e.printStackTrace();
647         }
648 
649         if (stackInfo != null) {
650             int userId = getCurrentUser();
651             boolean hasUserTask = false;
652             for (int i = stackInfo.taskUserIds.length - 1; i >= 0 && !hasUserTask; i--) {
653                 hasUserTask = (stackInfo.taskUserIds[i] == userId);
654             }
655             return hasUserTask;
656         }
657         return false;
658     }
659 
660     /**
661      * Returns whether there is a soft nav bar.
662      */
hasSoftNavigationBar()663     public boolean hasSoftNavigationBar() {
664         try {
665             return WindowManagerGlobal.getWindowManagerService().hasNavigationBar();
666         } catch (RemoteException e) {
667             e.printStackTrace();
668         }
669         return false;
670     }
671 
672     /**
673      * Returns whether the device has a transposed nav bar (on the right of the screen) in the
674      * current display orientation.
675      */
hasTransposedNavigationBar()676     public boolean hasTransposedNavigationBar() {
677         Rect insets = new Rect();
678         getStableInsets(insets);
679         return insets.right > 0;
680     }
681 
682     /**
683      * Cancels the current window transtion to/from Recents for the given task id.
684      */
cancelWindowTransition(int taskId)685     public void cancelWindowTransition(int taskId) {
686         if (mIam == null) return;
687 
688         try {
689             mIam.cancelTaskWindowTransition(taskId);
690         } catch (RemoteException e) {
691             e.printStackTrace();
692         }
693     }
694 
695     /**
696      * Cancels the current thumbnail transtion to/from Recents for the given task id.
697      */
cancelThumbnailTransition(int taskId)698     public void cancelThumbnailTransition(int taskId) {
699         if (mIam == null) return;
700 
701         try {
702             mIam.cancelTaskThumbnailTransition(taskId);
703         } catch (RemoteException e) {
704             e.printStackTrace();
705         }
706     }
707 
708     /** Returns the top task thumbnail for the given task id */
getTaskThumbnail(int taskId, boolean reduced)709     public ThumbnailData getTaskThumbnail(int taskId, boolean reduced) {
710         if (mAm == null) return null;
711 
712         // If we are mocking, then just return a dummy thumbnail
713         if (RecentsDebugFlags.Static.EnableMockTasks) {
714             ThumbnailData thumbnailData = new ThumbnailData();
715             thumbnailData.thumbnail = Bitmap.createBitmap(mDummyThumbnailWidth,
716                     mDummyThumbnailHeight, Bitmap.Config.ARGB_8888);
717             thumbnailData.thumbnail.eraseColor(0xff333333);
718             return thumbnailData;
719         }
720 
721         ThumbnailData thumbnailData = getThumbnail(taskId, reduced);
722         if (thumbnailData.thumbnail != null && !ActivityManager.ENABLE_TASK_SNAPSHOTS) {
723             thumbnailData.thumbnail.setHasAlpha(false);
724             // We use a dumb heuristic for now, if the thumbnail is purely transparent in the top
725             // left pixel, then assume the whole thumbnail is transparent. Generally, proper
726             // screenshots are always composed onto a bitmap that has no alpha.
727             if (Color.alpha(thumbnailData.thumbnail.getPixel(0, 0)) == 0) {
728                 mBgProtectionCanvas.setBitmap(thumbnailData.thumbnail);
729                 mBgProtectionCanvas.drawRect(0, 0, thumbnailData.thumbnail.getWidth(),
730                         thumbnailData.thumbnail.getHeight(), mBgProtectionPaint);
731                 mBgProtectionCanvas.setBitmap(null);
732                 Log.e(TAG, "Invalid screenshot detected from getTaskThumbnail()");
733             }
734         }
735         return thumbnailData;
736     }
737 
738     /**
739      * Returns a task thumbnail from the activity manager
740      */
getThumbnail(int taskId, boolean reducedResolution)741     public @NonNull ThumbnailData getThumbnail(int taskId, boolean reducedResolution) {
742         if (mAm == null) {
743             return new ThumbnailData();
744         }
745 
746         final ThumbnailData thumbnailData;
747         if (ActivityManager.ENABLE_TASK_SNAPSHOTS) {
748             ActivityManager.TaskSnapshot snapshot = null;
749             try {
750                 snapshot = ActivityManager.getService().getTaskSnapshot(taskId, reducedResolution);
751             } catch (RemoteException e) {
752                 Log.w(TAG, "Failed to retrieve snapshot", e);
753             }
754             if (snapshot != null) {
755                 thumbnailData = ThumbnailData.createFromTaskSnapshot(snapshot);
756             } else {
757                 return new ThumbnailData();
758             }
759         } else {
760             ActivityManager.TaskThumbnail taskThumbnail = mAm.getTaskThumbnail(taskId);
761             if (taskThumbnail == null) {
762                 return new ThumbnailData();
763             }
764 
765             Bitmap thumbnail = taskThumbnail.mainThumbnail;
766             ParcelFileDescriptor descriptor = taskThumbnail.thumbnailFileDescriptor;
767             if (thumbnail == null && descriptor != null) {
768                 thumbnail = BitmapFactory.decodeFileDescriptor(descriptor.getFileDescriptor(),
769                         null, sBitmapOptions);
770             }
771             if (descriptor != null) {
772                 try {
773                     descriptor.close();
774                 } catch (IOException e) {
775                 }
776             }
777             thumbnailData = new ThumbnailData();
778             thumbnailData.thumbnail = thumbnail;
779             thumbnailData.orientation = taskThumbnail.thumbnailInfo.screenOrientation;
780             thumbnailData.insets.setEmpty();
781         }
782         return thumbnailData;
783     }
784 
785     /**
786      * Moves a task into another stack.
787      */
moveTaskToStack(int taskId, int stackId)788     public void moveTaskToStack(int taskId, int stackId) {
789         if (mIam == null) return;
790 
791         try {
792             mIam.positionTaskInStack(taskId, stackId, 0);
793         } catch (RemoteException | IllegalArgumentException e) {
794             e.printStackTrace();
795         }
796     }
797 
798     /** Removes the task */
removeTask(final int taskId)799     public void removeTask(final int taskId) {
800         if (mAm == null) return;
801         if (RecentsDebugFlags.Static.EnableMockTasks) return;
802 
803         // Remove the task.
804         mUiOffloadThread.submit(() -> {
805             mAm.removeTask(taskId);
806         });
807     }
808 
809     /**
810      * Sends a message to close other system windows.
811      */
sendCloseSystemWindows(String reason)812     public void sendCloseSystemWindows(String reason) {
813         mUiOffloadThread.submit(() -> {
814             try {
815                 mIam.closeSystemDialogs(reason);
816             } catch (RemoteException e) {
817             }
818         });
819     }
820 
821     /**
822      * Returns the activity info for a given component name.
823      *
824      * @param cn The component name of the activity.
825      * @param userId The userId of the user that this is for.
826      */
getActivityInfo(ComponentName cn, int userId)827     public ActivityInfo getActivityInfo(ComponentName cn, int userId) {
828         if (mIpm == null) return null;
829         if (RecentsDebugFlags.Static.EnableMockTasks) return new ActivityInfo();
830 
831         try {
832             return mIpm.getActivityInfo(cn, PackageManager.GET_META_DATA, userId);
833         } catch (RemoteException e) {
834             e.printStackTrace();
835             return null;
836         }
837     }
838 
839     /**
840      * Returns the activity info for a given component name.
841      *
842      * @param cn The component name of the activity.
843      */
getActivityInfo(ComponentName cn)844     public ActivityInfo getActivityInfo(ComponentName cn) {
845         if (mPm == null) return null;
846         if (RecentsDebugFlags.Static.EnableMockTasks) return new ActivityInfo();
847 
848         try {
849             return mPm.getActivityInfo(cn, PackageManager.GET_META_DATA);
850         } catch (PackageManager.NameNotFoundException e) {
851             e.printStackTrace();
852             return null;
853         }
854     }
855 
856     /**
857      * Returns the activity label, badging if necessary.
858      */
getBadgedActivityLabel(ActivityInfo info, int userId)859     public String getBadgedActivityLabel(ActivityInfo info, int userId) {
860         if (mPm == null) return null;
861 
862         // If we are mocking, then return a mock label
863         if (RecentsDebugFlags.Static.EnableMockTasks) {
864             return "Recent Task: " + userId;
865         }
866 
867         return getBadgedLabel(info.loadLabel(mPm).toString(), userId);
868     }
869 
870     /**
871      * Returns the application label, badging if necessary.
872      */
getBadgedApplicationLabel(ApplicationInfo appInfo, int userId)873     public String getBadgedApplicationLabel(ApplicationInfo appInfo, int userId) {
874         if (mPm == null) return null;
875 
876         // If we are mocking, then return a mock label
877         if (RecentsDebugFlags.Static.EnableMockTasks) {
878             return "Recent Task App: " + userId;
879         }
880 
881         return getBadgedLabel(appInfo.loadLabel(mPm).toString(), userId);
882     }
883 
884     /**
885      * Returns the content description for a given task, badging it if necessary.  The content
886      * description joins the app and activity labels.
887      */
getBadgedContentDescription(ActivityInfo info, int userId, ActivityManager.TaskDescription td, Resources res)888     public String getBadgedContentDescription(ActivityInfo info, int userId,
889             ActivityManager.TaskDescription td, Resources res) {
890         // If we are mocking, then return a mock label
891         if (RecentsDebugFlags.Static.EnableMockTasks) {
892             return "Recent Task Content Description: " + userId;
893         }
894 
895         String activityLabel;
896         if (td != null && td.getLabel() != null) {
897             activityLabel = td.getLabel();
898         } else {
899             activityLabel = info.loadLabel(mPm).toString();
900         }
901         String applicationLabel = info.applicationInfo.loadLabel(mPm).toString();
902         String badgedApplicationLabel = getBadgedLabel(applicationLabel, userId);
903         return applicationLabel.equals(activityLabel) ? badgedApplicationLabel
904                 : res.getString(R.string.accessibility_recents_task_header,
905                         badgedApplicationLabel, activityLabel);
906     }
907 
908     /**
909      * Returns the activity icon for the ActivityInfo for a user, badging if
910      * necessary.
911      */
getBadgedActivityIcon(ActivityInfo info, int userId)912     public Drawable getBadgedActivityIcon(ActivityInfo info, int userId) {
913         if (mPm == null) return null;
914 
915         // If we are mocking, then return a mock label
916         if (RecentsDebugFlags.Static.EnableMockTasks) {
917             return new ColorDrawable(0xFF666666);
918         }
919 
920         return mDrawableFactory.getBadgedIcon(info, info.applicationInfo, userId);
921     }
922 
923     /**
924      * Returns the application icon for the ApplicationInfo for a user, badging if
925      * necessary.
926      */
getBadgedApplicationIcon(ApplicationInfo appInfo, int userId)927     public Drawable getBadgedApplicationIcon(ApplicationInfo appInfo, int userId) {
928         if (mPm == null) return null;
929 
930         // If we are mocking, then return a mock label
931         if (RecentsDebugFlags.Static.EnableMockTasks) {
932             return new ColorDrawable(0xFF666666);
933         }
934 
935         return mDrawableFactory.getBadgedIcon(appInfo, userId);
936     }
937 
938     /**
939      * Returns the task description icon, loading and badging it if it necessary.
940      */
getBadgedTaskDescriptionIcon(ActivityManager.TaskDescription taskDescription, int userId, Resources res)941     public Drawable getBadgedTaskDescriptionIcon(ActivityManager.TaskDescription taskDescription,
942             int userId, Resources res) {
943 
944         // If we are mocking, then return a mock label
945         if (RecentsDebugFlags.Static.EnableMockTasks) {
946             return new ColorDrawable(0xFF666666);
947         }
948 
949         Bitmap tdIcon = taskDescription.getInMemoryIcon();
950         if (tdIcon == null) {
951             tdIcon = ActivityManager.TaskDescription.loadTaskDescriptionIcon(
952                     taskDescription.getIconFilename(), userId);
953         }
954         if (tdIcon != null) {
955             return getBadgedIcon(new BitmapDrawable(res, tdIcon), userId);
956         }
957         return null;
958     }
959 
getTaskDescription(int taskId)960     public ActivityManager.TaskDescription getTaskDescription(int taskId) {
961         try {
962             return mIam.getTaskDescription(taskId);
963         } catch (RemoteException e) {
964             return null;
965         }
966     }
967 
968     /**
969      * Returns the given icon for a user, badging if necessary.
970      */
getBadgedIcon(Drawable icon, int userId)971     private Drawable getBadgedIcon(Drawable icon, int userId) {
972         if (userId != UserHandle.myUserId()) {
973             icon = mPm.getUserBadgedIcon(icon, new UserHandle(userId));
974         }
975         return icon;
976     }
977 
978     /**
979      * Returns a banner used on TV for the specified Activity.
980      */
getActivityBanner(ActivityInfo info)981     public Drawable getActivityBanner(ActivityInfo info) {
982         if (mPm == null) return null;
983 
984         // If we are mocking, then return a mock banner
985         if (RecentsDebugFlags.Static.EnableMockTasks) {
986             return new ColorDrawable(0xFF666666);
987         }
988 
989         Drawable banner = info.loadBanner(mPm);
990         return banner;
991     }
992 
993     /**
994      * Returns a logo used on TV for the specified Activity.
995      */
getActivityLogo(ActivityInfo info)996     public Drawable getActivityLogo(ActivityInfo info) {
997         if (mPm == null) return null;
998 
999         // If we are mocking, then return a mock logo
1000         if (RecentsDebugFlags.Static.EnableMockTasks) {
1001             return new ColorDrawable(0xFF666666);
1002         }
1003 
1004         Drawable logo = info.loadLogo(mPm);
1005         return logo;
1006     }
1007 
1008 
1009     /**
1010      * Returns the given label for a user, badging if necessary.
1011      */
getBadgedLabel(String label, int userId)1012     private String getBadgedLabel(String label, int userId) {
1013         if (userId != UserHandle.myUserId()) {
1014             label = mPm.getUserBadgedLabel(label, new UserHandle(userId)).toString();
1015         }
1016         return label;
1017     }
1018 
1019     /**
1020      * Returns whether the provided {@param userId} is currently locked (and showing Keyguard).
1021      */
isDeviceLocked(int userId)1022     public boolean isDeviceLocked(int userId) {
1023         if (mKgm == null) {
1024             return false;
1025         }
1026         return mKgm.isDeviceLocked(userId);
1027     }
1028 
1029     /** Returns the package name of the home activity. */
getHomeActivityPackageName()1030     public String getHomeActivityPackageName() {
1031         if (mPm == null) return null;
1032         if (RecentsDebugFlags.Static.EnableMockTasks) return null;
1033 
1034         ArrayList<ResolveInfo> homeActivities = new ArrayList<>();
1035         ComponentName defaultHomeActivity = mPm.getHomeActivities(homeActivities);
1036         if (defaultHomeActivity != null) {
1037             return defaultHomeActivity.getPackageName();
1038         } else if (homeActivities.size() == 1) {
1039             ResolveInfo info = homeActivities.get(0);
1040             if (info.activityInfo != null) {
1041                 return info.activityInfo.packageName;
1042             }
1043         }
1044         return null;
1045     }
1046 
1047     /**
1048      * Returns whether the provided {@param userId} represents the system user.
1049      */
isSystemUser(int userId)1050     public boolean isSystemUser(int userId) {
1051         return userId == UserHandle.USER_SYSTEM;
1052     }
1053 
1054     /**
1055      * Returns the current user id.  Used instead of KeyguardUpdateMonitor in SystemUI components
1056      * that run in the non-primary SystemUI process.
1057      */
getCurrentUser()1058     public int getCurrentUser() {
1059         return mCurrentUserId;
1060     }
1061 
1062     /**
1063      * Returns the processes user id.
1064      */
getProcessUser()1065     public int getProcessUser() {
1066         if (mUm == null) return 0;
1067         return mUm.getUserHandle();
1068     }
1069 
1070     /**
1071      * Returns whether touch exploration is currently enabled.
1072      */
isTouchExplorationEnabled()1073     public boolean isTouchExplorationEnabled() {
1074         if (mAccm == null) return false;
1075 
1076         return mAccm.isEnabled() && mAccm.isTouchExplorationEnabled();
1077     }
1078 
1079     /**
1080      * Returns whether the current task is in screen-pinning mode.
1081      */
isScreenPinningActive()1082     public boolean isScreenPinningActive() {
1083         if (mIam == null) return false;
1084 
1085         try {
1086             return mIam.isInLockTaskMode();
1087         } catch (RemoteException e) {
1088             return false;
1089         }
1090     }
1091 
1092     /**
1093      * Returns a global setting.
1094      */
getGlobalSetting(Context context, String setting)1095     public int getGlobalSetting(Context context, String setting) {
1096         ContentResolver cr = context.getContentResolver();
1097         return Settings.Global.getInt(cr, setting, 0);
1098     }
1099 
1100     /**
1101      * Returns a system setting.
1102      */
getSystemSetting(Context context, String setting)1103     public int getSystemSetting(Context context, String setting) {
1104         ContentResolver cr = context.getContentResolver();
1105         return Settings.System.getInt(cr, setting, 0);
1106     }
1107 
1108     /**
1109      * Returns a system property.
1110      */
getSystemProperty(String key)1111     public String getSystemProperty(String key) {
1112         return SystemProperties.get(key);
1113     }
1114 
1115     /**
1116      * Returns the smallest width/height.
1117      */
getDeviceSmallestWidth()1118     public int getDeviceSmallestWidth() {
1119         if (mDisplay == null) return 0;
1120 
1121         Point smallestSizeRange = new Point();
1122         Point largestSizeRange = new Point();
1123         mDisplay.getCurrentSizeRange(smallestSizeRange, largestSizeRange);
1124         return smallestSizeRange.x;
1125     }
1126 
1127     /**
1128      * Returns the current display rect in the current display orientation.
1129      */
getDisplayRect()1130     public Rect getDisplayRect() {
1131         Rect displayRect = new Rect();
1132         if (mDisplay == null) return displayRect;
1133 
1134         Point p = new Point();
1135         mDisplay.getRealSize(p);
1136         displayRect.set(0, 0, p.x, p.y);
1137         return displayRect;
1138     }
1139 
1140     /**
1141      * Returns the window rect for the RecentsActivity, based on the dimensions of the recents stack
1142      */
getWindowRect()1143     public Rect getWindowRect() {
1144         Rect windowRect = new Rect();
1145         if (mIam == null) return windowRect;
1146 
1147         try {
1148             // Use the recents stack bounds, fallback to fullscreen stack if it is null
1149             ActivityManager.StackInfo stackInfo = mIam.getStackInfo(RECENTS_STACK_ID);
1150             if (stackInfo == null) {
1151                 stackInfo = mIam.getStackInfo(FULLSCREEN_WORKSPACE_STACK_ID);
1152             }
1153             if (stackInfo != null) {
1154                 windowRect.set(stackInfo.bounds);
1155             }
1156         } catch (RemoteException e) {
1157             e.printStackTrace();
1158         } finally {
1159             return windowRect;
1160         }
1161     }
1162 
startActivityAsUserAsync(Intent intent, ActivityOptions opts)1163     public void startActivityAsUserAsync(Intent intent, ActivityOptions opts) {
1164         mUiOffloadThread.submit(() -> mContext.startActivityAsUser(intent,
1165                 opts != null ? opts.toBundle() : null, UserHandle.CURRENT));
1166     }
1167 
1168     /** Starts an activity from recents. */
startActivityFromRecents(Context context, Task.TaskKey taskKey, String taskName, ActivityOptions options, int stackId, @Nullable final StartActivityFromRecentsResultListener resultListener)1169     public void startActivityFromRecents(Context context, Task.TaskKey taskKey, String taskName,
1170             ActivityOptions options, int stackId,
1171             @Nullable final StartActivityFromRecentsResultListener resultListener) {
1172         if (mIam == null) {
1173             return;
1174         }
1175         if (taskKey.stackId == DOCKED_STACK_ID) {
1176             // We show non-visible docked tasks in Recents, but we always want to launch
1177             // them in the fullscreen stack.
1178             if (options == null) {
1179                 options = ActivityOptions.makeBasic();
1180             }
1181             options.setLaunchStackId(FULLSCREEN_WORKSPACE_STACK_ID);
1182         } else if (stackId != INVALID_STACK_ID) {
1183             if (options == null) {
1184                 options = ActivityOptions.makeBasic();
1185             }
1186             options.setLaunchStackId(stackId);
1187         }
1188         final ActivityOptions finalOptions = options;
1189 
1190         // Execute this from another thread such that we can do other things (like caching the
1191         // bitmap for the thumbnail) while AM is busy starting our activity.
1192         mUiOffloadThread.submit(() -> {
1193             try {
1194                 mIam.startActivityFromRecents(
1195                         taskKey.id, finalOptions == null ? null : finalOptions.toBundle());
1196                 if (resultListener != null) {
1197                     mHandler.post(() -> resultListener.onStartActivityResult(true));
1198                 }
1199             } catch (Exception e) {
1200                 Log.e(TAG, context.getString(
1201                         R.string.recents_launch_error_message, taskName), e);
1202                 if (resultListener != null) {
1203                     mHandler.post(() -> resultListener.onStartActivityResult(false));
1204                 }
1205             }
1206         });
1207     }
1208 
1209     /** Starts an in-place animation on the front most application windows. */
startInPlaceAnimationOnFrontMostApplication(ActivityOptions opts)1210     public void startInPlaceAnimationOnFrontMostApplication(ActivityOptions opts) {
1211         if (mIam == null) return;
1212 
1213         try {
1214             mIam.startInPlaceAnimationOnFrontMostApplication(
1215                     opts == null ? null : opts.toBundle());
1216         } catch (Exception e) {
1217             e.printStackTrace();
1218         }
1219     }
1220 
1221     /**
1222      * Registers a task stack listener with the system.
1223      * This should be called on the main thread.
1224      */
registerTaskStackListener(TaskStackListener listener)1225     public void registerTaskStackListener(TaskStackListener listener) {
1226         if (mIam == null) return;
1227 
1228         synchronized (mTaskStackListeners) {
1229             mTaskStackListeners.add(listener);
1230             if (mTaskStackListeners.size() == 1) {
1231                 // Register mTaskStackListener to IActivityManager only once if needed.
1232                 try {
1233                     mIam.registerTaskStackListener(mTaskStackListener);
1234                 } catch (Exception e) {
1235                     Log.w(TAG, "Failed to call registerTaskStackListener", e);
1236                 }
1237             }
1238         }
1239     }
1240 
endProlongedAnimations()1241     public void endProlongedAnimations() {
1242         if (mWm == null) {
1243             return;
1244         }
1245         try {
1246             WindowManagerGlobal.getWindowManagerService().endProlongedAnimations();
1247         } catch (Exception e) {
1248             e.printStackTrace();
1249         }
1250     }
1251 
registerDockedStackListener(IDockedStackListener listener)1252     public void registerDockedStackListener(IDockedStackListener listener) {
1253         if (mWm == null) return;
1254 
1255         try {
1256             WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(listener);
1257         } catch (Exception e) {
1258             e.printStackTrace();
1259         }
1260     }
1261 
1262     /**
1263      * Calculates the size of the dock divider in the current orientation.
1264      */
getDockedDividerSize(Context context)1265     public int getDockedDividerSize(Context context) {
1266         Resources res = context.getResources();
1267         int dividerWindowWidth = res.getDimensionPixelSize(
1268                 com.android.internal.R.dimen.docked_stack_divider_thickness);
1269         int dividerInsets = res.getDimensionPixelSize(
1270                 com.android.internal.R.dimen.docked_stack_divider_insets);
1271         return dividerWindowWidth - 2 * dividerInsets;
1272     }
1273 
requestKeyboardShortcuts( Context context, KeyboardShortcutsReceiver receiver, int deviceId)1274     public void requestKeyboardShortcuts(
1275             Context context, KeyboardShortcutsReceiver receiver, int deviceId) {
1276         mWm.requestAppKeyboardShortcuts(receiver, deviceId);
1277     }
1278 
getStableInsets(Rect outStableInsets)1279     public void getStableInsets(Rect outStableInsets) {
1280         if (mWm == null) return;
1281 
1282         try {
1283             WindowManagerGlobal.getWindowManagerService().getStableInsets(Display.DEFAULT_DISPLAY,
1284                     outStableInsets);
1285         } catch (Exception e) {
1286             e.printStackTrace();
1287         }
1288     }
1289 
overridePendingAppTransitionMultiThumbFuture( IAppTransitionAnimationSpecsFuture future, IRemoteCallback animStartedListener, boolean scaleUp)1290     public void overridePendingAppTransitionMultiThumbFuture(
1291             IAppTransitionAnimationSpecsFuture future, IRemoteCallback animStartedListener,
1292             boolean scaleUp) {
1293         try {
1294             WindowManagerGlobal.getWindowManagerService()
1295                     .overridePendingAppTransitionMultiThumbFuture(future, animStartedListener,
1296                             scaleUp);
1297         } catch (RemoteException e) {
1298             Log.w(TAG, "Failed to override transition: " + e);
1299         }
1300     }
1301 
1302     /**
1303      * Updates the visibility of recents.
1304      */
setRecentsVisibility(boolean visible)1305     public void setRecentsVisibility(boolean visible) {
1306         try {
1307             mIwm.setRecentsVisibility(visible);
1308         } catch (RemoteException e) {
1309             Log.e(TAG, "Unable to reach window manager", e);
1310         }
1311     }
1312 
1313     /**
1314      * Updates the visibility of the picture-in-picture.
1315      */
setPipVisibility(boolean visible)1316     public void setPipVisibility(boolean visible) {
1317         try {
1318             mIwm.setPipVisibility(visible);
1319         } catch (RemoteException e) {
1320             Log.e(TAG, "Unable to reach window manager", e);
1321         }
1322     }
1323 
isDreaming()1324     public boolean isDreaming() {
1325         try {
1326             return mDreamManager.isDreaming();
1327         } catch (RemoteException e) {
1328             Log.e(TAG, "Failed to query dream manager.", e);
1329         }
1330         return false;
1331     }
1332 
awakenDreamsAsync()1333     public void awakenDreamsAsync() {
1334         mUiOffloadThread.submit(() -> {
1335             try {
1336                 mDreamManager.awaken();
1337             } catch (RemoteException e) {
1338                 e.printStackTrace();
1339             }
1340         });
1341     }
1342 
updateOverviewLastStackActiveTimeAsync(long newLastStackActiveTime, int currentUserId)1343     public void updateOverviewLastStackActiveTimeAsync(long newLastStackActiveTime,
1344             int currentUserId) {
1345         mUiOffloadThread.submit(() -> {
1346             Settings.Secure.putLongForUser(mContext.getContentResolver(),
1347                     Secure.OVERVIEW_LAST_STACK_ACTIVE_TIME, newLastStackActiveTime, currentUserId);
1348         });
1349     }
1350 
1351     public interface StartActivityFromRecentsResultListener {
onStartActivityResult(boolean succeeded)1352         void onStartActivityResult(boolean succeeded);
1353     }
1354 
1355     private final class H extends Handler {
1356         private static final int ON_TASK_STACK_CHANGED = 1;
1357         private static final int ON_TASK_SNAPSHOT_CHANGED = 2;
1358         private static final int ON_ACTIVITY_PINNED = 3;
1359         private static final int ON_PINNED_ACTIVITY_RESTART_ATTEMPT = 4;
1360         private static final int ON_PINNED_STACK_ANIMATION_ENDED = 5;
1361         private static final int ON_ACTIVITY_FORCED_RESIZABLE = 6;
1362         private static final int ON_ACTIVITY_DISMISSING_DOCKED_STACK = 7;
1363         private static final int ON_TASK_PROFILE_LOCKED = 8;
1364         private static final int ON_PINNED_STACK_ANIMATION_STARTED = 9;
1365         private static final int ON_ACTIVITY_UNPINNED = 10;
1366         private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED = 11;
1367 
1368         @Override
handleMessage(Message msg)1369         public void handleMessage(Message msg) {
1370             synchronized (mTaskStackListeners) {
1371                 switch (msg.what) {
1372                     case ON_TASK_STACK_CHANGED: {
1373                     Trace.beginSection("onTaskStackChanged");
1374                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
1375                             mTaskStackListeners.get(i).onTaskStackChanged();
1376                         }
1377                     Trace.endSection();
1378                         break;
1379                     }
1380                     case ON_TASK_SNAPSHOT_CHANGED: {
1381                     Trace.beginSection("onTaskSnapshotChanged");
1382                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
1383                             mTaskStackListeners.get(i).onTaskSnapshotChanged(msg.arg1,
1384                                     (TaskSnapshot) msg.obj);
1385                         }
1386                     Trace.endSection();
1387                         break;
1388                     }
1389                     case ON_ACTIVITY_PINNED: {
1390                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
1391                             mTaskStackListeners.get(i).onActivityPinned((String) msg.obj, msg.arg1,
1392                                     msg.arg2);
1393                         }
1394                         break;
1395                     }
1396                     case ON_ACTIVITY_UNPINNED: {
1397                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
1398                             mTaskStackListeners.get(i).onActivityUnpinned();
1399                         }
1400                         break;
1401                     }
1402                     case ON_PINNED_ACTIVITY_RESTART_ATTEMPT: {
1403                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
1404                             mTaskStackListeners.get(i).onPinnedActivityRestartAttempt(
1405                                     msg.arg1 != 0);
1406                         }
1407                         break;
1408                     }
1409                     case ON_PINNED_STACK_ANIMATION_STARTED: {
1410                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
1411                             mTaskStackListeners.get(i).onPinnedStackAnimationStarted();
1412                         }
1413                         break;
1414                     }
1415                     case ON_PINNED_STACK_ANIMATION_ENDED: {
1416                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
1417                             mTaskStackListeners.get(i).onPinnedStackAnimationEnded();
1418                         }
1419                         break;
1420                     }
1421                     case ON_ACTIVITY_FORCED_RESIZABLE: {
1422                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
1423                             mTaskStackListeners.get(i).onActivityForcedResizable(
1424                                     (String) msg.obj, msg.arg1, msg.arg2);
1425                         }
1426                         break;
1427                     }
1428                     case ON_ACTIVITY_DISMISSING_DOCKED_STACK: {
1429                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
1430                             mTaskStackListeners.get(i).onActivityDismissingDockedStack();
1431                         }
1432                         break;
1433                     }
1434                     case ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED: {
1435                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
1436                             mTaskStackListeners.get(i).onActivityLaunchOnSecondaryDisplayFailed();
1437                         }
1438                         break;
1439                     }
1440                     case ON_TASK_PROFILE_LOCKED: {
1441                         for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
1442                             mTaskStackListeners.get(i).onTaskProfileLocked(msg.arg1, msg.arg2);
1443                         }
1444                         break;
1445                     }
1446                 }
1447             }
1448         }
1449     }
1450 }
1451