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