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