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