SystemServicesProxy.java revision 59924fe0d9136cf349759bea1e06b661603f95fe
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.HOME_STACK_ID; 22import static android.app.ActivityManager.StackId.PINNED_STACK_ID; 23import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT; 24 25import android.app.ActivityManager; 26import android.app.ActivityManagerNative; 27import android.app.ActivityOptions; 28import android.app.AppGlobals; 29import android.app.IActivityManager; 30import android.app.ITaskStackListener; 31import android.appwidget.AppWidgetHost; 32import android.appwidget.AppWidgetManager; 33import android.appwidget.AppWidgetProviderInfo; 34import android.content.ComponentName; 35import android.content.ContentResolver; 36import android.content.Context; 37import android.content.Intent; 38import android.content.pm.ActivityInfo; 39import android.content.pm.ApplicationInfo; 40import android.content.pm.IPackageManager; 41import android.content.pm.PackageManager; 42import android.content.pm.ResolveInfo; 43import android.content.res.Configuration; 44import android.content.res.Resources; 45import android.graphics.Bitmap; 46import android.graphics.BitmapFactory; 47import android.graphics.Canvas; 48import android.graphics.Color; 49import android.graphics.Paint; 50import android.graphics.Point; 51import android.graphics.PorterDuff; 52import android.graphics.PorterDuffXfermode; 53import android.graphics.Rect; 54import android.graphics.drawable.BitmapDrawable; 55import android.graphics.drawable.ColorDrawable; 56import android.graphics.drawable.Drawable; 57import android.os.Bundle; 58import android.os.Handler; 59import android.os.Looper; 60import android.os.Message; 61import android.os.ParcelFileDescriptor; 62import android.os.RemoteException; 63import android.os.SystemProperties; 64import android.os.UserHandle; 65import android.os.UserManager; 66import android.provider.Settings; 67import android.util.ArraySet; 68import android.util.Log; 69import android.util.MutableBoolean; 70import android.util.Pair; 71import android.view.Display; 72import android.view.IDockedStackListener; 73import android.view.Surface; 74import android.view.View; 75import android.view.WindowManager; 76import android.view.WindowManager.KeyboardShortcutsReceiver; 77import android.view.WindowManagerGlobal; 78import android.view.accessibility.AccessibilityManager; 79 80import com.android.internal.app.AssistUtils; 81import com.android.internal.os.BackgroundThread; 82import com.android.systemui.Prefs; 83import com.android.systemui.R; 84import com.android.systemui.recents.RecentsDebugFlags; 85import com.android.systemui.recents.RecentsImpl; 86import com.android.systemui.recents.tv.RecentsTvImpl; 87 88import java.io.IOException; 89import java.util.ArrayList; 90import java.util.Iterator; 91import java.util.List; 92import java.util.Random; 93 94/** 95 * Acts as a shim around the real system services that we need to access data from, and provides 96 * a point of injection when testing UI. 97 */ 98public class SystemServicesProxy { 99 final static String TAG = "SystemServicesProxy"; 100 101 final static BitmapFactory.Options sBitmapOptions; 102 static { 103 sBitmapOptions = new BitmapFactory.Options(); 104 sBitmapOptions.inMutable = true; 105 } 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 AppWidgetManager mAwm; 120 PackageManager mPm; 121 IPackageManager mIpm; 122 AssistUtils mAssistUtils; 123 WindowManager mWm; 124 UserManager mUm; 125 Display mDisplay; 126 String mRecentsPackage; 127 ComponentName mAssistComponent; 128 129 boolean mIsSafeMode; 130 boolean mHasFreeformWorkspaceSupport; 131 132 Bitmap mDummyIcon; 133 int mDummyThumbnailWidth; 134 int mDummyThumbnailHeight; 135 Paint mBgProtectionPaint; 136 Canvas mBgProtectionCanvas; 137 138 private final Handler mHandler = new H(); 139 140 /** 141 * An abstract class to track task stack changes. 142 * Classes should implement this instead of {@link android.app.ITaskStackListener} 143 * to reduce IPC calls from system services. These callbacks will be called on the main thread. 144 */ 145 public abstract static class TaskStackListener { 146 public void onTaskStackChanged() { } 147 public void onActivityPinned() { } 148 public void onPinnedActivityRestartAttempt() { } 149 public void onPinnedStackAnimationEnded() { } 150 } 151 152 /** 153 * Implementation of {@link android.app.ITaskStackListener} to listen task stack changes from 154 * ActivityManagerNative. 155 * This simply passes callbacks to listeners through {@link H}. 156 * */ 157 private ITaskStackListener.Stub mTaskStackListener = new ITaskStackListener.Stub() { 158 @Override 159 public void onTaskStackChanged() throws RemoteException { 160 mHandler.removeMessages(H.ON_TASK_STACK_CHANGED); 161 mHandler.sendEmptyMessage(H.ON_TASK_STACK_CHANGED); 162 } 163 164 @Override 165 public void onActivityPinned() throws RemoteException { 166 mHandler.removeMessages(H.ON_ACTIVITY_PINNED); 167 mHandler.sendEmptyMessage(H.ON_ACTIVITY_PINNED); 168 } 169 170 @Override 171 public void onPinnedActivityRestartAttempt() throws RemoteException{ 172 mHandler.removeMessages(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT); 173 mHandler.sendEmptyMessage(H.ON_PINNED_ACTIVITY_RESTART_ATTEMPT); 174 } 175 176 @Override 177 public void onPinnedStackAnimationEnded() throws RemoteException { 178 mHandler.removeMessages(H.ON_PINNED_STACK_ANIMATION_ENDED); 179 mHandler.sendEmptyMessage(H.ON_PINNED_STACK_ANIMATION_ENDED); 180 } 181 }; 182 183 /** 184 * List of {@link TaskStackListener} registered from {@link registerTaskStackListener}. 185 */ 186 private List<TaskStackListener> mTaskStackListeners = new ArrayList<>(); 187 188 /** Private constructor */ 189 private SystemServicesProxy(Context context) { 190 mAccm = AccessibilityManager.getInstance(context); 191 mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); 192 mIam = ActivityManagerNative.getDefault(); 193 mAwm = AppWidgetManager.getInstance(context); 194 mPm = context.getPackageManager(); 195 mIpm = AppGlobals.getPackageManager(); 196 mAssistUtils = new AssistUtils(context); 197 mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 198 mUm = UserManager.get(context); 199 mDisplay = mWm.getDefaultDisplay(); 200 mRecentsPackage = context.getPackageName(); 201 mHasFreeformWorkspaceSupport = 202 mPm.hasSystemFeature(PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT) || 203 Settings.Global.getInt(context.getContentResolver(), 204 DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0; 205 mIsSafeMode = mPm.isSafeMode(); 206 207 // Get the dummy thumbnail width/heights 208 Resources res = context.getResources(); 209 int wId = com.android.internal.R.dimen.thumbnail_width; 210 int hId = com.android.internal.R.dimen.thumbnail_height; 211 mDummyThumbnailWidth = res.getDimensionPixelSize(wId); 212 mDummyThumbnailHeight = res.getDimensionPixelSize(hId); 213 214 // Create the protection paints 215 mBgProtectionPaint = new Paint(); 216 mBgProtectionPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP)); 217 mBgProtectionPaint.setColor(0xFFffffff); 218 mBgProtectionCanvas = new Canvas(); 219 220 // Resolve the assist intent 221 mAssistComponent = mAssistUtils.getAssistComponentForUser(UserHandle.myUserId()); 222 223 if (RecentsDebugFlags.Static.EnableMockTasks) { 224 // Create a dummy icon 225 mDummyIcon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); 226 mDummyIcon.eraseColor(0xFF999999); 227 } 228 } 229 230 /** 231 * Returns the single instance of the {@link SystemServicesProxy}. 232 * This should only be called on the main thread. 233 */ 234 public static SystemServicesProxy getInstance(Context context) { 235 if (!Looper.getMainLooper().isCurrentThread()) { 236 throw new RuntimeException("Must be called on the UI thread"); 237 } 238 if (sSystemServicesProxy == null) { 239 sSystemServicesProxy = new SystemServicesProxy(context); 240 } 241 return sSystemServicesProxy; 242 } 243 244 /** Returns a list of the recents tasks */ 245 public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numLatestTasks, int userId, 246 boolean isTopTaskHome, ArraySet<Integer> quietProfileIds) { 247 if (mAm == null) return null; 248 249 // If we are mocking, then create some recent tasks 250 if (RecentsDebugFlags.Static.EnableMockTasks) { 251 ArrayList<ActivityManager.RecentTaskInfo> tasks = 252 new ArrayList<ActivityManager.RecentTaskInfo>(); 253 int count = Math.min(numLatestTasks, RecentsDebugFlags.Static.MockTaskCount); 254 for (int i = 0; i < count; i++) { 255 // Create a dummy component name 256 int packageIndex = i % RecentsDebugFlags.Static.MockTasksPackageCount; 257 ComponentName cn = new ComponentName("com.android.test" + packageIndex, 258 "com.android.test" + i + ".Activity"); 259 String description = "" + i + " - " + 260 Long.toString(Math.abs(new Random().nextLong()), 36); 261 // Create the recent task info 262 ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo(); 263 rti.id = rti.persistentId = rti.affiliatedTaskId = i; 264 rti.baseIntent = new Intent(); 265 rti.baseIntent.setComponent(cn); 266 rti.description = description; 267 rti.firstActiveTime = rti.lastActiveTime = i; 268 if (i % 2 == 0) { 269 rti.taskDescription = new ActivityManager.TaskDescription(description, 270 Bitmap.createBitmap(mDummyIcon), null, 271 0xFF000000 | (0xFFFFFF & new Random().nextInt()), 272 0xFF000000 | (0xFFFFFF & new Random().nextInt())); 273 } else { 274 rti.taskDescription = new ActivityManager.TaskDescription(); 275 } 276 tasks.add(rti); 277 } 278 return tasks; 279 } 280 281 // Remove home/recents/excluded tasks 282 int minNumTasksToQuery = 10; 283 int numTasksToQuery = Math.max(minNumTasksToQuery, numLatestTasks); 284 List<ActivityManager.RecentTaskInfo> tasks = mAm.getRecentTasksForUser(numTasksToQuery, 285 ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS | 286 ActivityManager.RECENT_INGORE_DOCKED_STACK_TASKS | 287 ActivityManager.RECENT_INGORE_PINNED_STACK_TASKS | 288 ActivityManager.RECENT_IGNORE_UNAVAILABLE | 289 ActivityManager.RECENT_INCLUDE_PROFILES | 290 ActivityManager.RECENT_WITH_EXCLUDED, userId); 291 292 // Break early if we can't get a valid set of tasks 293 if (tasks == null) { 294 return new ArrayList<>(); 295 } 296 297 boolean isFirstValidTask = true; 298 Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator(); 299 while (iter.hasNext()) { 300 ActivityManager.RecentTaskInfo t = iter.next(); 301 302 // NOTE: The order of these checks happens in the expected order of the traversal of the 303 // tasks 304 305 // Check the first non-recents task, include this task even if it is marked as excluded 306 // from recents if we are currently in the app. In other words, only remove excluded 307 // tasks if it is not the first active task, and not in the blacklist. 308 boolean isExcluded = (t.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) 309 == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; 310 boolean isBlackListed = sRecentsBlacklist.contains(t.realActivity.getClassName()); 311 // Filter out recent tasks from managed profiles which are in quiet mode. 312 isExcluded |= quietProfileIds.contains(t.userId); 313 if (isBlackListed || (isExcluded && (isTopTaskHome || !isFirstValidTask))) { 314 iter.remove(); 315 continue; 316 } 317 isFirstValidTask = false; 318 } 319 320 return tasks.subList(0, Math.min(tasks.size(), numLatestTasks)); 321 } 322 323 /** Returns a list of the running tasks */ 324 private List<ActivityManager.RunningTaskInfo> getRunningTasks(int numTasks) { 325 if (mAm == null) return null; 326 return mAm.getRunningTasks(numTasks); 327 } 328 329 /** Returns the top task. */ 330 public ActivityManager.RunningTaskInfo getTopMostTask() { 331 List<ActivityManager.RunningTaskInfo> tasks = getRunningTasks(1); 332 if (tasks != null && !tasks.isEmpty()) { 333 return tasks.get(0); 334 } 335 return null; 336 } 337 338 /** 339 * Returns whether this device has freeform workspaces. 340 */ 341 public boolean hasFreeformWorkspaceSupport() { 342 return mHasFreeformWorkspaceSupport; 343 } 344 345 /** 346 * Returns whether this device is in the safe mode. 347 */ 348 public boolean isInSafeMode() { 349 return mIsSafeMode; 350 } 351 352 /** Returns whether the recents is currently running */ 353 public boolean isRecentsTopMost(ActivityManager.RunningTaskInfo topTask, 354 MutableBoolean isHomeTopMost) { 355 if (topTask != null) { 356 ComponentName topActivity = topTask.topActivity; 357 358 // Check if the front most activity is recents 359 if ((topActivity.getPackageName().equals(RecentsImpl.RECENTS_PACKAGE) && 360 (topActivity.getClassName().equals(RecentsImpl.RECENTS_ACTIVITY) || 361 topActivity.getClassName().equals(RecentsTvImpl.RECENTS_TV_ACTIVITY)))) { 362 if (isHomeTopMost != null) { 363 isHomeTopMost.value = false; 364 } 365 return true; 366 } 367 368 // Note, this is only valid because we currently only allow the recents and home 369 // activities in the home stack 370 if (isHomeTopMost != null) { 371 isHomeTopMost.value = SystemServicesProxy.isHomeStack(topTask.stackId); 372 } 373 } 374 return false; 375 } 376 377 /** Get the bounds of a task. */ 378 public Rect getTaskBounds(int taskId) { 379 if (mIam == null) return null; 380 381 try { 382 return mIam.getTaskBounds(taskId); 383 } catch (RemoteException e) { 384 e.printStackTrace(); 385 } 386 return null; 387 } 388 389 /** 390 * Resizes the given task to the new bounds. 391 */ 392 public void resizeTask(int taskId, Rect bounds) { 393 if (mIam == null) return; 394 395 try { 396 mIam.resizeTask(taskId, bounds, ActivityManager.RESIZE_MODE_FORCED); 397 } catch (RemoteException e) { 398 e.printStackTrace(); 399 } 400 } 401 402 /** Docks a task to the side of the screen and starts it. */ 403 public void startTaskInDockedMode(Context context, View view, int taskId, int createMode) { 404 if (mIam == null) return; 405 406 try { 407 // TODO: Determine what animation we want for the incoming task 408 final ActivityOptions options = ActivityOptions.makeThumbnailAspectScaleUpAnimation( 409 view, null, 0, 0, view.getWidth(), view.getHeight(), null, null); 410 options.setDockCreateMode(createMode); 411 options.setLaunchStackId(DOCKED_STACK_ID); 412 mIam.startActivityFromRecents(taskId, options.toBundle()); 413 } catch (RemoteException e) { 414 e.printStackTrace(); 415 } 416 } 417 418 /** Docks an already resumed task to the side of the screen. */ 419 public boolean moveTaskToDockedStack(int taskId, int createMode, Rect initialBounds) { 420 if (mIam == null) { 421 return false; 422 } 423 424 try { 425 return mIam.moveTaskToDockedStack( 426 taskId, createMode, true /* onTop */, false /* animate */, initialBounds); 427 } catch (RemoteException e) { 428 e.printStackTrace(); 429 } 430 return false; 431 } 432 433 /** Returns the focused stack id. */ 434 public int getFocusedStack() { 435 if (mIam == null) return -1; 436 437 try { 438 return mIam.getFocusedStackId(); 439 } catch (RemoteException e) { 440 e.printStackTrace(); 441 return -1; 442 } 443 } 444 445 /** 446 * Returns whether the given stack id is the home stack id. 447 */ 448 public static boolean isHomeStack(int stackId) { 449 return stackId == HOME_STACK_ID; 450 } 451 452 /** 453 * Returns whether the given stack id is the pinned stack id. 454 */ 455 public static boolean isPinnedStack(int stackId){ 456 return stackId == PINNED_STACK_ID; 457 } 458 459 /** 460 * Returns whether the given stack id is the docked stack id. 461 */ 462 public static boolean isDockedStack(int stackId) { 463 return stackId == DOCKED_STACK_ID; 464 } 465 466 /** 467 * Returns whether the given stack id is the freeform workspace stack id. 468 */ 469 public static boolean isFreeformStack(int stackId) { 470 return stackId == FREEFORM_WORKSPACE_STACK_ID; 471 } 472 473 /** 474 * @return whether there are any docked tasks for the current user. 475 */ 476 public boolean hasDockedTask() { 477 if (mIam == null) return false; 478 479 ActivityManager.StackInfo stackInfo = null; 480 try { 481 stackInfo = mIam.getStackInfo(DOCKED_STACK_ID); 482 } catch (RemoteException e) { 483 e.printStackTrace(); 484 } 485 486 if (stackInfo != null) { 487 int userId = getCurrentUser(); 488 boolean hasUserTask = false; 489 for (int i = stackInfo.taskUserIds.length - 1; i >= 0 && !hasUserTask; i--) { 490 hasUserTask = (stackInfo.taskUserIds[i] == userId); 491 } 492 return hasUserTask; 493 } 494 return false; 495 } 496 497 /** 498 * Cancels the current window transtion to/from Recents for the given task id. 499 */ 500 public void cancelWindowTransition(int taskId) { 501 if (mWm == null) return; 502 503 try { 504 WindowManagerGlobal.getWindowManagerService().cancelTaskWindowTransition(taskId); 505 } catch (RemoteException e) { 506 e.printStackTrace(); 507 } 508 } 509 510 /** 511 * Cancels the current thumbnail transtion to/from Recents for the given task id. 512 */ 513 public void cancelThumbnailTransition(int taskId) { 514 if (mWm == null) return; 515 516 try { 517 WindowManagerGlobal.getWindowManagerService().cancelTaskThumbnailTransition(taskId); 518 } catch (RemoteException e) { 519 e.printStackTrace(); 520 } 521 } 522 523 /** Returns the top task thumbnail for the given task id */ 524 public Bitmap getTaskThumbnail(int taskId) { 525 if (mAm == null) return null; 526 527 // If we are mocking, then just return a dummy thumbnail 528 if (RecentsDebugFlags.Static.EnableMockTasks) { 529 Bitmap thumbnail = Bitmap.createBitmap(mDummyThumbnailWidth, mDummyThumbnailHeight, 530 Bitmap.Config.ARGB_8888); 531 thumbnail.eraseColor(0xff333333); 532 return thumbnail; 533 } 534 535 Bitmap thumbnail = getThumbnail(taskId); 536 if (thumbnail != null) { 537 thumbnail.setHasAlpha(false); 538 // We use a dumb heuristic for now, if the thumbnail is purely transparent in the top 539 // left pixel, then assume the whole thumbnail is transparent. Generally, proper 540 // screenshots are always composed onto a bitmap that has no alpha. 541 if (Color.alpha(thumbnail.getPixel(0, 0)) == 0) { 542 mBgProtectionCanvas.setBitmap(thumbnail); 543 mBgProtectionCanvas.drawRect(0, 0, thumbnail.getWidth(), thumbnail.getHeight(), 544 mBgProtectionPaint); 545 mBgProtectionCanvas.setBitmap(null); 546 Log.e(TAG, "Invalid screenshot detected from getTaskThumbnail()"); 547 } 548 } 549 return thumbnail; 550 } 551 552 /** 553 * Returns a task thumbnail from the activity manager 554 */ 555 public Bitmap getThumbnail(int taskId) { 556 if (mAm == null) { 557 return null; 558 } 559 560 ActivityManager.TaskThumbnail taskThumbnail = mAm.getTaskThumbnail(taskId); 561 if (taskThumbnail == null) return null; 562 563 Bitmap thumbnail = taskThumbnail.mainThumbnail; 564 ParcelFileDescriptor descriptor = taskThumbnail.thumbnailFileDescriptor; 565 if (thumbnail == null && descriptor != null) { 566 thumbnail = BitmapFactory.decodeFileDescriptor(descriptor.getFileDescriptor(), 567 null, sBitmapOptions); 568 } 569 if (descriptor != null) { 570 try { 571 descriptor.close(); 572 } catch (IOException e) { 573 } 574 } 575 return thumbnail; 576 } 577 578 /** 579 * Moves a task into another stack. 580 */ 581 public void moveTaskToStack(int taskId, int stackId) { 582 if (mIam == null) return; 583 584 try { 585 mIam.positionTaskInStack(taskId, stackId, 0); 586 } catch (RemoteException | IllegalArgumentException e) { 587 e.printStackTrace(); 588 } 589 } 590 591 /** Moves a task to the front with the specified activity options. */ 592 public void moveTaskToFront(int taskId, ActivityOptions opts) { 593 if (mAm == null) return; 594 if (RecentsDebugFlags.Static.EnableMockTasks) return; 595 596 if (opts != null) { 597 mAm.moveTaskToFront(taskId, ActivityManager.MOVE_TASK_WITH_HOME, 598 opts.toBundle()); 599 } else { 600 mAm.moveTaskToFront(taskId, ActivityManager.MOVE_TASK_WITH_HOME); 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 the current search widget id. 860 */ 861 public int getSearchAppWidgetId(Context context) { 862 return Prefs.getInt(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_ID, -1); 863 } 864 865 /** 866 * Returns the current search widget info, binding a new one if necessary. 867 */ 868 public AppWidgetProviderInfo getOrBindSearchAppWidget(Context context, AppWidgetHost host) { 869 int searchWidgetId = Prefs.getInt(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_ID, -1); 870 AppWidgetProviderInfo searchWidgetInfo = mAwm.getAppWidgetInfo(searchWidgetId); 871 AppWidgetProviderInfo resolvedSearchWidgetInfo = resolveSearchAppWidget(); 872 873 // Return the search widget info if it hasn't changed 874 if (searchWidgetInfo != null && resolvedSearchWidgetInfo != null && 875 searchWidgetInfo.provider.equals(resolvedSearchWidgetInfo.provider)) { 876 if (Prefs.getString(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE, null) == null) { 877 Prefs.putString(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE, 878 searchWidgetInfo.provider.getPackageName()); 879 } 880 return searchWidgetInfo; 881 } 882 883 // Delete the old widget 884 if (searchWidgetId != -1) { 885 host.deleteAppWidgetId(searchWidgetId); 886 } 887 888 // And rebind a new search widget 889 if (resolvedSearchWidgetInfo != null) { 890 Pair<Integer, AppWidgetProviderInfo> widgetInfo = bindSearchAppWidget(host, 891 resolvedSearchWidgetInfo); 892 if (widgetInfo != null) { 893 Prefs.putInt(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_ID, widgetInfo.first); 894 Prefs.putString(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE, 895 widgetInfo.second.provider.getPackageName()); 896 return widgetInfo.second; 897 } 898 } 899 900 // If we fall through here, then there is no resolved search widget, so clear the state 901 Prefs.remove(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_ID); 902 Prefs.remove(context, Prefs.Key.OVERVIEW_SEARCH_APP_WIDGET_PACKAGE); 903 return null; 904 } 905 906 /** 907 * Returns the first Recents widget from the same package as the global assist activity. 908 */ 909 public AppWidgetProviderInfo resolveSearchAppWidget() { 910 if (mAssistComponent == null) return null; 911 List<AppWidgetProviderInfo> widgets = mAwm.getInstalledProviders( 912 AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX); 913 for (AppWidgetProviderInfo info : widgets) { 914 if (info.provider.getPackageName().equals(mAssistComponent.getPackageName())) { 915 return info; 916 } 917 } 918 return null; 919 } 920 921 /** 922 * Resolves and binds the search app widget that is to appear in the recents. 923 */ 924 private Pair<Integer, AppWidgetProviderInfo> bindSearchAppWidget(AppWidgetHost host, 925 AppWidgetProviderInfo resolvedSearchWidgetInfo) { 926 if (mAwm == null) return null; 927 if (mAssistComponent == null) return null; 928 929 // Allocate a new widget id and try and bind the app widget (if that fails, then just skip) 930 int searchWidgetId = host.allocateAppWidgetId(); 931 Bundle opts = new Bundle(); 932 opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, 933 AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX); 934 if (!mAwm.bindAppWidgetIdIfAllowed(searchWidgetId, resolvedSearchWidgetInfo.provider, opts)) { 935 host.deleteAppWidgetId(searchWidgetId); 936 return null; 937 } 938 return new Pair<>(searchWidgetId, resolvedSearchWidgetInfo); 939 } 940 941 /** 942 * Returns whether touch exploration is currently enabled. 943 */ 944 public boolean isTouchExplorationEnabled() { 945 if (mAccm == null) return false; 946 947 return mAccm.isEnabled() && mAccm.isTouchExplorationEnabled(); 948 } 949 950 /** 951 * Returns whether the current task is in screen-pinning mode. 952 */ 953 public boolean isScreenPinningActive() { 954 if (mIam == null) return false; 955 956 try { 957 return mIam.isInLockTaskMode(); 958 } catch (RemoteException e) { 959 return false; 960 } 961 } 962 963 /** 964 * Returns a global setting. 965 */ 966 public int getGlobalSetting(Context context, String setting) { 967 ContentResolver cr = context.getContentResolver(); 968 return Settings.Global.getInt(cr, setting, 0); 969 } 970 971 /** 972 * Returns a system setting. 973 */ 974 public int getSystemSetting(Context context, String setting) { 975 ContentResolver cr = context.getContentResolver(); 976 return Settings.System.getInt(cr, setting, 0); 977 } 978 979 /** 980 * Returns a system property. 981 */ 982 public String getSystemProperty(String key) { 983 return SystemProperties.get(key); 984 } 985 986 /** 987 * Returns the smallest width/height. 988 */ 989 public int getDeviceSmallestWidth() { 990 if (mDisplay == null) return 0; 991 992 Point smallestSizeRange = new Point(); 993 Point largestSizeRange = new Point(); 994 mDisplay.getCurrentSizeRange(smallestSizeRange, largestSizeRange); 995 return smallestSizeRange.x; 996 } 997 998 /** 999 * Returns the current display rect in the current display orientation. 1000 */ 1001 public Rect getDisplayRect() { 1002 Rect displayRect = new Rect(); 1003 if (mDisplay == null) return displayRect; 1004 1005 Point p = new Point(); 1006 mDisplay.getRealSize(p); 1007 displayRect.set(0, 0, p.x, p.y); 1008 return displayRect; 1009 } 1010 1011 /** 1012 * Returns the current display orientation. 1013 */ 1014 public int getDisplayOrientation() { 1015 // Because of multi-window, the configuration orientation does not necessarily reflect the 1016 // orientation of the display, instead we just use the display's real-size. 1017 Rect displayRect = getDisplayRect(); 1018 return displayRect.width() > displayRect.height() 1019 ? Configuration.ORIENTATION_LANDSCAPE 1020 : Configuration.ORIENTATION_PORTRAIT; 1021 } 1022 1023 /** 1024 * Returns the window rect for the RecentsActivity, based on the dimensions of the home stack. 1025 */ 1026 public Rect getWindowRect() { 1027 Rect windowRect = new Rect(); 1028 if (mIam == null) return windowRect; 1029 1030 try { 1031 // Use the home stack bounds 1032 ActivityManager.StackInfo stackInfo = mIam.getStackInfo(HOME_STACK_ID); 1033 if (stackInfo != null) { 1034 windowRect.set(stackInfo.bounds); 1035 } 1036 } catch (RemoteException e) { 1037 e.printStackTrace(); 1038 } finally { 1039 return windowRect; 1040 } 1041 } 1042 1043 /** Starts an activity from recents. */ 1044 public boolean startActivityFromRecents(Context context, int taskId, String taskName, 1045 ActivityOptions options) { 1046 if (mIam != null) { 1047 try { 1048 mIam.startActivityFromRecents(taskId, options == null ? null : options.toBundle()); 1049 return true; 1050 } catch (Exception e) { 1051 Log.e(TAG, context.getString(R.string.recents_launch_error_message, taskName), e); 1052 } 1053 } 1054 return false; 1055 } 1056 1057 /** Starts an in-place animation on the front most application windows. */ 1058 public void startInPlaceAnimationOnFrontMostApplication(ActivityOptions opts) { 1059 if (mIam == null) return; 1060 1061 try { 1062 mIam.startInPlaceAnimationOnFrontMostApplication(opts); 1063 } catch (Exception e) { 1064 e.printStackTrace(); 1065 } 1066 } 1067 1068 /** 1069 * Registers a task stack listener with the system. 1070 * This should be called on the main thread. 1071 */ 1072 public void registerTaskStackListener(TaskStackListener listener) { 1073 if (mIam == null) return; 1074 1075 mTaskStackListeners.add(listener); 1076 if (mTaskStackListeners.size() == 1) { 1077 // Register mTaskStackListener to IActivityManager only once if needed. 1078 try { 1079 mIam.registerTaskStackListener(mTaskStackListener); 1080 } catch (Exception e) { 1081 Log.w(TAG, "Failed to call registerTaskStackListener", e); 1082 } 1083 } 1084 } 1085 1086 public void endProlongedAnimations() { 1087 if (mWm == null) { 1088 return; 1089 } 1090 try { 1091 WindowManagerGlobal.getWindowManagerService().endProlongedAnimations(); 1092 } catch (Exception e) { 1093 e.printStackTrace(); 1094 } 1095 } 1096 1097 public void registerDockedStackListener(IDockedStackListener listener) { 1098 if (mWm == null) return; 1099 1100 try { 1101 WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(listener); 1102 } catch (Exception e) { 1103 e.printStackTrace(); 1104 } 1105 } 1106 1107 /** 1108 * Calculates the size of the dock divider in the current orientation. 1109 */ 1110 public int getDockedDividerSize(Context context) { 1111 Resources res = context.getResources(); 1112 int dividerWindowWidth = res.getDimensionPixelSize( 1113 com.android.internal.R.dimen.docked_stack_divider_thickness); 1114 int dividerInsets = res.getDimensionPixelSize( 1115 com.android.internal.R.dimen.docked_stack_divider_insets); 1116 return dividerWindowWidth - 2 * dividerInsets; 1117 } 1118 1119 public void requestKeyboardShortcuts(Context context, KeyboardShortcutsReceiver receiver) { 1120 mWm.requestAppKeyboardShortcuts(receiver); 1121 } 1122 1123 public void getStableInsets(Rect outStableInsets) { 1124 if (mWm == null) return; 1125 1126 try { 1127 WindowManagerGlobal.getWindowManagerService().getStableInsets(outStableInsets); 1128 } catch (Exception e) { 1129 e.printStackTrace(); 1130 } 1131 } 1132 1133 private final class H extends Handler { 1134 private static final int ON_TASK_STACK_CHANGED = 1; 1135 private static final int ON_ACTIVITY_PINNED = 2; 1136 private static final int ON_PINNED_ACTIVITY_RESTART_ATTEMPT = 3; 1137 private static final int ON_PINNED_STACK_ANIMATION_ENDED = 4; 1138 1139 @Override 1140 public void handleMessage(Message msg) { 1141 switch (msg.what) { 1142 case ON_TASK_STACK_CHANGED: { 1143 for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { 1144 mTaskStackListeners.get(i).onTaskStackChanged(); 1145 } 1146 break; 1147 } 1148 case ON_ACTIVITY_PINNED: { 1149 for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { 1150 mTaskStackListeners.get(i).onActivityPinned(); 1151 } 1152 break; 1153 } 1154 case ON_PINNED_ACTIVITY_RESTART_ATTEMPT: { 1155 for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { 1156 mTaskStackListeners.get(i).onPinnedActivityRestartAttempt(); 1157 } 1158 break; 1159 } 1160 case ON_PINNED_STACK_ANIMATION_ENDED: { 1161 for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { 1162 mTaskStackListeners.get(i).onPinnedStackAnimationEnded(); 1163 } 1164 break; 1165 } 1166 } 1167 } 1168 } 1169} 1170