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