SystemServicesProxy.java revision ac52f2892d5c72c26387d510093ddfc741a8a632
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                } else {
362                    rti.taskDescription = new ActivityManager.TaskDescription();
363                }
364                tasks.add(rti);
365            }
366            return tasks;
367        }
368
369        // Remove home/recents/excluded tasks
370        int minNumTasksToQuery = 10;
371        int numTasksToQuery = Math.max(minNumTasksToQuery, numLatestTasks);
372        int flags = ActivityManager.RECENT_IGNORE_HOME_AND_RECENTS_STACK_TASKS |
373                ActivityManager.RECENT_INGORE_DOCKED_STACK_TOP_TASK |
374                ActivityManager.RECENT_INGORE_PINNED_STACK_TASKS |
375                ActivityManager.RECENT_IGNORE_UNAVAILABLE |
376                ActivityManager.RECENT_INCLUDE_PROFILES;
377        if (includeFrontMostExcludedTask) {
378            flags |= ActivityManager.RECENT_WITH_EXCLUDED;
379        }
380        List<ActivityManager.RecentTaskInfo> tasks = null;
381        try {
382            tasks = mAm.getRecentTasksForUser(numTasksToQuery, flags, userId);
383        } catch (Exception e) {
384            Log.e(TAG, "Failed to get recent tasks", e);
385        }
386
387        // Break early if we can't get a valid set of tasks
388        if (tasks == null) {
389            return new ArrayList<>();
390        }
391
392        boolean isFirstValidTask = true;
393        Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
394        while (iter.hasNext()) {
395            ActivityManager.RecentTaskInfo t = iter.next();
396
397            // NOTE: The order of these checks happens in the expected order of the traversal of the
398            // tasks
399
400            // Remove the task if it or it's package are blacklsited
401            if (sRecentsBlacklist.contains(t.realActivity.getClassName()) ||
402                    sRecentsBlacklist.contains(t.realActivity.getPackageName())) {
403                iter.remove();
404                continue;
405            }
406
407            // Remove the task if it is marked as excluded, unless it is the first most task and we
408            // are requested to include it
409            boolean isExcluded = (t.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
410                    == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
411            isExcluded |= quietProfileIds.contains(t.userId);
412            if (isExcluded && (!isFirstValidTask || !includeFrontMostExcludedTask)) {
413                iter.remove();
414            }
415
416            isFirstValidTask = false;
417        }
418
419        return tasks.subList(0, Math.min(tasks.size(), numLatestTasks));
420    }
421
422    /**
423     * Returns the top running task.
424     */
425    public ActivityManager.RunningTaskInfo getRunningTask() {
426        List<ActivityManager.RunningTaskInfo> tasks = mAm.getRunningTasks(1);
427        if (tasks != null && !tasks.isEmpty()) {
428            return tasks.get(0);
429        }
430        return null;
431    }
432
433    /**
434     * Returns whether the recents activity is currently visible.
435     */
436    public boolean isRecentsActivityVisible() {
437        return isRecentsActivityVisible(null);
438    }
439
440    /**
441     * Returns whether the recents activity is currently visible.
442     *
443     * @param isHomeStackVisible if provided, will return whether the home stack is visible
444     *                           regardless of the recents visibility
445     */
446    public boolean isRecentsActivityVisible(MutableBoolean isHomeStackVisible) {
447        if (mIam == null) return false;
448
449        try {
450            ActivityManager.StackInfo homeStackInfo = mIam.getStackInfo(
451                    ActivityManager.StackId.HOME_STACK_ID);
452            ActivityManager.StackInfo fullscreenStackInfo = mIam.getStackInfo(
453                    ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID);
454            ActivityManager.StackInfo recentsStackInfo = mIam.getStackInfo(
455                    ActivityManager.StackId.RECENTS_STACK_ID);
456
457            boolean homeStackVisibleNotOccluded = isStackNotOccluded(homeStackInfo,
458                    fullscreenStackInfo);
459            boolean recentsStackVisibleNotOccluded = isStackNotOccluded(recentsStackInfo,
460                    fullscreenStackInfo);
461            if (isHomeStackVisible != null) {
462                isHomeStackVisible.value = homeStackVisibleNotOccluded;
463            }
464            ComponentName topActivity = recentsStackInfo != null ?
465                    recentsStackInfo.topActivity : null;
466            return (recentsStackVisibleNotOccluded && topActivity != null
467                    && topActivity.getPackageName().equals(RecentsImpl.RECENTS_PACKAGE)
468                    && Recents.RECENTS_ACTIVITIES.contains(topActivity.getClassName()));
469        } catch (RemoteException e) {
470            e.printStackTrace();
471        }
472        return false;
473    }
474
475    private boolean isStackNotOccluded(ActivityManager.StackInfo stackInfo,
476            ActivityManager.StackInfo fullscreenStackInfo) {
477        boolean stackVisibleNotOccluded = stackInfo == null || stackInfo.visible;
478        if (fullscreenStackInfo != null && stackInfo != null) {
479            boolean isFullscreenStackOccludingg = fullscreenStackInfo.visible &&
480                    fullscreenStackInfo.position > stackInfo.position;
481            stackVisibleNotOccluded &= !isFullscreenStackOccludingg;
482        }
483        return stackVisibleNotOccluded;
484    }
485
486    /**
487     * Returns whether this device has freeform workspaces.
488     */
489    public boolean hasFreeformWorkspaceSupport() {
490        return mHasFreeformWorkspaceSupport;
491    }
492
493    /**
494     * Returns whether this device is in the safe mode.
495     */
496    public boolean isInSafeMode() {
497        return mIsSafeMode;
498    }
499
500    /** Docks a task to the side of the screen and starts it. */
501    public boolean startTaskInDockedMode(int taskId, int createMode) {
502        if (mIam == null) return false;
503
504        try {
505            final ActivityOptions options = ActivityOptions.makeBasic();
506            options.setDockCreateMode(createMode);
507            options.setLaunchStackId(DOCKED_STACK_ID);
508            mIam.startActivityFromRecents(taskId, options.toBundle());
509            return true;
510        } catch (Exception e) {
511            Log.e(TAG, "Failed to dock task: " + taskId + " with createMode: " + createMode, e);
512        }
513        return false;
514    }
515
516    /** Docks an already resumed task to the side of the screen. */
517    public boolean moveTaskToDockedStack(int taskId, int createMode, Rect initialBounds) {
518        if (mIam == null) {
519            return false;
520        }
521
522        try {
523            return mIam.moveTaskToDockedStack(taskId, createMode, true /* onTop */,
524                    false /* animate */, initialBounds);
525        } catch (RemoteException e) {
526            e.printStackTrace();
527        }
528        return false;
529    }
530
531    /**
532     * Returns whether the given stack id is the home stack id.
533     */
534    public static boolean isHomeStack(int stackId) {
535        return stackId == HOME_STACK_ID;
536    }
537
538    /**
539     * Returns whether the given stack id is the pinned stack id.
540     */
541    public static boolean isPinnedStack(int stackId){
542        return stackId == PINNED_STACK_ID;
543    }
544
545    /**
546     * Returns whether the given stack id is the docked stack id.
547     */
548    public static boolean isDockedStack(int stackId) {
549        return stackId == DOCKED_STACK_ID;
550    }
551
552    /**
553     * Returns whether the given stack id is the freeform workspace stack id.
554     */
555    public static boolean isFreeformStack(int stackId) {
556        return stackId == FREEFORM_WORKSPACE_STACK_ID;
557    }
558
559    /**
560     * @return whether there are any docked tasks for the current user.
561     */
562    public boolean hasDockedTask() {
563        if (mIam == null) return false;
564
565        ActivityManager.StackInfo stackInfo = null;
566        try {
567            stackInfo = mIam.getStackInfo(DOCKED_STACK_ID);
568        } catch (RemoteException e) {
569            e.printStackTrace();
570        }
571
572        if (stackInfo != null) {
573            int userId = getCurrentUser();
574            boolean hasUserTask = false;
575            for (int i = stackInfo.taskUserIds.length - 1; i >= 0 && !hasUserTask; i--) {
576                hasUserTask = (stackInfo.taskUserIds[i] == userId);
577            }
578            return hasUserTask;
579        }
580        return false;
581    }
582
583    /**
584     * Returns whether there is a soft nav bar.
585     */
586    public boolean hasSoftNavigationBar() {
587        try {
588            return WindowManagerGlobal.getWindowManagerService().hasNavigationBar();
589        } catch (RemoteException e) {
590            e.printStackTrace();
591        }
592        return false;
593    }
594
595    /**
596     * Returns whether the device has a transposed nav bar (on the right of the screen) in the
597     * current display orientation.
598     */
599    public boolean hasTransposedNavigationBar() {
600        Rect insets = new Rect();
601        getStableInsets(insets);
602        return insets.right > 0;
603    }
604
605    /**
606     * Cancels the current window transtion to/from Recents for the given task id.
607     */
608    public void cancelWindowTransition(int taskId) {
609        if (mIam == null) return;
610
611        try {
612            mIam.cancelTaskWindowTransition(taskId);
613        } catch (RemoteException e) {
614            e.printStackTrace();
615        }
616    }
617
618    /**
619     * Cancels the current thumbnail transtion to/from Recents for the given task id.
620     */
621    public void cancelThumbnailTransition(int taskId) {
622        if (mIam == null) return;
623
624        try {
625            mIam.cancelTaskThumbnailTransition(taskId);
626        } catch (RemoteException e) {
627            e.printStackTrace();
628        }
629    }
630
631    /** Returns the top task thumbnail for the given task id */
632    public ThumbnailData getTaskThumbnail(int taskId, boolean reduced) {
633        if (mAm == null) return null;
634
635        // If we are mocking, then just return a dummy thumbnail
636        if (RecentsDebugFlags.Static.EnableMockTasks) {
637            ThumbnailData thumbnailData = new ThumbnailData();
638            thumbnailData.thumbnail = Bitmap.createBitmap(mDummyThumbnailWidth,
639                    mDummyThumbnailHeight, Bitmap.Config.ARGB_8888);
640            thumbnailData.thumbnail.eraseColor(0xff333333);
641            return thumbnailData;
642        }
643
644        ThumbnailData thumbnailData = getThumbnail(taskId, reduced);
645        if (thumbnailData.thumbnail != null && !ActivityManager.ENABLE_TASK_SNAPSHOTS) {
646            thumbnailData.thumbnail.setHasAlpha(false);
647            // We use a dumb heuristic for now, if the thumbnail is purely transparent in the top
648            // left pixel, then assume the whole thumbnail is transparent. Generally, proper
649            // screenshots are always composed onto a bitmap that has no alpha.
650            if (Color.alpha(thumbnailData.thumbnail.getPixel(0, 0)) == 0) {
651                mBgProtectionCanvas.setBitmap(thumbnailData.thumbnail);
652                mBgProtectionCanvas.drawRect(0, 0, thumbnailData.thumbnail.getWidth(),
653                        thumbnailData.thumbnail.getHeight(), mBgProtectionPaint);
654                mBgProtectionCanvas.setBitmap(null);
655                Log.e(TAG, "Invalid screenshot detected from getTaskThumbnail()");
656            }
657        }
658        return thumbnailData;
659    }
660
661    /**
662     * Returns a task thumbnail from the activity manager
663     */
664    public @NonNull ThumbnailData getThumbnail(int taskId, boolean reducedResolution) {
665        if (mAm == null) {
666            return new ThumbnailData();
667        }
668
669        final ThumbnailData thumbnailData;
670        if (ActivityManager.ENABLE_TASK_SNAPSHOTS) {
671            ActivityManager.TaskSnapshot snapshot = null;
672            try {
673                snapshot = ActivityManager.getService().getTaskSnapshot(taskId,
674                        false /* reducedResolution */);
675            } catch (RemoteException e) {
676                Log.w(TAG, "Failed to retrieve snapshot", e);
677            }
678            if (snapshot != null) {
679                thumbnailData = ThumbnailData.createFromTaskSnapshot(snapshot);
680            } else {
681                return new ThumbnailData();
682            }
683        } else {
684            ActivityManager.TaskThumbnail taskThumbnail = mAm.getTaskThumbnail(taskId);
685            if (taskThumbnail == null) {
686                return new ThumbnailData();
687            }
688
689            Bitmap thumbnail = taskThumbnail.mainThumbnail;
690            ParcelFileDescriptor descriptor = taskThumbnail.thumbnailFileDescriptor;
691            if (thumbnail == null && descriptor != null) {
692                thumbnail = BitmapFactory.decodeFileDescriptor(descriptor.getFileDescriptor(),
693                        null, sBitmapOptions);
694            }
695            if (descriptor != null) {
696                try {
697                    descriptor.close();
698                } catch (IOException e) {
699                }
700            }
701            thumbnailData = new ThumbnailData();
702            thumbnailData.thumbnail = thumbnail;
703            thumbnailData.orientation = taskThumbnail.thumbnailInfo.screenOrientation;
704            thumbnailData.insets.setEmpty();
705        }
706        return thumbnailData;
707    }
708
709    /**
710     * Moves a task into another stack.
711     */
712    public void moveTaskToStack(int taskId, int stackId) {
713        if (mIam == null) return;
714
715        try {
716            mIam.positionTaskInStack(taskId, stackId, 0);
717        } catch (RemoteException | IllegalArgumentException e) {
718            e.printStackTrace();
719        }
720    }
721
722    /** Removes the task */
723    public void removeTask(final int taskId) {
724        if (mAm == null) return;
725        if (RecentsDebugFlags.Static.EnableMockTasks) return;
726
727        // Remove the task.
728        BackgroundThread.getHandler().post(new Runnable() {
729            @Override
730            public void run() {
731                mAm.removeTask(taskId);
732            }
733        });
734    }
735
736    /**
737     * Sends a message to close other system windows.
738     */
739    public void sendCloseSystemWindows(String reason) {
740        try {
741            mIam.closeSystemDialogs(reason);
742        } catch (RemoteException e) {
743        }
744    }
745
746    /**
747     * Returns the activity info for a given component name.
748     *
749     * @param cn The component name of the activity.
750     * @param userId The userId of the user that this is for.
751     */
752    public ActivityInfo getActivityInfo(ComponentName cn, int userId) {
753        if (mIpm == null) return null;
754        if (RecentsDebugFlags.Static.EnableMockTasks) return new ActivityInfo();
755
756        try {
757            return mIpm.getActivityInfo(cn, PackageManager.GET_META_DATA, userId);
758        } catch (RemoteException e) {
759            e.printStackTrace();
760            return null;
761        }
762    }
763
764    /**
765     * Returns the activity info for a given component name.
766     *
767     * @param cn The component name of the activity.
768     */
769    public ActivityInfo getActivityInfo(ComponentName cn) {
770        if (mPm == null) return null;
771        if (RecentsDebugFlags.Static.EnableMockTasks) return new ActivityInfo();
772
773        try {
774            return mPm.getActivityInfo(cn, PackageManager.GET_META_DATA);
775        } catch (PackageManager.NameNotFoundException e) {
776            e.printStackTrace();
777            return null;
778        }
779    }
780
781    /**
782     * Returns the activity label, badging if necessary.
783     */
784    public String getBadgedActivityLabel(ActivityInfo info, int userId) {
785        if (mPm == null) return null;
786
787        // If we are mocking, then return a mock label
788        if (RecentsDebugFlags.Static.EnableMockTasks) {
789            return "Recent Task: " + userId;
790        }
791
792        return getBadgedLabel(info.loadLabel(mPm).toString(), userId);
793    }
794
795    /**
796     * Returns the application label, badging if necessary.
797     */
798    public String getBadgedApplicationLabel(ApplicationInfo appInfo, int userId) {
799        if (mPm == null) return null;
800
801        // If we are mocking, then return a mock label
802        if (RecentsDebugFlags.Static.EnableMockTasks) {
803            return "Recent Task App: " + userId;
804        }
805
806        return getBadgedLabel(appInfo.loadLabel(mPm).toString(), userId);
807    }
808
809    /**
810     * Returns the content description for a given task, badging it if necessary.  The content
811     * description joins the app and activity labels.
812     */
813    public String getBadgedContentDescription(ActivityInfo info, int userId, Resources res) {
814        // If we are mocking, then return a mock label
815        if (RecentsDebugFlags.Static.EnableMockTasks) {
816            return "Recent Task Content Description: " + userId;
817        }
818
819        String activityLabel = info.loadLabel(mPm).toString();
820        String applicationLabel = info.applicationInfo.loadLabel(mPm).toString();
821        String badgedApplicationLabel = getBadgedLabel(applicationLabel, userId);
822        return applicationLabel.equals(activityLabel) ? badgedApplicationLabel
823                : res.getString(R.string.accessibility_recents_task_header,
824                        badgedApplicationLabel, activityLabel);
825    }
826
827    /**
828     * Returns the activity icon for the ActivityInfo for a user, badging if
829     * necessary.
830     */
831    public Drawable getBadgedActivityIcon(ActivityInfo info, int userId) {
832        if (mPm == null) return null;
833
834        // If we are mocking, then return a mock label
835        if (RecentsDebugFlags.Static.EnableMockTasks) {
836            return new ColorDrawable(0xFF666666);
837        }
838
839        Drawable icon = mLauncherIcons.wrapIconDrawableWithShadow(info.loadIcon(mPm));
840        return getBadgedIcon(icon, userId);
841    }
842
843    /**
844     * Returns the application icon for the ApplicationInfo for a user, badging if
845     * necessary.
846     */
847    public Drawable getBadgedApplicationIcon(ApplicationInfo appInfo, int userId) {
848        if (mPm == null) return null;
849
850        // If we are mocking, then return a mock label
851        if (RecentsDebugFlags.Static.EnableMockTasks) {
852            return new ColorDrawable(0xFF666666);
853        }
854
855        Drawable icon = mLauncherIcons.wrapIconDrawableWithShadow(appInfo.loadIcon(mPm));
856        return getBadgedIcon(icon, userId);
857    }
858
859    /**
860     * Returns the task description icon, loading and badging it if it necessary.
861     */
862    public Drawable getBadgedTaskDescriptionIcon(ActivityManager.TaskDescription taskDescription,
863            int userId, Resources res) {
864
865        // If we are mocking, then return a mock label
866        if (RecentsDebugFlags.Static.EnableMockTasks) {
867            return new ColorDrawable(0xFF666666);
868        }
869
870        Bitmap tdIcon = taskDescription.getInMemoryIcon();
871        if (tdIcon == null) {
872            tdIcon = ActivityManager.TaskDescription.loadTaskDescriptionIcon(
873                    taskDescription.getIconFilename(), userId);
874        }
875        if (tdIcon != null) {
876            return getBadgedIcon(new BitmapDrawable(res, tdIcon), userId);
877        }
878        return null;
879    }
880
881    public ActivityManager.TaskDescription getTaskDescription(int taskId) {
882        try {
883            return mIam.getTaskDescription(taskId);
884        } catch (RemoteException e) {
885            return null;
886        }
887    }
888
889    /**
890     * Returns the given icon for a user, badging if necessary.
891     */
892    private Drawable getBadgedIcon(Drawable icon, int userId) {
893        if (userId != UserHandle.myUserId()) {
894            icon = mPm.getUserBadgedIcon(icon, new UserHandle(userId));
895        }
896        return icon;
897    }
898
899    /**
900     * Returns a banner used on TV for the specified Activity.
901     */
902    public Drawable getActivityBanner(ActivityInfo info) {
903        if (mPm == null) return null;
904
905        // If we are mocking, then return a mock banner
906        if (RecentsDebugFlags.Static.EnableMockTasks) {
907            return new ColorDrawable(0xFF666666);
908        }
909
910        Drawable banner = info.loadBanner(mPm);
911        return banner;
912    }
913
914    /**
915     * Returns a logo used on TV for the specified Activity.
916     */
917    public Drawable getActivityLogo(ActivityInfo info) {
918        if (mPm == null) return null;
919
920        // If we are mocking, then return a mock logo
921        if (RecentsDebugFlags.Static.EnableMockTasks) {
922            return new ColorDrawable(0xFF666666);
923        }
924
925        Drawable logo = info.loadLogo(mPm);
926        return logo;
927    }
928
929
930    /**
931     * Returns the given label for a user, badging if necessary.
932     */
933    private String getBadgedLabel(String label, int userId) {
934        if (userId != UserHandle.myUserId()) {
935            label = mPm.getUserBadgedLabel(label, new UserHandle(userId)).toString();
936        }
937        return label;
938    }
939
940    /**
941     * Returns whether the provided {@param userId} is currently locked (and showing Keyguard).
942     */
943    public boolean isDeviceLocked(int userId) {
944        if (mKgm == null) {
945            return false;
946        }
947        return mKgm.isDeviceLocked(userId);
948    }
949
950    /** Returns the package name of the home activity. */
951    public String getHomeActivityPackageName() {
952        if (mPm == null) return null;
953        if (RecentsDebugFlags.Static.EnableMockTasks) return null;
954
955        ArrayList<ResolveInfo> homeActivities = new ArrayList<>();
956        ComponentName defaultHomeActivity = mPm.getHomeActivities(homeActivities);
957        if (defaultHomeActivity != null) {
958            return defaultHomeActivity.getPackageName();
959        } else if (homeActivities.size() == 1) {
960            ResolveInfo info = homeActivities.get(0);
961            if (info.activityInfo != null) {
962                return info.activityInfo.packageName;
963            }
964        }
965        return null;
966    }
967
968    /**
969     * Returns whether the provided {@param userId} represents the system user.
970     */
971    public boolean isSystemUser(int userId) {
972        return userId == UserHandle.USER_SYSTEM;
973    }
974
975    /**
976     * Returns the current user id.
977     */
978    public int getCurrentUser() {
979        if (mAm == null) return 0;
980
981        return mAm.getCurrentUser();
982    }
983
984    /**
985     * Returns the processes user id.
986     */
987    public int getProcessUser() {
988        if (mUm == null) return 0;
989        return mUm.getUserHandle();
990    }
991
992    /**
993     * Returns whether touch exploration is currently enabled.
994     */
995    public boolean isTouchExplorationEnabled() {
996        if (mAccm == null) return false;
997
998        return mAccm.isEnabled() && mAccm.isTouchExplorationEnabled();
999    }
1000
1001    /**
1002     * Returns whether the current task is in screen-pinning mode.
1003     */
1004    public boolean isScreenPinningActive() {
1005        if (mIam == null) return false;
1006
1007        try {
1008            return mIam.isInLockTaskMode();
1009        } catch (RemoteException e) {
1010            return false;
1011        }
1012    }
1013
1014    /**
1015     * Returns a global setting.
1016     */
1017    public int getGlobalSetting(Context context, String setting) {
1018        ContentResolver cr = context.getContentResolver();
1019        return Settings.Global.getInt(cr, setting, 0);
1020    }
1021
1022    /**
1023     * Returns a system setting.
1024     */
1025    public int getSystemSetting(Context context, String setting) {
1026        ContentResolver cr = context.getContentResolver();
1027        return Settings.System.getInt(cr, setting, 0);
1028    }
1029
1030    /**
1031     * Returns a system property.
1032     */
1033    public String getSystemProperty(String key) {
1034        return SystemProperties.get(key);
1035    }
1036
1037    /**
1038     * Returns the smallest width/height.
1039     */
1040    public int getDeviceSmallestWidth() {
1041        if (mDisplay == null) return 0;
1042
1043        Point smallestSizeRange = new Point();
1044        Point largestSizeRange = new Point();
1045        mDisplay.getCurrentSizeRange(smallestSizeRange, largestSizeRange);
1046        return smallestSizeRange.x;
1047    }
1048
1049    /**
1050     * Returns the current display rect in the current display orientation.
1051     */
1052    public Rect getDisplayRect() {
1053        Rect displayRect = new Rect();
1054        if (mDisplay == null) return displayRect;
1055
1056        Point p = new Point();
1057        mDisplay.getRealSize(p);
1058        displayRect.set(0, 0, p.x, p.y);
1059        return displayRect;
1060    }
1061
1062    /**
1063     * Returns the window rect for the RecentsActivity, based on the dimensions of the recents stack
1064     */
1065    public Rect getWindowRect() {
1066        Rect windowRect = new Rect();
1067        if (mIam == null) return windowRect;
1068
1069        try {
1070            // Use the recents stack bounds, fallback to fullscreen stack if it is null
1071            ActivityManager.StackInfo stackInfo = mIam.getStackInfo(RECENTS_STACK_ID);
1072            if (stackInfo == null) {
1073                stackInfo = mIam.getStackInfo(FULLSCREEN_WORKSPACE_STACK_ID);
1074            }
1075            if (stackInfo != null) {
1076                windowRect.set(stackInfo.bounds);
1077            }
1078        } catch (RemoteException e) {
1079            e.printStackTrace();
1080        } finally {
1081            return windowRect;
1082        }
1083    }
1084
1085    /** Starts an activity from recents. */
1086    public boolean startActivityFromRecents(Context context, Task.TaskKey taskKey, String taskName,
1087            ActivityOptions options, int stackId) {
1088        if (mIam != null) {
1089            try {
1090                if (taskKey.stackId == DOCKED_STACK_ID) {
1091                    // We show non-visible docked tasks in Recents, but we always want to launch
1092                    // them in the fullscreen stack.
1093                    if (options == null) {
1094                        options = ActivityOptions.makeBasic();
1095                    }
1096                    options.setLaunchStackId(FULLSCREEN_WORKSPACE_STACK_ID);
1097                } else if (stackId != INVALID_STACK_ID){
1098                    if (options == null) {
1099                        options = ActivityOptions.makeBasic();
1100                    }
1101                    options.setLaunchStackId(stackId);
1102                }
1103                mIam.startActivityFromRecents(
1104                        taskKey.id, options == null ? null : options.toBundle());
1105                return true;
1106            } catch (Exception e) {
1107                Log.e(TAG, context.getString(R.string.recents_launch_error_message, taskName), e);
1108            }
1109        }
1110        return false;
1111    }
1112
1113    /** Starts an in-place animation on the front most application windows. */
1114    public void startInPlaceAnimationOnFrontMostApplication(ActivityOptions opts) {
1115        if (mIam == null) return;
1116
1117        try {
1118            mIam.startInPlaceAnimationOnFrontMostApplication(
1119                    opts == null ? null : opts.toBundle());
1120        } catch (Exception e) {
1121            e.printStackTrace();
1122        }
1123    }
1124
1125    /**
1126     * Registers a task stack listener with the system.
1127     * This should be called on the main thread.
1128     */
1129    public void registerTaskStackListener(TaskStackListener listener) {
1130        if (mIam == null) return;
1131
1132        mTaskStackListeners.add(listener);
1133        if (mTaskStackListeners.size() == 1) {
1134            // Register mTaskStackListener to IActivityManager only once if needed.
1135            try {
1136                mIam.registerTaskStackListener(mTaskStackListener);
1137            } catch (Exception e) {
1138                Log.w(TAG, "Failed to call registerTaskStackListener", e);
1139            }
1140        }
1141    }
1142
1143    public void endProlongedAnimations() {
1144        if (mWm == null) {
1145            return;
1146        }
1147        try {
1148            WindowManagerGlobal.getWindowManagerService().endProlongedAnimations();
1149        } catch (Exception e) {
1150            e.printStackTrace();
1151        }
1152    }
1153
1154    public void registerDockedStackListener(IDockedStackListener listener) {
1155        if (mWm == null) return;
1156
1157        try {
1158            WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(listener);
1159        } catch (Exception e) {
1160            e.printStackTrace();
1161        }
1162    }
1163
1164    /**
1165     * Calculates the size of the dock divider in the current orientation.
1166     */
1167    public int getDockedDividerSize(Context context) {
1168        Resources res = context.getResources();
1169        int dividerWindowWidth = res.getDimensionPixelSize(
1170                com.android.internal.R.dimen.docked_stack_divider_thickness);
1171        int dividerInsets = res.getDimensionPixelSize(
1172                com.android.internal.R.dimen.docked_stack_divider_insets);
1173        return dividerWindowWidth - 2 * dividerInsets;
1174    }
1175
1176    public void requestKeyboardShortcuts(
1177            Context context, KeyboardShortcutsReceiver receiver, int deviceId) {
1178        mWm.requestAppKeyboardShortcuts(receiver, deviceId);
1179    }
1180
1181    public void getStableInsets(Rect outStableInsets) {
1182        if (mWm == null) return;
1183
1184        try {
1185            WindowManagerGlobal.getWindowManagerService().getStableInsets(Display.DEFAULT_DISPLAY,
1186                    outStableInsets);
1187        } catch (Exception e) {
1188            e.printStackTrace();
1189        }
1190    }
1191
1192    public void overridePendingAppTransitionMultiThumbFuture(
1193            IAppTransitionAnimationSpecsFuture future, IRemoteCallback animStartedListener,
1194            boolean scaleUp) {
1195        try {
1196            WindowManagerGlobal.getWindowManagerService()
1197                    .overridePendingAppTransitionMultiThumbFuture(future, animStartedListener,
1198                            scaleUp);
1199        } catch (RemoteException e) {
1200            Log.w(TAG, "Failed to override transition: " + e);
1201        }
1202    }
1203
1204    /**
1205     * Updates the visibility of recents.
1206     */
1207    public void setRecentsVisibility(boolean visible) {
1208        try {
1209            mIwm.setRecentsVisibility(visible);
1210        } catch (RemoteException e) {
1211            Log.e(TAG, "Unable to reach window manager", e);
1212        }
1213    }
1214
1215    /**
1216     * Updates the visibility of the picture-in-picture.
1217     */
1218    public void setPipVisibility(boolean visible) {
1219        try {
1220            mIwm.setPipVisibility(visible);
1221        } catch (RemoteException e) {
1222            Log.e(TAG, "Unable to reach window manager", e);
1223        }
1224    }
1225
1226    private final class H extends Handler {
1227        private static final int ON_TASK_STACK_CHANGED = 1;
1228        private static final int ON_TASK_SNAPSHOT_CHANGED = 2;
1229        private static final int ON_ACTIVITY_PINNED = 3;
1230        private static final int ON_PINNED_ACTIVITY_RESTART_ATTEMPT = 4;
1231        private static final int ON_PINNED_STACK_ANIMATION_ENDED = 5;
1232        private static final int ON_ACTIVITY_FORCED_RESIZABLE = 6;
1233        private static final int ON_ACTIVITY_DISMISSING_DOCKED_STACK = 7;
1234        private static final int ON_TASK_PROFILE_LOCKED = 8;
1235        private static final int ON_PINNED_STACK_ANIMATION_STARTED = 9;
1236        private static final int ON_ACTIVITY_UNPINNED = 10;
1237
1238        @Override
1239        public void handleMessage(Message msg) {
1240            switch (msg.what) {
1241                case ON_TASK_STACK_CHANGED: {
1242                    for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
1243                        mTaskStackListeners.get(i).onTaskStackChanged();
1244                    }
1245                    break;
1246                }
1247                case ON_TASK_SNAPSHOT_CHANGED: {
1248                    for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
1249                        mTaskStackListeners.get(i).onTaskSnapshotChanged(msg.arg1,
1250                                (TaskSnapshot) msg.obj);
1251                    }
1252                    break;
1253                }
1254                case ON_ACTIVITY_PINNED: {
1255                    for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
1256                        mTaskStackListeners.get(i).onActivityPinned((String) msg.obj);
1257                    }
1258                    break;
1259                }
1260                case ON_ACTIVITY_UNPINNED: {
1261                    for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
1262                        mTaskStackListeners.get(i).onActivityUnpinned();
1263                    }
1264                    break;
1265                }
1266                case ON_PINNED_ACTIVITY_RESTART_ATTEMPT: {
1267                    for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
1268                        mTaskStackListeners.get(i).onPinnedActivityRestartAttempt();
1269                    }
1270                    break;
1271                }
1272                case ON_PINNED_STACK_ANIMATION_STARTED: {
1273                    for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
1274                        mTaskStackListeners.get(i).onPinnedStackAnimationStarted();
1275                    }
1276                    break;
1277                }
1278                case ON_PINNED_STACK_ANIMATION_ENDED: {
1279                    for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
1280                        mTaskStackListeners.get(i).onPinnedStackAnimationEnded();
1281                    }
1282                    break;
1283                }
1284                case ON_ACTIVITY_FORCED_RESIZABLE: {
1285                    for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
1286                        mTaskStackListeners.get(i).onActivityForcedResizable(
1287                                (String) msg.obj, msg.arg1);
1288                    }
1289                    break;
1290                }
1291                case ON_ACTIVITY_DISMISSING_DOCKED_STACK: {
1292                    for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
1293                        mTaskStackListeners.get(i).onActivityDismissingDockedStack();
1294                    }
1295                    break;
1296                }
1297                case ON_TASK_PROFILE_LOCKED: {
1298                    for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
1299                        mTaskStackListeners.get(i).onTaskProfileLocked(msg.arg1, msg.arg2);
1300                    }
1301                    break;
1302                }
1303            }
1304        }
1305    }
1306}
1307