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