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