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