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