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