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