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