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