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