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