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