Home | History | Annotate | Download | only in misc
      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<>();
    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          */
    176         public void onTaskStackChangedBackground() { }
    177         public void onTaskStackChanged() { }
    178         public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) { }
    179         public void onActivityPinned(String packageName, int taskId) { }
    180         public void onActivityUnpinned() { }
    181         public void onPinnedActivityRestartAttempt(boolean clearedTask) { }
    182         public void onPinnedStackAnimationStarted() { }
    183         public void onPinnedStackAnimationEnded() { }
    184         public void onActivityForcedResizable(String packageName, int taskId, int reason) { }
    185         public void onActivityDismissingDockedStack() { }
    186         public void onActivityLaunchOnSecondaryDisplayFailed() { }
    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          */
    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 */
    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      */
    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      */
    374     public void gc() {
    375         BackgroundThread.getHandler().post(mGcRunnable);
    376     }
    377 
    378     /**
    379      * @return whether the provided {@param className} is blacklisted
    380      */
    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      */
    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      */
    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      */
    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      */
    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 
    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      */
    565     public boolean hasFreeformWorkspaceSupport() {
    566         return mHasFreeformWorkspaceSupport;
    567     }
    568 
    569     /**
    570      * Returns whether this device is in the safe mode.
    571      */
    572     public boolean isInSafeMode() {
    573         return mIsSafeMode;
    574     }
    575 
    576     /** Docks a task to the side of the screen and starts it. */
    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. */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 */
    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      */
    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      */
    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 */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 
    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      */
    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      */
    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      */
    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      */
   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      */
   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. */
   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      */
   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      */
   1057     public int getCurrentUser() {
   1058         return mCurrentUserId;
   1059     }
   1060 
   1061     /**
   1062      * Returns the processes user id.
   1063      */
   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      */
   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      */
   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      */
   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      */
   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      */
   1110     public String getSystemProperty(String key) {
   1111         return SystemProperties.get(key);
   1112     }
   1113 
   1114     /**
   1115      * Returns the smallest width/height.
   1116      */
   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      */
   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      */
   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 
   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. */
   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. */
   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      */
   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 
   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 
   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      */
   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 
   1273     public void requestKeyboardShortcuts(
   1274             Context context, KeyboardShortcutsReceiver receiver, int deviceId) {
   1275         mWm.requestAppKeyboardShortcuts(receiver, deviceId);
   1276     }
   1277 
   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 
   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      */
   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      */
   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 
   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 
   1332     public void awakenDreamsAsync() {
   1333         mUiOffloadThread.submit(() -> {
   1334             try {
   1335                 mDreamManager.awaken();
   1336             } catch (RemoteException e) {
   1337                 e.printStackTrace();
   1338             }
   1339         });
   1340     }
   1341 
   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 {
   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
   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