OSDN Git Service

DO NOT MERGE. Grant MMS Uri permissions as the calling UID.
[android-x86/frameworks-base.git] / packages / SystemUI / src / com / android / systemui / recents / misc / SystemServicesProxy.java
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 }