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