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