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