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