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