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