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