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.PINNED_STACK_ID;
     24 import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
     25 
     26 import android.app.ActivityManager;
     27 import android.app.ActivityManagerNative;
     28 import android.app.ActivityOptions;
     29 import android.app.AppGlobals;
     30 import android.app.IActivityManager;
     31 import android.app.ITaskStackListener;
     32 import android.app.UiModeManager;
     33 import android.content.ComponentName;
     34 import android.content.ContentResolver;
     35 import android.content.Context;
     36 import android.content.Intent;
     37 import android.content.pm.ActivityInfo;
     38 import android.content.pm.ApplicationInfo;
     39 import android.content.pm.IPackageManager;
     40 import android.content.pm.PackageManager;
     41 import android.content.pm.ResolveInfo;
     42 import android.content.res.Configuration;
     43 import android.content.res.Resources;
     44 import android.graphics.Bitmap;
     45 import android.graphics.BitmapFactory;
     46 import android.graphics.Canvas;
     47 import android.graphics.Color;
     48 import android.graphics.Paint;
     49 import android.graphics.Point;
     50 import android.graphics.PorterDuff;
     51 import android.graphics.PorterDuffXfermode;
     52 import android.graphics.Rect;
     53 import android.graphics.drawable.BitmapDrawable;
     54 import android.graphics.drawable.ColorDrawable;
     55 import android.graphics.drawable.Drawable;
     56 import android.os.Handler;
     57 import android.os.IRemoteCallback;
     58 import android.os.Looper;
     59 import android.os.Message;
     60 import android.os.ParcelFileDescriptor;
     61 import android.os.RemoteException;
     62 import android.os.SystemProperties;
     63 import android.os.UserHandle;
     64 import android.os.UserManager;
     65 import android.provider.Settings;
     66 import android.util.ArraySet;
     67 import android.util.Log;
     68 import android.util.MutableBoolean;
     69 import android.view.Display;
     70 import android.view.IAppTransitionAnimationSpecsFuture;
     71 import android.view.IDockedStackListener;
     72 import android.view.WindowManager;
     73 import android.view.WindowManager.KeyboardShortcutsReceiver;
     74 import android.view.WindowManagerGlobal;
     75 import android.view.accessibility.AccessibilityManager;
     76 
     77 import com.android.internal.app.AssistUtils;
     78 import com.android.internal.os.BackgroundThread;
     79 import com.android.systemui.R;
     80 import com.android.systemui.recents.RecentsDebugFlags;
     81 import com.android.systemui.recents.RecentsImpl;
     82 import com.android.systemui.recents.model.Task;
     83 import com.android.systemui.recents.tv.RecentsTvImpl;
     84 import com.android.systemui.recents.model.ThumbnailData;
     85 
     86 import java.io.IOException;
     87 import java.util.ArrayList;
     88 import java.util.Collections;
     89 import java.util.Iterator;
     90 import java.util.List;
     91 import java.util.Random;
     92 
     93 /**
     94  * Acts as a shim around the real system services that we need to access data from, and provides
     95  * a point of injection when testing UI.
     96  */
     97 public class SystemServicesProxy {
     98     final static String TAG = "SystemServicesProxy";
     99 
    100     final static BitmapFactory.Options sBitmapOptions;
    101     static {
    102         sBitmapOptions = new BitmapFactory.Options();
    103         sBitmapOptions.inMutable = true;
    104         sBitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565;
    105     }
    106 
    107     final static List<String> sRecentsBlacklist;
    108     static {
    109         sRecentsBlacklist = new ArrayList<>();
    110         sRecentsBlacklist.add("com.android.systemui.tv.pip.PipOnboardingActivity");
    111         sRecentsBlacklist.add("com.android.systemui.tv.pip.PipMenuActivity");
    112     }
    113 
    114     private static SystemServicesProxy sSystemServicesProxy;
    115 
    116     AccessibilityManager mAccm;
    117     ActivityManager mAm;
    118     IActivityManager mIam;
    119     PackageManager mPm;
    120     IPackageManager mIpm;
    121     AssistUtils mAssistUtils;
    122     WindowManager mWm;
    123     UserManager mUm;
    124     Display mDisplay;
    125     String mRecentsPackage;
    126     ComponentName mAssistComponent;
    127 
    128     boolean mIsSafeMode;
    129     boolean mHasFreeformWorkspaceSupport;
    130 
    131     Bitmap mDummyIcon;
    132     int mDummyThumbnailWidth;
    133     int mDummyThumbnailHeight;
    134     Paint mBgProtectionPaint;
    135     Canvas mBgProtectionCanvas;
    136 
    137     private final Handler mHandler = new H();
    138 
    139     /**
    140      * An abstract class to track task stack changes.
    141      * Classes should implement this instead of {@link android.app.ITaskStackListener}
    142      * to reduce IPC calls from system services. These callbacks will be called on the main thread.
    143      */
    144     public abstract static class TaskStackListener {
    145         public void onTaskStackChanged() { }
    146         public void onActivityPinned() { }
    147         public void onPinnedActivityRestartAttempt() { }
    148         public void onPinnedStackAnimationEnded() { }
    149         public void onActivityForcedResizable(String packageName, int taskId) { }
    150         public void onActivityDismissingDockedStack() { }
    151     }
    152 
    153     /**
    154      * Implementation of {@link android.app.ITaskStackListener} to listen task stack changes from
    155      * ActivityManagerNative.
    156      * This simply passes callbacks to listeners through {@link H}.
    157      * */
    158     private ITaskStackListener.Stub mTaskStackListener = new ITaskStackListener.Stub() {
    159         @Override
    160         public void onTaskStackChanged() throws RemoteException {
    161             mHandler.removeMessages(H.ON_TASK_STACK_CHANGED);
    162             mHandler.sendEmptyMessage(H.ON_TASK_STACK_CHANGED);
    163         }
    164 
    165         @Override
    166         public void onActivityPinned() throws RemoteException {
    167             mHandler.removeMessages(H.ON_ACTIVITY_PINNED);
    168             mHandler.sendEmptyMessage(H.ON_ACTIVITY_PINNED);
    169         }
    170 
    171         @Override
    172         public void onPinnedActivityRestartAttempt() throws RemoteException{
    173             mHandler.removeMessages(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT);
    174             mHandler.sendEmptyMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT);
    175         }
    176 
    177         @Override
    178         public void onPinnedStackAnimationEnded() throws RemoteException {
    179             mHandler.removeMessages(H.ON_PINNED_STACK_ANIMATION_ENDED);
    180             mHandler.sendEmptyMessage(H.ON_PINNED_STACK_ANIMATION_ENDED);
    181         }
    182 
    183         @Override
    184         public void onActivityForcedResizable(String packageName, int taskId)
    185                 throws RemoteException {
    186             mHandler.obtainMessage(H.ON_ACTIVITY_FORCED_RESIZABLE, taskId, 0, packageName)
    187                     .sendToTarget();
    188         }
    189 
    190         @Override
    191         public void onActivityDismissingDockedStack() throws RemoteException {
    192             mHandler.sendEmptyMessage(H.ON_ACTIVITY_DISMISSING_DOCKED_STACK);
    193         }
    194     };
    195 
    196     /**
    197      * List of {@link TaskStackListener} registered from {@link #registerTaskStackListener}.
    198      */
    199     private List<TaskStackListener> mTaskStackListeners = new ArrayList<>();
    200 
    201     /** Private constructor */
    202     private SystemServicesProxy(Context context) {
    203         mAccm = AccessibilityManager.getInstance(context);
    204         mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    205         mIam = ActivityManagerNative.getDefault();
    206         mPm = context.getPackageManager();
    207         mIpm = AppGlobals.getPackageManager();
    208         mAssistUtils = new AssistUtils(context);
    209         mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    210         mUm = UserManager.get(context);
    211         mDisplay = mWm.getDefaultDisplay();
    212         mRecentsPackage = context.getPackageName();
    213         mHasFreeformWorkspaceSupport =
    214                 mPm.hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT) ||
    215                         Settings.Global.getInt(context.getContentResolver(),
    216                                 DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
    217         mIsSafeMode = mPm.isSafeMode();
    218 
    219         // Get the dummy thumbnail width/heights
    220         Resources res = context.getResources();
    221         int wId = com.android.internal.R.dimen.thumbnail_width;
    222         int hId = com.android.internal.R.dimen.thumbnail_height;
    223         mDummyThumbnailWidth = res.getDimensionPixelSize(wId);
    224         mDummyThumbnailHeight = res.getDimensionPixelSize(hId);
    225 
    226         // Create the protection paints
    227         mBgProtectionPaint = new Paint();
    228         mBgProtectionPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
    229         mBgProtectionPaint.setColor(0xFFffffff);
    230         mBgProtectionCanvas = new Canvas();
    231 
    232         // Resolve the assist intent
    233         mAssistComponent = mAssistUtils.getAssistComponentForUser(UserHandle.myUserId());
    234 
    235         if (RecentsDebugFlags.Static.EnableMockTasks) {
    236             // Create a dummy icon
    237             mDummyIcon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
    238             mDummyIcon.eraseColor(0xFF999999);
    239         }
    240 
    241         UiModeManager uiModeManager = (UiModeManager) context.
    242                 getSystemService(Context.UI_MODE_SERVICE);
    243         if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
    244             Collections.addAll(sRecentsBlacklist,
    245                     res.getStringArray(R.array.recents_tv_blacklist_array));
    246         } else {
    247             Collections.addAll(sRecentsBlacklist,
    248                     res.getStringArray(R.array.recents_blacklist_array));
    249         }
    250     }
    251 
    252     /**
    253      * Returns the single instance of the {@link SystemServicesProxy}.
    254      * This should only be called on the main thread.
    255      */
    256     public static SystemServicesProxy getInstance(Context context) {
    257         if (!Looper.getMainLooper().isCurrentThread()) {
    258             throw new RuntimeException("Must be called on the UI thread");
    259         }
    260         if (sSystemServicesProxy == null) {
    261             sSystemServicesProxy = new SystemServicesProxy(context);
    262         }
    263         return sSystemServicesProxy;
    264     }
    265 
    266     /**
    267      * @return whether the provided {@param className} is blacklisted
    268      */
    269     public boolean isBlackListedActivity(String className) {
    270         return sRecentsBlacklist.contains(className);
    271     }
    272 
    273     /**
    274      * Returns a list of the recents tasks.
    275      *
    276      * @param includeFrontMostExcludedTask if set, will ensure that the front most excluded task
    277      *                                     will be visible, otherwise no excluded tasks will be
    278      *                                     visible.
    279      */
    280     public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numLatestTasks, int userId,
    281             boolean includeFrontMostExcludedTask, ArraySet<Integer> quietProfileIds) {
    282         if (mAm == null) return null;
    283 
    284         // If we are mocking, then create some recent tasks
    285         if (RecentsDebugFlags.Static.EnableMockTasks) {
    286             ArrayList<ActivityManager.RecentTaskInfo> tasks =
    287                     new ArrayList<ActivityManager.RecentTaskInfo>();
    288             int count = Math.min(numLatestTasks, RecentsDebugFlags.Static.MockTaskCount);
    289             for (int i = 0; i < count; i++) {
    290                 // Create a dummy component name
    291                 int packageIndex = i % RecentsDebugFlags.Static.MockTasksPackageCount;
    292                 ComponentName cn = new ComponentName("com.android.test" + packageIndex,
    293                         "com.android.test" + i + ".Activity");
    294                 String description = "" + i + " - " +
    295                         Long.toString(Math.abs(new Random().nextLong()), 36);
    296                 // Create the recent task info
    297                 ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
    298                 rti.id = rti.persistentId = rti.affiliatedTaskId = i;
    299                 rti.baseIntent = new Intent();
    300                 rti.baseIntent.setComponent(cn);
    301                 rti.description = description;
    302                 rti.firstActiveTime = rti.lastActiveTime = i;
    303                 if (i % 2 == 0) {
    304                     rti.taskDescription = new ActivityManager.TaskDescription(description,
    305                         Bitmap.createBitmap(mDummyIcon), null,
    306                         0xFF000000 | (0xFFFFFF & new Random().nextInt()),
    307                         0xFF000000 | (0xFFFFFF & new Random().nextInt()));
    308                 } else {
    309                     rti.taskDescription = new ActivityManager.TaskDescription();
    310                 }
    311                 tasks.add(rti);
    312             }
    313             return tasks;
    314         }
    315 
    316         // Remove home/recents/excluded tasks
    317         int minNumTasksToQuery = 10;
    318         int numTasksToQuery = Math.max(minNumTasksToQuery, numLatestTasks);
    319         int flags = ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS |
    320                 ActivityManager.RECENT_INGORE_DOCKED_STACK_TOP_TASK |
    321                 ActivityManager.RECENT_INGORE_PINNED_STACK_TASKS |
    322                 ActivityManager.RECENT_IGNORE_UNAVAILABLE |
    323                 ActivityManager.RECENT_INCLUDE_PROFILES;
    324         if (includeFrontMostExcludedTask) {
    325             flags |= ActivityManager.RECENT_WITH_EXCLUDED;
    326         }
    327         List<ActivityManager.RecentTaskInfo> tasks = null;
    328         try {
    329             tasks = mAm.getRecentTasksForUser(numTasksToQuery, flags, userId);
    330         } catch (Exception e) {
    331             Log.e(TAG, "Failed to get recent tasks", e);
    332         }
    333 
    334         // Break early if we can't get a valid set of tasks
    335         if (tasks == null) {
    336             return new ArrayList<>();
    337         }
    338 
    339         boolean isFirstValidTask = true;
    340         Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
    341         while (iter.hasNext()) {
    342             ActivityManager.RecentTaskInfo t = iter.next();
    343 
    344             // NOTE: The order of these checks happens in the expected order of the traversal of the
    345             // tasks
    346 
    347             // Remove the task if it or it's package are blacklsited
    348             if (sRecentsBlacklist.contains(t.realActivity.getClassName()) ||
    349                     sRecentsBlacklist.contains(t.realActivity.getPackageName())) {
    350                 iter.remove();
    351                 continue;
    352             }
    353 
    354             // Remove the task if it is marked as excluded, unless it is the first most task and we
    355             // are requested to include it
    356             boolean isExcluded = (t.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
    357                     == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
    358             isExcluded |= quietProfileIds.contains(t.userId);
    359             if (isExcluded && (!isFirstValidTask || !includeFrontMostExcludedTask)) {
    360                 iter.remove();
    361             }
    362 
    363             isFirstValidTask = false;
    364         }
    365 
    366         return tasks.subList(0, Math.min(tasks.size(), numLatestTasks));
    367     }
    368 
    369     /**
    370      * Returns the top running task.
    371      */
    372     public ActivityManager.RunningTaskInfo getRunningTask() {
    373         List<ActivityManager.RunningTaskInfo> tasks = mAm.getRunningTasks(1);
    374         if (tasks != null && !tasks.isEmpty()) {
    375             return tasks.get(0);
    376         }
    377         return null;
    378     }
    379 
    380     /**
    381      * Returns whether the recents activity is currently visible.
    382      */
    383     public boolean isRecentsActivityVisible() {
    384         return isRecentsActivityVisible(null);
    385     }
    386 
    387     /**
    388      * Returns whether the recents activity is currently visible.
    389      *
    390      * @param isHomeStackVisible if provided, will return whether the home stack is visible
    391      *                           regardless of the recents visibility
    392      */
    393     public boolean isRecentsActivityVisible(MutableBoolean isHomeStackVisible) {
    394         if (mIam == null) return false;
    395 
    396         try {
    397             ActivityManager.StackInfo stackInfo = mIam.getStackInfo(
    398                     ActivityManager.StackId.HOME_STACK_ID);
    399             ActivityManager.StackInfo fullscreenStackInfo = mIam.getStackInfo(
    400                     ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID);
    401             ComponentName topActivity = stackInfo.topActivity;
    402             boolean homeStackVisibleNotOccluded = stackInfo.visible;
    403             if (fullscreenStackInfo != null) {
    404                 boolean isFullscreenStackOccludingHome = fullscreenStackInfo.visible &&
    405                         fullscreenStackInfo.position > stackInfo.position;
    406                 homeStackVisibleNotOccluded &= !isFullscreenStackOccludingHome;
    407             }
    408             if (isHomeStackVisible != null) {
    409                 isHomeStackVisible.value = homeStackVisibleNotOccluded;
    410             }
    411             return (homeStackVisibleNotOccluded && topActivity != null
    412                     && topActivity.getPackageName().equals(RecentsImpl.RECENTS_PACKAGE)
    413                     && (topActivity.getClassName().equals(RecentsImpl.RECENTS_ACTIVITY)
    414                         || topActivity.getClassName().equals(RecentsTvImpl.RECENTS_TV_ACTIVITY)));
    415         } catch (RemoteException e) {
    416             e.printStackTrace();
    417         }
    418         return false;
    419     }
    420 
    421     /**
    422      * Returns whether this device has freeform workspaces.
    423      */
    424     public boolean hasFreeformWorkspaceSupport() {
    425         return mHasFreeformWorkspaceSupport;
    426     }
    427 
    428     /**
    429      * Returns whether this device is in the safe mode.
    430      */
    431     public boolean isInSafeMode() {
    432         return mIsSafeMode;
    433     }
    434 
    435     /** Docks a task to the side of the screen and starts it. */
    436     public boolean startTaskInDockedMode(int taskId, int createMode) {
    437         if (mIam == null) return false;
    438 
    439         try {
    440             final ActivityOptions options = ActivityOptions.makeBasic();
    441             options.setDockCreateMode(createMode);
    442             options.setLaunchStackId(DOCKED_STACK_ID);
    443             mIam.startActivityFromRecents(taskId, options.toBundle());
    444             return true;
    445         } catch (Exception e) {
    446             Log.e(TAG, "Failed to dock task: " + taskId + " with createMode: " + createMode, e);
    447         }
    448         return false;
    449     }
    450 
    451     /** Docks an already resumed task to the side of the screen. */
    452     public boolean moveTaskToDockedStack(int taskId, int createMode, Rect initialBounds) {
    453         if (mIam == null) {
    454             return false;
    455         }
    456 
    457         try {
    458             return mIam.moveTaskToDockedStack(taskId, createMode, true /* onTop */,
    459                     false /* animate */, initialBounds, true /* moveHomeStackFront */ );
    460         } catch (RemoteException e) {
    461             e.printStackTrace();
    462         }
    463         return false;
    464     }
    465 
    466     /**
    467      * Returns whether the given stack id is the home stack id.
    468      */
    469     public static boolean isHomeStack(int stackId) {
    470         return stackId == HOME_STACK_ID;
    471     }
    472 
    473     /**
    474      * Returns whether the given stack id is the pinned stack id.
    475      */
    476     public static boolean isPinnedStack(int stackId){
    477         return stackId == PINNED_STACK_ID;
    478     }
    479 
    480     /**
    481      * Returns whether the given stack id is the docked stack id.
    482      */
    483     public static boolean isDockedStack(int stackId) {
    484         return stackId == DOCKED_STACK_ID;
    485     }
    486 
    487     /**
    488      * Returns whether the given stack id is the freeform workspace stack id.
    489      */
    490     public static boolean isFreeformStack(int stackId) {
    491         return stackId == FREEFORM_WORKSPACE_STACK_ID;
    492     }
    493 
    494     /**
    495      * @return whether there are any docked tasks for the current user.
    496      */
    497     public boolean hasDockedTask() {
    498         if (mIam == null) return false;
    499 
    500         ActivityManager.StackInfo stackInfo = null;
    501         try {
    502             stackInfo = mIam.getStackInfo(DOCKED_STACK_ID);
    503         } catch (RemoteException e) {
    504             e.printStackTrace();
    505         }
    506 
    507         if (stackInfo != null) {
    508             int userId = getCurrentUser();
    509             boolean hasUserTask = false;
    510             for (int i = stackInfo.taskUserIds.length - 1; i >= 0 && !hasUserTask; i--) {
    511                 hasUserTask = (stackInfo.taskUserIds[i] == userId);
    512             }
    513             return hasUserTask;
    514         }
    515         return false;
    516     }
    517 
    518     /**
    519      * Returns whether there is a soft nav bar.
    520      */
    521     public boolean hasSoftNavigationBar() {
    522         try {
    523             return WindowManagerGlobal.getWindowManagerService().hasNavigationBar();
    524         } catch (RemoteException e) {
    525             e.printStackTrace();
    526         }
    527         return false;
    528     }
    529 
    530     /**
    531      * Returns whether the device has a transposed nav bar (on the right of the screen) in the
    532      * current display orientation.
    533      */
    534     public boolean hasTransposedNavigationBar() {
    535         Rect insets = new Rect();
    536         getStableInsets(insets);
    537         return insets.right > 0;
    538     }
    539 
    540     /**
    541      * Cancels the current window transtion to/from Recents for the given task id.
    542      */
    543     public void cancelWindowTransition(int taskId) {
    544         if (mWm == null) return;
    545 
    546         try {
    547             WindowManagerGlobal.getWindowManagerService().cancelTaskWindowTransition(taskId);
    548         } catch (RemoteException e) {
    549             e.printStackTrace();
    550         }
    551     }
    552 
    553     /**
    554      * Cancels the current thumbnail transtion to/from Recents for the given task id.
    555      */
    556     public void cancelThumbnailTransition(int taskId) {
    557         if (mWm == null) return;
    558 
    559         try {
    560             WindowManagerGlobal.getWindowManagerService().cancelTaskThumbnailTransition(taskId);
    561         } catch (RemoteException e) {
    562             e.printStackTrace();
    563         }
    564     }
    565 
    566     /** Returns the top task thumbnail for the given task id */
    567     public ThumbnailData getTaskThumbnail(int taskId) {
    568         if (mAm == null) return null;
    569         ThumbnailData thumbnailData = new ThumbnailData();
    570 
    571         // If we are mocking, then just return a dummy thumbnail
    572         if (RecentsDebugFlags.Static.EnableMockTasks) {
    573             thumbnailData.thumbnail = Bitmap.createBitmap(mDummyThumbnailWidth,
    574                     mDummyThumbnailHeight, Bitmap.Config.ARGB_8888);
    575             thumbnailData.thumbnail.eraseColor(0xff333333);
    576             return thumbnailData;
    577         }
    578 
    579         getThumbnail(taskId, thumbnailData);
    580         if (thumbnailData.thumbnail != null) {
    581             thumbnailData.thumbnail.setHasAlpha(false);
    582             // We use a dumb heuristic for now, if the thumbnail is purely transparent in the top
    583             // left pixel, then assume the whole thumbnail is transparent. Generally, proper
    584             // screenshots are always composed onto a bitmap that has no alpha.
    585             if (Color.alpha(thumbnailData.thumbnail.getPixel(0, 0)) == 0) {
    586                 mBgProtectionCanvas.setBitmap(thumbnailData.thumbnail);
    587                 mBgProtectionCanvas.drawRect(0, 0, thumbnailData.thumbnail.getWidth(),
    588                         thumbnailData.thumbnail.getHeight(), mBgProtectionPaint);
    589                 mBgProtectionCanvas.setBitmap(null);
    590                 Log.e(TAG, "Invalid screenshot detected from getTaskThumbnail()");
    591             }
    592         }
    593         return thumbnailData;
    594     }
    595 
    596     /**
    597      * Returns a task thumbnail from the activity manager
    598      */
    599     public void getThumbnail(int taskId, ThumbnailData thumbnailDataOut) {
    600         if (mAm == null) {
    601             return;
    602         }
    603 
    604         ActivityManager.TaskThumbnail taskThumbnail = mAm.getTaskThumbnail(taskId);
    605         if (taskThumbnail == null) {
    606             return;
    607         }
    608 
    609         Bitmap thumbnail = taskThumbnail.mainThumbnail;
    610         ParcelFileDescriptor descriptor = taskThumbnail.thumbnailFileDescriptor;
    611         if (thumbnail == null && descriptor != null) {
    612             thumbnail = BitmapFactory.decodeFileDescriptor(descriptor.getFileDescriptor(),
    613                     null, sBitmapOptions);
    614         }
    615         if (descriptor != null) {
    616             try {
    617                 descriptor.close();
    618             } catch (IOException e) {
    619             }
    620         }
    621         thumbnailDataOut.thumbnail = thumbnail;
    622         thumbnailDataOut.thumbnailInfo = taskThumbnail.thumbnailInfo;
    623     }
    624 
    625     /**
    626      * Moves a task into another stack.
    627      */
    628     public void moveTaskToStack(int taskId, int stackId) {
    629         if (mIam == null) return;
    630 
    631         try {
    632             mIam.positionTaskInStack(taskId, stackId, 0);
    633         } catch (RemoteException | IllegalArgumentException e) {
    634             e.printStackTrace();
    635         }
    636     }
    637 
    638     /** Removes the task */
    639     public void removeTask(final int taskId) {
    640         if (mAm == null) return;
    641         if (RecentsDebugFlags.Static.EnableMockTasks) return;
    642 
    643         // Remove the task.
    644         BackgroundThread.getHandler().post(new Runnable() {
    645             @Override
    646             public void run() {
    647                 mAm.removeTask(taskId);
    648             }
    649         });
    650     }
    651 
    652     /**
    653      * Sends a message to close other system windows.
    654      */
    655     public void sendCloseSystemWindows(String reason) {
    656         if (ActivityManagerNative.isSystemReady()) {
    657             try {
    658                 mIam.closeSystemDialogs(reason);
    659             } catch (RemoteException e) {
    660             }
    661         }
    662     }
    663 
    664     /**
    665      * Returns the activity info for a given component name.
    666      *
    667      * @param cn The component name of the activity.
    668      * @param userId The userId of the user that this is for.
    669      */
    670     public ActivityInfo getActivityInfo(ComponentName cn, int userId) {
    671         if (mIpm == null) return null;
    672         if (RecentsDebugFlags.Static.EnableMockTasks) return new ActivityInfo();
    673 
    674         try {
    675             return mIpm.getActivityInfo(cn, PackageManager.GET_META_DATA, userId);
    676         } catch (RemoteException e) {
    677             e.printStackTrace();
    678             return null;
    679         }
    680     }
    681 
    682     /**
    683      * Returns the activity info for a given component name.
    684      *
    685      * @param cn The component name of the activity.
    686      */
    687     public ActivityInfo getActivityInfo(ComponentName cn) {
    688         if (mPm == null) return null;
    689         if (RecentsDebugFlags.Static.EnableMockTasks) return new ActivityInfo();
    690 
    691         try {
    692             return mPm.getActivityInfo(cn, PackageManager.GET_META_DATA);
    693         } catch (PackageManager.NameNotFoundException e) {
    694             e.printStackTrace();
    695             return null;
    696         }
    697     }
    698 
    699     /**
    700      * Returns the activity label, badging if necessary.
    701      */
    702     public String getBadgedActivityLabel(ActivityInfo info, int userId) {
    703         if (mPm == null) return null;
    704 
    705         // If we are mocking, then return a mock label
    706         if (RecentsDebugFlags.Static.EnableMockTasks) {
    707             return "Recent Task: " + userId;
    708         }
    709 
    710         return getBadgedLabel(info.loadLabel(mPm).toString(), userId);
    711     }
    712 
    713     /**
    714      * Returns the application label, badging if necessary.
    715      */
    716     public String getBadgedApplicationLabel(ApplicationInfo appInfo, int userId) {
    717         if (mPm == null) return null;
    718 
    719         // If we are mocking, then return a mock label
    720         if (RecentsDebugFlags.Static.EnableMockTasks) {
    721             return "Recent Task App: " + userId;
    722         }
    723 
    724         return getBadgedLabel(appInfo.loadLabel(mPm).toString(), userId);
    725     }
    726 
    727     /**
    728      * Returns the content description for a given task, badging it if necessary.  The content
    729      * description joins the app and activity labels.
    730      */
    731     public String getBadgedContentDescription(ActivityInfo info, int userId, Resources res) {
    732         // If we are mocking, then return a mock label
    733         if (RecentsDebugFlags.Static.EnableMockTasks) {
    734             return "Recent Task Content Description: " + userId;
    735         }
    736 
    737         String activityLabel = info.loadLabel(mPm).toString();
    738         String applicationLabel = info.applicationInfo.loadLabel(mPm).toString();
    739         String badgedApplicationLabel = getBadgedLabel(applicationLabel, userId);
    740         return applicationLabel.equals(activityLabel) ? badgedApplicationLabel
    741                 : res.getString(R.string.accessibility_recents_task_header,
    742                         badgedApplicationLabel, activityLabel);
    743     }
    744 
    745     /**
    746      * Returns the activity icon for the ActivityInfo for a user, badging if
    747      * necessary.
    748      */
    749     public Drawable getBadgedActivityIcon(ActivityInfo info, int userId) {
    750         if (mPm == null) return null;
    751 
    752         // If we are mocking, then return a mock label
    753         if (RecentsDebugFlags.Static.EnableMockTasks) {
    754             return new ColorDrawable(0xFF666666);
    755         }
    756 
    757         Drawable icon = info.loadIcon(mPm);
    758         return getBadgedIcon(icon, userId);
    759     }
    760 
    761     /**
    762      * Returns the application icon for the ApplicationInfo for a user, badging if
    763      * necessary.
    764      */
    765     public Drawable getBadgedApplicationIcon(ApplicationInfo appInfo, int userId) {
    766         if (mPm == null) return null;
    767 
    768         // If we are mocking, then return a mock label
    769         if (RecentsDebugFlags.Static.EnableMockTasks) {
    770             return new ColorDrawable(0xFF666666);
    771         }
    772 
    773         Drawable icon = appInfo.loadIcon(mPm);
    774         return getBadgedIcon(icon, userId);
    775     }
    776 
    777     /**
    778      * Returns the task description icon, loading and badging it if it necessary.
    779      */
    780     public Drawable getBadgedTaskDescriptionIcon(ActivityManager.TaskDescription taskDescription,
    781             int userId, Resources res) {
    782 
    783         // If we are mocking, then return a mock label
    784         if (RecentsDebugFlags.Static.EnableMockTasks) {
    785             return new ColorDrawable(0xFF666666);
    786         }
    787 
    788         Bitmap tdIcon = taskDescription.getInMemoryIcon();
    789         if (tdIcon == null) {
    790             tdIcon = ActivityManager.TaskDescription.loadTaskDescriptionIcon(
    791                     taskDescription.getIconFilename(), userId);
    792         }
    793         if (tdIcon != null) {
    794             return getBadgedIcon(new BitmapDrawable(res, tdIcon), userId);
    795         }
    796         return null;
    797     }
    798 
    799     /**
    800      * Returns the given icon for a user, badging if necessary.
    801      */
    802     private Drawable getBadgedIcon(Drawable icon, int userId) {
    803         if (userId != UserHandle.myUserId()) {
    804             icon = mPm.getUserBadgedIcon(icon, new UserHandle(userId));
    805         }
    806         return icon;
    807     }
    808 
    809     /**
    810      * Returns a banner used on TV for the specified Activity.
    811      */
    812     public Drawable getActivityBanner(ActivityInfo info) {
    813         if (mPm == null) return null;
    814 
    815         // If we are mocking, then return a mock banner
    816         if (RecentsDebugFlags.Static.EnableMockTasks) {
    817             return new ColorDrawable(0xFF666666);
    818         }
    819 
    820         Drawable banner = info.loadBanner(mPm);
    821         return banner;
    822     }
    823 
    824     /**
    825      * Returns a logo used on TV for the specified Activity.
    826      */
    827     public Drawable getActivityLogo(ActivityInfo info) {
    828         if (mPm == null) return null;
    829 
    830         // If we are mocking, then return a mock logo
    831         if (RecentsDebugFlags.Static.EnableMockTasks) {
    832             return new ColorDrawable(0xFF666666);
    833         }
    834 
    835         Drawable logo = info.loadLogo(mPm);
    836         return logo;
    837     }
    838 
    839 
    840     /**
    841      * Returns the given label for a user, badging if necessary.
    842      */
    843     private String getBadgedLabel(String label, int userId) {
    844         if (userId != UserHandle.myUserId()) {
    845             label = mPm.getUserBadgedLabel(label, new UserHandle(userId)).toString();
    846         }
    847         return label;
    848     }
    849 
    850     /** Returns the package name of the home activity. */
    851     public String getHomeActivityPackageName() {
    852         if (mPm == null) return null;
    853         if (RecentsDebugFlags.Static.EnableMockTasks) return null;
    854 
    855         ArrayList<ResolveInfo> homeActivities = new ArrayList<>();
    856         ComponentName defaultHomeActivity = mPm.getHomeActivities(homeActivities);
    857         if (defaultHomeActivity != null) {
    858             return defaultHomeActivity.getPackageName();
    859         } else if (homeActivities.size() == 1) {
    860             ResolveInfo info = homeActivities.get(0);
    861             if (info.activityInfo != null) {
    862                 return info.activityInfo.packageName;
    863             }
    864         }
    865         return null;
    866     }
    867 
    868     /**
    869      * Returns whether the provided {@param userId} represents the system user.
    870      */
    871     public boolean isSystemUser(int userId) {
    872         return userId == UserHandle.USER_SYSTEM;
    873     }
    874 
    875     /**
    876      * Returns the current user id.
    877      */
    878     public int getCurrentUser() {
    879         if (mAm == null) return 0;
    880 
    881         return mAm.getCurrentUser();
    882     }
    883 
    884     /**
    885      * Returns the processes user id.
    886      */
    887     public int getProcessUser() {
    888         if (mUm == null) return 0;
    889         return mUm.getUserHandle();
    890     }
    891 
    892     /**
    893      * Returns whether touch exploration is currently enabled.
    894      */
    895     public boolean isTouchExplorationEnabled() {
    896         if (mAccm == null) return false;
    897 
    898         return mAccm.isEnabled() && mAccm.isTouchExplorationEnabled();
    899     }
    900 
    901     /**
    902      * Returns whether the current task is in screen-pinning mode.
    903      */
    904     public boolean isScreenPinningActive() {
    905         if (mIam == null) return false;
    906 
    907         try {
    908             return mIam.isInLockTaskMode();
    909         } catch (RemoteException e) {
    910             return false;
    911         }
    912     }
    913 
    914     /**
    915      * Returns a global setting.
    916      */
    917     public int getGlobalSetting(Context context, String setting) {
    918         ContentResolver cr = context.getContentResolver();
    919         return Settings.Global.getInt(cr, setting, 0);
    920     }
    921 
    922     /**
    923      * Returns a system setting.
    924      */
    925     public int getSystemSetting(Context context, String setting) {
    926         ContentResolver cr = context.getContentResolver();
    927         return Settings.System.getInt(cr, setting, 0);
    928     }
    929 
    930     /**
    931      * Returns a system property.
    932      */
    933     public String getSystemProperty(String key) {
    934         return SystemProperties.get(key);
    935     }
    936 
    937     /**
    938      * Returns the smallest width/height.
    939      */
    940     public int getDeviceSmallestWidth() {
    941         if (mDisplay == null) return 0;
    942 
    943         Point smallestSizeRange = new Point();
    944         Point largestSizeRange = new Point();
    945         mDisplay.getCurrentSizeRange(smallestSizeRange, largestSizeRange);
    946         return smallestSizeRange.x;
    947     }
    948 
    949     /**
    950      * Returns the current display rect in the current display orientation.
    951      */
    952     public Rect getDisplayRect() {
    953         Rect displayRect = new Rect();
    954         if (mDisplay == null) return displayRect;
    955 
    956         Point p = new Point();
    957         mDisplay.getRealSize(p);
    958         displayRect.set(0, 0, p.x, p.y);
    959         return displayRect;
    960     }
    961 
    962     /**
    963      * Returns the window rect for the RecentsActivity, based on the dimensions of the home stack.
    964      */
    965     public Rect getWindowRect() {
    966         Rect windowRect = new Rect();
    967         if (mIam == null) return windowRect;
    968 
    969         try {
    970             // Use the home stack bounds
    971             ActivityManager.StackInfo stackInfo = mIam.getStackInfo(HOME_STACK_ID);
    972             if (stackInfo != null) {
    973                 windowRect.set(stackInfo.bounds);
    974             }
    975         } catch (RemoteException e) {
    976             e.printStackTrace();
    977         } finally {
    978             return windowRect;
    979         }
    980     }
    981 
    982     /** Starts an activity from recents. */
    983     public boolean startActivityFromRecents(Context context, Task.TaskKey taskKey, String taskName,
    984             ActivityOptions options) {
    985         if (mIam != null) {
    986             try {
    987                 if (taskKey.stackId == DOCKED_STACK_ID) {
    988                     // We show non-visible docked tasks in Recents, but we always want to launch
    989                     // them in the fullscreen stack.
    990                     if (options == null) {
    991                         options = ActivityOptions.makeBasic();
    992                     }
    993                     options.setLaunchStackId(FULLSCREEN_WORKSPACE_STACK_ID);
    994                 }
    995                 mIam.startActivityFromRecents(
    996                         taskKey.id, options == null ? null : options.toBundle());
    997                 return true;
    998             } catch (Exception e) {
    999                 Log.e(TAG, context.getString(R.string.recents_launch_error_message, taskName), e);
   1000             }
   1001         }
   1002         return false;
   1003     }
   1004 
   1005     /** Starts an in-place animation on the front most application windows. */
   1006     public void startInPlaceAnimationOnFrontMostApplication(ActivityOptions opts) {
   1007         if (mIam == null) return;
   1008 
   1009         try {
   1010             mIam.startInPlaceAnimationOnFrontMostApplication(opts);
   1011         } catch (Exception e) {
   1012             e.printStackTrace();
   1013         }
   1014     }
   1015 
   1016     /**
   1017      * Registers a task stack listener with the system.
   1018      * This should be called on the main thread.
   1019      */
   1020     public void registerTaskStackListener(TaskStackListener listener) {
   1021         if (mIam == null) return;
   1022 
   1023         mTaskStackListeners.add(listener);
   1024         if (mTaskStackListeners.size() == 1) {
   1025             // Register mTaskStackListener to IActivityManager only once if needed.
   1026             try {
   1027                 mIam.registerTaskStackListener(mTaskStackListener);
   1028             } catch (Exception e) {
   1029                 Log.w(TAG, "Failed to call registerTaskStackListener", e);
   1030             }
   1031         }
   1032     }
   1033 
   1034     public void endProlongedAnimations() {
   1035         if (mWm == null) {
   1036             return;
   1037         }
   1038         try {
   1039             WindowManagerGlobal.getWindowManagerService().endProlongedAnimations();
   1040         } catch (Exception e) {
   1041             e.printStackTrace();
   1042         }
   1043     }
   1044 
   1045     public void registerDockedStackListener(IDockedStackListener listener) {
   1046         if (mWm == null) return;
   1047 
   1048         try {
   1049             WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(listener);
   1050         } catch (Exception e) {
   1051             e.printStackTrace();
   1052         }
   1053     }
   1054 
   1055     /**
   1056      * Calculates the size of the dock divider in the current orientation.
   1057      */
   1058     public int getDockedDividerSize(Context context) {
   1059         Resources res = context.getResources();
   1060         int dividerWindowWidth = res.getDimensionPixelSize(
   1061                 com.android.internal.R.dimen.docked_stack_divider_thickness);
   1062         int dividerInsets = res.getDimensionPixelSize(
   1063                 com.android.internal.R.dimen.docked_stack_divider_insets);
   1064         return dividerWindowWidth - 2 * dividerInsets;
   1065     }
   1066 
   1067     public void requestKeyboardShortcuts(
   1068             Context context, KeyboardShortcutsReceiver receiver, int deviceId) {
   1069         mWm.requestAppKeyboardShortcuts(receiver, deviceId);
   1070     }
   1071 
   1072     public void getStableInsets(Rect outStableInsets) {
   1073         if (mWm == null) return;
   1074 
   1075         try {
   1076             WindowManagerGlobal.getWindowManagerService().getStableInsets(outStableInsets);
   1077         } catch (Exception e) {
   1078             e.printStackTrace();
   1079         }
   1080     }
   1081 
   1082     public void overridePendingAppTransitionMultiThumbFuture(
   1083             IAppTransitionAnimationSpecsFuture future, IRemoteCallback animStartedListener,
   1084             boolean scaleUp) {
   1085         try {
   1086             WindowManagerGlobal.getWindowManagerService()
   1087                     .overridePendingAppTransitionMultiThumbFuture(future, animStartedListener,
   1088                             scaleUp);
   1089         } catch (RemoteException e) {
   1090             Log.w(TAG, "Failed to override transition: " + e);
   1091         }
   1092     }
   1093 
   1094     private final class H extends Handler {
   1095         private static final int ON_TASK_STACK_CHANGED = 1;
   1096         private static final int ON_ACTIVITY_PINNED = 2;
   1097         private static final int ON_PINNED_ACTIVITY_RESTART_ATTEMPT = 3;
   1098         private static final int ON_PINNED_STACK_ANIMATION_ENDED = 4;
   1099         private static final int ON_ACTIVITY_FORCED_RESIZABLE = 5;
   1100         private static final int ON_ACTIVITY_DISMISSING_DOCKED_STACK = 6;
   1101 
   1102         @Override
   1103         public void handleMessage(Message msg) {
   1104             switch (msg.what) {
   1105                 case ON_TASK_STACK_CHANGED: {
   1106                     for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
   1107                         mTaskStackListeners.get(i).onTaskStackChanged();
   1108                     }
   1109                     break;
   1110                 }
   1111                 case ON_ACTIVITY_PINNED: {
   1112                     for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
   1113                         mTaskStackListeners.get(i).onActivityPinned();
   1114                     }
   1115                     break;
   1116                 }
   1117                 case ON_PINNED_ACTIVITY_RESTART_ATTEMPT: {
   1118                     for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
   1119                         mTaskStackListeners.get(i).onPinnedActivityRestartAttempt();
   1120                     }
   1121                     break;
   1122                 }
   1123                 case ON_PINNED_STACK_ANIMATION_ENDED: {
   1124                     for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
   1125                         mTaskStackListeners.get(i).onPinnedStackAnimationEnded();
   1126                     }
   1127                     break;
   1128                 }
   1129                 case ON_ACTIVITY_FORCED_RESIZABLE: {
   1130                     for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
   1131                         mTaskStackListeners.get(i).onActivityForcedResizable(
   1132                                 (String) msg.obj, msg.arg1);
   1133                     }
   1134                     break;
   1135                 }
   1136                 case ON_ACTIVITY_DISMISSING_DOCKED_STACK: {
   1137                     for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
   1138                         mTaskStackListeners.get(i).onActivityDismissingDockedStack();
   1139                     }
   1140                     break;
   1141                 }
   1142             }
   1143         }
   1144     }
   1145 }
   1146