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