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