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