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