1 /* 2 * Copyright (C) 2020 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 17 package com.android.server.wm; 18 19 import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS; 20 import static android.app.ActivityTaskManager.INVALID_TASK_ID; 21 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; 22 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 23 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; 24 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; 25 import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_APPLICATION; 26 import static android.view.Display.DEFAULT_DISPLAY; 27 import static android.view.Display.INVALID_DISPLAY; 28 29 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; 30 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IMMERSIVE; 31 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL; 32 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH; 33 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; 34 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; 35 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE; 36 import static com.android.server.wm.ActivityTaskManagerService.TAG_SWITCH; 37 import static com.android.server.wm.ActivityTaskManagerService.enforceNotIsolatedCaller; 38 import static com.android.server.wm.Task.ActivityState.DESTROYED; 39 import static com.android.server.wm.Task.ActivityState.DESTROYING; 40 41 import android.annotation.NonNull; 42 import android.app.Activity; 43 import android.app.ActivityManager; 44 import android.app.ActivityTaskManager; 45 import android.app.IActivityClientController; 46 import android.app.IRequestFinishCallback; 47 import android.app.PictureInPictureParams; 48 import android.app.PictureInPictureUiState; 49 import android.app.servertransaction.ClientTransaction; 50 import android.app.servertransaction.EnterPipRequestedItem; 51 import android.app.servertransaction.PipStateTransactionItem; 52 import android.content.ComponentName; 53 import android.content.Context; 54 import android.content.Intent; 55 import android.content.pm.ActivityInfo; 56 import android.content.pm.ParceledListSlice; 57 import android.content.pm.ResolveInfo; 58 import android.content.res.Configuration; 59 import android.os.Binder; 60 import android.os.Bundle; 61 import android.os.IBinder; 62 import android.os.Parcel; 63 import android.os.PersistableBundle; 64 import android.os.RemoteException; 65 import android.os.SystemClock; 66 import android.os.Trace; 67 import android.service.voice.VoiceInteractionManagerInternal; 68 import android.util.Slog; 69 import android.view.RemoteAnimationDefinition; 70 import android.window.SizeConfigurationBuckets; 71 72 import com.android.internal.app.AssistUtils; 73 import com.android.internal.policy.IKeyguardDismissCallback; 74 import com.android.internal.protolog.common.ProtoLog; 75 import com.android.server.LocalServices; 76 import com.android.server.Watchdog; 77 import com.android.server.uri.NeededUriGrants; 78 import com.android.server.vr.VrManagerInternal; 79 80 /** 81 * Server side implementation for the client activity to interact with system. 82 * 83 * @see android.app.ActivityClient 84 */ 85 class ActivityClientController extends IActivityClientController.Stub { 86 private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityClientController" : TAG_ATM; 87 88 private final ActivityTaskManagerService mService; 89 private final WindowManagerGlobalLock mGlobalLock; 90 private final ActivityTaskSupervisor mTaskSupervisor; 91 private final Context mContext; 92 93 /** Wrapper around VoiceInteractionServiceManager. */ 94 private AssistUtils mAssistUtils; 95 ActivityClientController(ActivityTaskManagerService service)96 ActivityClientController(ActivityTaskManagerService service) { 97 mService = service; 98 mGlobalLock = service.mGlobalLock; 99 mTaskSupervisor = service.mTaskSupervisor; 100 mContext = service.mContext; 101 } 102 onSystemReady()103 void onSystemReady() { 104 mAssistUtils = new AssistUtils(mContext); 105 } 106 107 @Override onTransact(int code, Parcel data, Parcel reply, int flags)108 public boolean onTransact(int code, Parcel data, Parcel reply, int flags) 109 throws RemoteException { 110 try { 111 return super.onTransact(code, data, reply, flags); 112 } catch (RuntimeException e) { 113 throw ActivityTaskManagerService.logAndRethrowRuntimeExceptionOnTransact( 114 "ActivityClientController", e); 115 } 116 } 117 118 @Override activityIdle(IBinder token, Configuration config, boolean stopProfiling)119 public void activityIdle(IBinder token, Configuration config, boolean stopProfiling) { 120 final long origId = Binder.clearCallingIdentity(); 121 try { 122 synchronized (mGlobalLock) { 123 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityIdle"); 124 final ActivityRecord r = ActivityRecord.forTokenLocked(token); 125 if (r == null) { 126 return; 127 } 128 mTaskSupervisor.activityIdleInternal(r, false /* fromTimeout */, 129 false /* processPausingActivities */, config); 130 if (stopProfiling && r.hasProcess()) { 131 r.app.clearProfilerIfNeeded(); 132 } 133 } 134 } finally { 135 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 136 Binder.restoreCallingIdentity(origId); 137 } 138 } 139 140 @Override activityResumed(IBinder token, boolean handleSplashScreenExit)141 public void activityResumed(IBinder token, boolean handleSplashScreenExit) { 142 final long origId = Binder.clearCallingIdentity(); 143 synchronized (mGlobalLock) { 144 ActivityRecord.activityResumedLocked(token, handleSplashScreenExit); 145 } 146 Binder.restoreCallingIdentity(origId); 147 } 148 149 @Override activityTopResumedStateLost()150 public void activityTopResumedStateLost() { 151 final long origId = Binder.clearCallingIdentity(); 152 synchronized (mGlobalLock) { 153 mTaskSupervisor.handleTopResumedStateReleased(false /* timeout */); 154 } 155 Binder.restoreCallingIdentity(origId); 156 } 157 158 @Override activityPaused(IBinder token)159 public void activityPaused(IBinder token) { 160 final long origId = Binder.clearCallingIdentity(); 161 synchronized (mGlobalLock) { 162 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityPaused"); 163 final ActivityRecord r = ActivityRecord.forTokenLocked(token); 164 if (r != null) { 165 r.activityPaused(false); 166 } 167 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 168 } 169 Binder.restoreCallingIdentity(origId); 170 } 171 172 @Override activityStopped(IBinder token, Bundle icicle, PersistableBundle persistentState, CharSequence description)173 public void activityStopped(IBinder token, Bundle icicle, PersistableBundle persistentState, 174 CharSequence description) { 175 if (DEBUG_ALL) Slog.v(TAG, "Activity stopped: token=" + token); 176 177 // Refuse possible leaked file descriptors. 178 if (icicle != null && icicle.hasFileDescriptors()) { 179 throw new IllegalArgumentException("File descriptors passed in Bundle"); 180 } 181 182 final long origId = Binder.clearCallingIdentity(); 183 184 String restartingName = null; 185 int restartingUid = 0; 186 final ActivityRecord r; 187 synchronized (mGlobalLock) { 188 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityStopped"); 189 r = ActivityRecord.isInRootTaskLocked(token); 190 if (r != null) { 191 if (r.attachedToProcess() && r.isState(Task.ActivityState.RESTARTING_PROCESS)) { 192 // The activity was requested to restart from 193 // {@link #restartActivityProcessIfVisible}. 194 restartingName = r.app.mName; 195 restartingUid = r.app.mUid; 196 } 197 r.activityStopped(icicle, persistentState, description); 198 } 199 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 200 } 201 202 if (restartingName != null) { 203 // In order to let the foreground activity can be restarted with its saved state from 204 // {@link android.app.Activity#onSaveInstanceState}, the kill operation is postponed 205 // until the activity reports stopped with the state. And the activity record will be 206 // kept because the record state is restarting, then the activity will be restarted 207 // immediately if it is still the top one. 208 mTaskSupervisor.removeRestartTimeouts(r); 209 mService.mAmInternal.killProcess(restartingName, restartingUid, 210 "restartActivityProcess"); 211 } 212 mService.mAmInternal.trimApplications(); 213 214 Binder.restoreCallingIdentity(origId); 215 } 216 217 @Override activityDestroyed(IBinder token)218 public void activityDestroyed(IBinder token) { 219 if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "ACTIVITY DESTROYED: " + token); 220 final long origId = Binder.clearCallingIdentity(); 221 synchronized (mGlobalLock) { 222 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityDestroyed"); 223 try { 224 final ActivityRecord r = ActivityRecord.forTokenLocked(token); 225 if (r != null) { 226 r.destroyed("activityDestroyed"); 227 } 228 } finally { 229 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 230 Binder.restoreCallingIdentity(origId); 231 } 232 } 233 } 234 235 @Override activityRelaunched(IBinder token)236 public void activityRelaunched(IBinder token) { 237 final long origId = Binder.clearCallingIdentity(); 238 synchronized (mGlobalLock) { 239 final ActivityRecord r = ActivityRecord.forTokenLocked(token); 240 if (r != null) { 241 r.finishRelaunching(); 242 } 243 } 244 Binder.restoreCallingIdentity(origId); 245 } 246 247 @Override reportSizeConfigurations(IBinder token, SizeConfigurationBuckets sizeConfigurations)248 public void reportSizeConfigurations(IBinder token, 249 SizeConfigurationBuckets sizeConfigurations) { 250 ProtoLog.v(WM_DEBUG_CONFIGURATION, "Report configuration: %s %s", 251 token, sizeConfigurations); 252 synchronized (mGlobalLock) { 253 final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); 254 if (r != null) { 255 r.setSizeConfigurations(sizeConfigurations); 256 } 257 } 258 } 259 260 /** 261 * Attempts to move a task backwards in z-order (the order of activities within the task is 262 * unchanged). 263 * 264 * There are several possible results of this call: 265 * - if the task is locked, then we will show the lock toast. 266 * - if there is a task behind the provided task, then that task is made visible and resumed as 267 * this task is moved to the back. 268 * - otherwise, if there are no other tasks in the root task: 269 * - if this task is in the pinned mode, then we remove the task completely, which will 270 * have the effect of moving the task to the top or bottom of the fullscreen root task 271 * (depending on whether it is visible). 272 * - otherwise, we simply return home and hide this task. 273 * 274 * @param token A reference to the activity we wish to move. 275 * @param nonRoot If false then this only works if the activity is the root 276 * of a task; if true it will work for any activity in a task. 277 * @return Returns true if the move completed, false if not. 278 */ 279 @Override moveActivityTaskToBack(IBinder token, boolean nonRoot)280 public boolean moveActivityTaskToBack(IBinder token, boolean nonRoot) { 281 enforceNotIsolatedCaller("moveActivityTaskToBack"); 282 final long origId = Binder.clearCallingIdentity(); 283 try { 284 synchronized (mGlobalLock) { 285 final int taskId = ActivityRecord.getTaskForActivityLocked(token, !nonRoot); 286 final Task task = mService.mRootWindowContainer.anyTaskForId(taskId); 287 if (task != null) { 288 return ActivityRecord.getRootTask(token).moveTaskToBack(task); 289 } 290 } 291 } finally { 292 Binder.restoreCallingIdentity(origId); 293 } 294 return false; 295 } 296 297 @Override shouldUpRecreateTask(IBinder token, String destAffinity)298 public boolean shouldUpRecreateTask(IBinder token, String destAffinity) { 299 synchronized (mGlobalLock) { 300 final ActivityRecord srec = ActivityRecord.forTokenLocked(token); 301 if (srec != null) { 302 return srec.getRootTask().shouldUpRecreateTaskLocked(srec, destAffinity); 303 } 304 } 305 return false; 306 } 307 308 @Override navigateUpTo(IBinder token, Intent destIntent, int resultCode, Intent resultData)309 public boolean navigateUpTo(IBinder token, Intent destIntent, int resultCode, 310 Intent resultData) { 311 final ActivityRecord r; 312 synchronized (mGlobalLock) { 313 r = ActivityRecord.isInRootTaskLocked(token); 314 if (r == null) { 315 return false; 316 } 317 } 318 319 // Carefully collect grants without holding lock. 320 final NeededUriGrants destGrants = mService.collectGrants(destIntent, r); 321 final NeededUriGrants resultGrants = mService.collectGrants(resultData, r.resultTo); 322 323 synchronized (mGlobalLock) { 324 return r.getRootTask().navigateUpTo( 325 r, destIntent, destGrants, resultCode, resultData, resultGrants); 326 } 327 } 328 329 @Override releaseActivityInstance(IBinder token)330 public boolean releaseActivityInstance(IBinder token) { 331 final long origId = Binder.clearCallingIdentity(); 332 try { 333 synchronized (mGlobalLock) { 334 final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); 335 if (r == null || !r.isDestroyable()) { 336 return false; 337 } 338 r.destroyImmediately("app-req"); 339 return r.isState(DESTROYING, DESTROYED); 340 } 341 } finally { 342 Binder.restoreCallingIdentity(origId); 343 } 344 } 345 346 /** 347 * This is the internal entry point for handling Activity.finish(). 348 * 349 * @param token The Binder token referencing the Activity we want to finish. 350 * @param resultCode Result code, if any, from this Activity. 351 * @param resultData Result data (Intent), if any, from this Activity. 352 * @param finishTask Whether to finish the task associated with this Activity. 353 * @return Returns true if the activity successfully finished, or false if it is still running. 354 */ 355 @Override finishActivity(IBinder token, int resultCode, Intent resultData, int finishTask)356 public boolean finishActivity(IBinder token, int resultCode, Intent resultData, 357 int finishTask) { 358 // Refuse possible leaked file descriptors. 359 if (resultData != null && resultData.hasFileDescriptors()) { 360 throw new IllegalArgumentException("File descriptors passed in Intent"); 361 } 362 363 final ActivityRecord r; 364 synchronized (mGlobalLock) { 365 r = ActivityRecord.isInRootTaskLocked(token); 366 if (r == null) { 367 return true; 368 } 369 } 370 371 // Carefully collect grants without holding lock. 372 final NeededUriGrants resultGrants = mService.collectGrants(resultData, r.resultTo); 373 374 synchronized (mGlobalLock) { 375 // Check again in case activity was removed when collecting grants. 376 if (!r.isInHistory()) { 377 return true; 378 } 379 380 // Keep track of the root activity of the task before we finish it. 381 final Task tr = r.getTask(); 382 final ActivityRecord rootR = tr.getRootActivity(); 383 if (rootR == null) { 384 Slog.w(TAG, "Finishing task with all activities already finished"); 385 } 386 // Do not allow task to finish if last task in lockTask mode. Launchable priv-apps can 387 // finish. 388 if (mService.getLockTaskController().activityBlockedFromFinish(r)) { 389 return false; 390 } 391 392 // TODO: There is a dup. of this block of code in ActivityStack.navigateUpToLocked 393 // We should consolidate. 394 if (mService.mController != null) { 395 // Find the first activity that is not finishing. 396 final ActivityRecord next = 397 r.getRootTask().topRunningActivity(token, INVALID_TASK_ID); 398 if (next != null) { 399 // ask watcher if this is allowed 400 boolean resumeOK = true; 401 try { 402 resumeOK = mService.mController.activityResuming(next.packageName); 403 } catch (RemoteException e) { 404 mService.mController = null; 405 Watchdog.getInstance().setActivityController(null); 406 } 407 408 if (!resumeOK) { 409 Slog.i(TAG, "Not finishing activity because controller resumed"); 410 return false; 411 } 412 } 413 } 414 415 // Note down that the process has finished an activity and is in background activity 416 // starts grace period. 417 if (r.app != null) { 418 r.app.setLastActivityFinishTimeIfNeeded(SystemClock.uptimeMillis()); 419 } 420 421 final long origId = Binder.clearCallingIdentity(); 422 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "finishActivity"); 423 try { 424 final boolean res; 425 final boolean finishWithRootActivity = 426 finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY; 427 if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY 428 || (finishWithRootActivity && r == rootR)) { 429 // If requested, remove the task that is associated to this activity only if it 430 // was the root activity in the task. The result code and data is ignored 431 // because we don't support returning them across task boundaries. Also, to 432 // keep backwards compatibility we remove the task from recents when finishing 433 // task with root activity. 434 mTaskSupervisor.removeTask(tr, false /*killProcess*/, 435 finishWithRootActivity, "finish-activity"); 436 res = true; 437 // Explicitly dismissing the activity so reset its relaunch flag. 438 r.mRelaunchReason = RELAUNCH_REASON_NONE; 439 } else { 440 r.finishIfPossible(resultCode, resultData, resultGrants, 441 "app-request", true /* oomAdj */); 442 res = r.finishing; 443 if (!res) { 444 Slog.i(TAG, "Failed to finish by app-request"); 445 } 446 } 447 return res; 448 } finally { 449 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 450 Binder.restoreCallingIdentity(origId); 451 } 452 } 453 } 454 455 @Override finishActivityAffinity(IBinder token)456 public boolean finishActivityAffinity(IBinder token) { 457 final long origId = Binder.clearCallingIdentity(); 458 try { 459 synchronized (mGlobalLock) { 460 final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); 461 if (r == null) { 462 return false; 463 } 464 465 // Do not allow task to finish if last task in lockTask mode. Launchable priv-apps 466 // can finish. 467 if (mService.getLockTaskController().activityBlockedFromFinish(r)) { 468 return false; 469 } 470 471 r.getTask().forAllActivities(activity -> r.finishIfSameAffinity(activity), 472 r /* boundary */, true /* includeBoundary */, 473 true /* traverseTopToBottom */); 474 return true; 475 } 476 } finally { 477 Binder.restoreCallingIdentity(origId); 478 } 479 } 480 481 @Override finishSubActivity(IBinder token, String resultWho, int requestCode)482 public void finishSubActivity(IBinder token, String resultWho, int requestCode) { 483 final long origId = Binder.clearCallingIdentity(); 484 try { 485 synchronized (mGlobalLock) { 486 final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); 487 if (r == null) return; 488 489 // TODO: This should probably only loop over the task since you need to be in the 490 // same task to return results. 491 r.getRootTask().forAllActivities(activity -> { 492 activity.finishIfSubActivity(r /* parent */, resultWho, requestCode); 493 }, true /* traverseTopToBottom */); 494 495 mService.updateOomAdj(); 496 } 497 } finally { 498 Binder.restoreCallingIdentity(origId); 499 } 500 } 501 502 @Override isTopOfTask(IBinder token)503 public boolean isTopOfTask(IBinder token) { 504 synchronized (mGlobalLock) { 505 final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); 506 return r != null && r.getTask().getTopNonFinishingActivity() == r; 507 } 508 } 509 510 @Override willActivityBeVisible(IBinder token)511 public boolean willActivityBeVisible(IBinder token) { 512 synchronized (mGlobalLock) { 513 final Task rootTask = ActivityRecord.getRootTask(token); 514 return rootTask != null && rootTask.willActivityBeVisible(token); 515 } 516 } 517 518 @Override getDisplayId(IBinder activityToken)519 public int getDisplayId(IBinder activityToken) { 520 synchronized (mGlobalLock) { 521 final Task rootTask = ActivityRecord.getRootTask(activityToken); 522 if (rootTask != null) { 523 final int displayId = rootTask.getDisplayId(); 524 return displayId != INVALID_DISPLAY ? displayId : DEFAULT_DISPLAY; 525 } 526 return DEFAULT_DISPLAY; 527 } 528 } 529 530 @Override getTaskForActivity(IBinder token, boolean onlyRoot)531 public int getTaskForActivity(IBinder token, boolean onlyRoot) { 532 synchronized (mGlobalLock) { 533 return ActivityRecord.getTaskForActivityLocked(token, onlyRoot); 534 } 535 } 536 537 @Override getCallingActivity(IBinder token)538 public ComponentName getCallingActivity(IBinder token) { 539 synchronized (mGlobalLock) { 540 final ActivityRecord r = getCallingRecord(token); 541 return r != null ? r.intent.getComponent() : null; 542 } 543 } 544 545 @Override getCallingPackage(IBinder token)546 public String getCallingPackage(IBinder token) { 547 synchronized (mGlobalLock) { 548 final ActivityRecord r = getCallingRecord(token); 549 return r != null ? r.info.packageName : null; 550 } 551 } 552 getCallingRecord(IBinder token)553 private static ActivityRecord getCallingRecord(IBinder token) { 554 final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); 555 return r != null ? r.resultTo : null; 556 } 557 558 @Override getLaunchedFromUid(IBinder token)559 public int getLaunchedFromUid(IBinder token) { 560 synchronized (mGlobalLock) { 561 final ActivityRecord r = ActivityRecord.forTokenLocked(token); 562 return r != null ? r.launchedFromUid : android.os.Process.INVALID_UID; 563 } 564 } 565 566 @Override getLaunchedFromPackage(IBinder token)567 public String getLaunchedFromPackage(IBinder token) { 568 synchronized (mGlobalLock) { 569 final ActivityRecord r = ActivityRecord.forTokenLocked(token); 570 return r != null ? r.launchedFromPackage : null; 571 } 572 } 573 574 @Override setRequestedOrientation(IBinder token, int requestedOrientation)575 public void setRequestedOrientation(IBinder token, int requestedOrientation) { 576 final long origId = Binder.clearCallingIdentity(); 577 try { 578 synchronized (mGlobalLock) { 579 final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); 580 if (r != null) { 581 r.setRequestedOrientation(requestedOrientation); 582 } 583 } 584 } finally { 585 Binder.restoreCallingIdentity(origId); 586 } 587 } 588 589 @Override getRequestedOrientation(IBinder token)590 public int getRequestedOrientation(IBinder token) { 591 synchronized (mGlobalLock) { 592 final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); 593 return r != null 594 ? r.getRequestedOrientation() : ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; 595 } 596 } 597 598 @Override convertFromTranslucent(IBinder token)599 public boolean convertFromTranslucent(IBinder token) { 600 final long origId = Binder.clearCallingIdentity(); 601 try { 602 synchronized (mGlobalLock) { 603 final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); 604 return r != null && r.setOccludesParent(true); 605 } 606 } finally { 607 Binder.restoreCallingIdentity(origId); 608 } 609 } 610 611 @Override convertToTranslucent(IBinder token, Bundle options)612 public boolean convertToTranslucent(IBinder token, Bundle options) { 613 final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(options); 614 final long origId = Binder.clearCallingIdentity(); 615 try { 616 synchronized (mGlobalLock) { 617 final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); 618 if (r == null) { 619 return false; 620 } 621 final ActivityRecord under = r.getTask().getActivityBelow(r); 622 if (under != null) { 623 under.returningOptions = safeOptions != null ? safeOptions.getOptions(r) : null; 624 } 625 return r.setOccludesParent(false); 626 } 627 } finally { 628 Binder.restoreCallingIdentity(origId); 629 } 630 } 631 632 @Override isImmersive(IBinder token)633 public boolean isImmersive(IBinder token) { 634 synchronized (mGlobalLock) { 635 final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); 636 if (r == null) { 637 throw new IllegalArgumentException(); 638 } 639 return r.immersive; 640 } 641 } 642 643 @Override setImmersive(IBinder token, boolean immersive)644 public void setImmersive(IBinder token, boolean immersive) { 645 synchronized (mGlobalLock) { 646 final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); 647 if (r == null) { 648 throw new IllegalArgumentException(); 649 } 650 r.immersive = immersive; 651 652 // Update associated state if we're frontmost. 653 if (r.isFocusedActivityOnDisplay()) { 654 ProtoLog.d(WM_DEBUG_IMMERSIVE, "Frontmost changed immersion: %s", r); 655 mService.applyUpdateLockStateLocked(r); 656 } 657 } 658 } 659 660 @Override enterPictureInPictureMode(IBinder token, final PictureInPictureParams params)661 public boolean enterPictureInPictureMode(IBinder token, final PictureInPictureParams params) { 662 final long origId = Binder.clearCallingIdentity(); 663 try { 664 synchronized (mGlobalLock) { 665 final ActivityRecord r = ensureValidPictureInPictureActivityParams( 666 "enterPictureInPictureMode", token, params); 667 return mService.enterPictureInPictureMode(r, params); 668 } 669 } finally { 670 Binder.restoreCallingIdentity(origId); 671 } 672 } 673 674 @Override setPictureInPictureParams(IBinder token, final PictureInPictureParams params)675 public void setPictureInPictureParams(IBinder token, final PictureInPictureParams params) { 676 final long origId = Binder.clearCallingIdentity(); 677 try { 678 synchronized (mGlobalLock) { 679 final ActivityRecord r = ensureValidPictureInPictureActivityParams( 680 "setPictureInPictureParams", token, params); 681 682 // Only update the saved args from the args that are set. 683 r.setPictureInPictureParams(params); 684 if (r.inPinnedWindowingMode()) { 685 // If the activity is already in picture-in-picture, update the pinned task now 686 // if it is not already expanding to fullscreen. Otherwise, the arguments will 687 // be used the next time the activity enters PiP. 688 final Task rootTask = r.getRootTask(); 689 rootTask.setPictureInPictureAspectRatio( 690 r.pictureInPictureArgs.getAspectRatio()); 691 rootTask.setPictureInPictureActions(r.pictureInPictureArgs.getActions()); 692 } 693 } 694 } finally { 695 Binder.restoreCallingIdentity(origId); 696 } 697 } 698 699 /** 700 * Splash screen view is attached to activity. 701 */ 702 @Override splashScreenAttached(IBinder token)703 public void splashScreenAttached(IBinder token) { 704 final long origId = Binder.clearCallingIdentity(); 705 synchronized (mGlobalLock) { 706 ActivityRecord.splashScreenAttachedLocked(token); 707 } 708 Binder.restoreCallingIdentity(origId); 709 } 710 711 /** 712 * Checks the state of the system and the activity associated with the given {@param token} to 713 * verify that picture-in-picture is supported for that activity. 714 * 715 * @return the activity record for the given {@param token} if all the checks pass. 716 */ ensureValidPictureInPictureActivityParams(String caller, IBinder token, PictureInPictureParams params)717 private ActivityRecord ensureValidPictureInPictureActivityParams(String caller, 718 IBinder token, PictureInPictureParams params) { 719 if (!mService.mSupportsPictureInPicture) { 720 throw new IllegalStateException(caller 721 + ": Device doesn't support picture-in-picture mode."); 722 } 723 724 final ActivityRecord r = ActivityRecord.forTokenLocked(token); 725 if (r == null) { 726 throw new IllegalStateException(caller 727 + ": Can't find activity for token=" + token); 728 } 729 730 if (!r.supportsPictureInPicture()) { 731 throw new IllegalStateException(caller 732 + ": Current activity does not support picture-in-picture."); 733 } 734 735 if (params.hasSetAspectRatio() 736 && !mService.mWindowManager.isValidPictureInPictureAspectRatio( 737 r.mDisplayContent, params.getAspectRatio())) { 738 final float minAspectRatio = mContext.getResources().getFloat( 739 com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio); 740 final float maxAspectRatio = mContext.getResources().getFloat( 741 com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio); 742 throw new IllegalArgumentException(String.format(caller 743 + ": Aspect ratio is too extreme (must be between %f and %f).", 744 minAspectRatio, maxAspectRatio)); 745 } 746 747 // Truncate the number of actions if necessary. 748 params.truncateActions(ActivityTaskManager.getMaxNumPictureInPictureActions(mContext)); 749 return r; 750 } 751 752 /** 753 * Requests that an activity should enter picture-in-picture mode if possible. This method may 754 * be used by the implementation of non-phone form factors. 755 */ requestPictureInPictureMode(@onNull ActivityRecord r)756 void requestPictureInPictureMode(@NonNull ActivityRecord r) { 757 if (r.inPinnedWindowingMode()) { 758 throw new IllegalStateException("Activity is already in PIP mode"); 759 } 760 761 final boolean canEnterPictureInPicture = r.checkEnterPictureInPictureState( 762 "requestPictureInPictureMode", /* beforeStopping */ false); 763 if (!canEnterPictureInPicture) { 764 throw new IllegalStateException( 765 "Requested PIP on an activity that doesn't support it"); 766 } 767 768 if (r.pictureInPictureArgs.isAutoEnterEnabled()) { 769 mService.enterPictureInPictureMode(r, r.pictureInPictureArgs); 770 return; 771 } 772 773 try { 774 final ClientTransaction transaction = ClientTransaction.obtain( 775 r.app.getThread(), r.token); 776 transaction.addCallback(EnterPipRequestedItem.obtain()); 777 mService.getLifecycleManager().scheduleTransaction(transaction); 778 } catch (Exception e) { 779 Slog.w(TAG, "Failed to send enter pip requested item: " 780 + r.intent.getComponent(), e); 781 } 782 } 783 784 /** 785 * Alert the client that the Picture-in-Picture state has changed. 786 */ onPictureInPictureStateChanged(@onNull ActivityRecord r, PictureInPictureUiState pipState)787 void onPictureInPictureStateChanged(@NonNull ActivityRecord r, 788 PictureInPictureUiState pipState) { 789 if (!r.inPinnedWindowingMode()) { 790 throw new IllegalStateException("Activity is not in PIP mode"); 791 } 792 793 try { 794 final ClientTransaction transaction = ClientTransaction.obtain( 795 r.app.getThread(), r.token); 796 transaction.addCallback(PipStateTransactionItem.obtain(pipState)); 797 mService.getLifecycleManager().scheduleTransaction(transaction); 798 } catch (Exception e) { 799 Slog.w(TAG, "Failed to send pip state transaction item: " 800 + r.intent.getComponent(), e); 801 } 802 } 803 804 @Override toggleFreeformWindowingMode(IBinder token)805 public void toggleFreeformWindowingMode(IBinder token) { 806 final long ident = Binder.clearCallingIdentity(); 807 try { 808 synchronized (mGlobalLock) { 809 final ActivityRecord r = ActivityRecord.forTokenLocked(token); 810 if (r == null) { 811 throw new IllegalArgumentException( 812 "toggleFreeformWindowingMode: No activity record matching token=" 813 + token); 814 } 815 816 final Task rootTask = r.getRootTask(); 817 if (rootTask == null) { 818 throw new IllegalStateException("toggleFreeformWindowingMode: the activity " 819 + "doesn't have a root task"); 820 } 821 822 if (!rootTask.inFreeformWindowingMode() 823 && rootTask.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) { 824 throw new IllegalStateException("toggleFreeformWindowingMode: You can only " 825 + "toggle between fullscreen and freeform."); 826 } 827 828 if (rootTask.inFreeformWindowingMode()) { 829 rootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN); 830 } else if (!r.supportsFreeform()) { 831 throw new IllegalStateException( 832 "This activity is currently not freeform-enabled"); 833 } else if (rootTask.getParent().inFreeformWindowingMode()) { 834 // If the window is on a freeform display, set it to undefined. It will be 835 // resolved to freeform and it can adjust windowing mode when the display mode 836 // changes in runtime. 837 rootTask.setWindowingMode(WINDOWING_MODE_UNDEFINED); 838 } else { 839 rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM); 840 } 841 } 842 } finally { 843 Binder.restoreCallingIdentity(ident); 844 } 845 } 846 847 @Override startLockTaskModeByToken(IBinder token)848 public void startLockTaskModeByToken(IBinder token) { 849 synchronized (mGlobalLock) { 850 final ActivityRecord r = ActivityRecord.forTokenLocked(token); 851 if (r != null) { 852 mService.startLockTaskMode(r.getTask(), false /* isSystemCaller */); 853 } 854 } 855 } 856 857 @Override stopLockTaskModeByToken(IBinder token)858 public void stopLockTaskModeByToken(IBinder token) { 859 mService.stopLockTaskModeInternal(token, false /* isSystemCaller */); 860 } 861 862 @Override showLockTaskEscapeMessage(IBinder token)863 public void showLockTaskEscapeMessage(IBinder token) { 864 synchronized (mGlobalLock) { 865 if (ActivityRecord.forTokenLocked(token) != null) { 866 mService.getLockTaskController().showLockTaskToast(); 867 } 868 } 869 } 870 871 @Override setTaskDescription(IBinder token, ActivityManager.TaskDescription td)872 public void setTaskDescription(IBinder token, ActivityManager.TaskDescription td) { 873 synchronized (mGlobalLock) { 874 final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); 875 if (r != null) { 876 r.setTaskDescription(td); 877 } 878 } 879 } 880 881 @Override showAssistFromActivity(IBinder token, Bundle args)882 public boolean showAssistFromActivity(IBinder token, Bundle args) { 883 final long ident = Binder.clearCallingIdentity(); 884 try { 885 synchronized (mGlobalLock) { 886 final ActivityRecord caller = ActivityRecord.forTokenLocked(token); 887 final Task topRootTask = mService.getTopDisplayFocusedRootTask(); 888 final ActivityRecord top = topRootTask != null 889 ? topRootTask.getTopNonFinishingActivity() : null; 890 if (top != caller) { 891 Slog.w(TAG, "showAssistFromActivity failed: caller " + caller 892 + " is not current top " + top); 893 return false; 894 } 895 if (!top.nowVisible) { 896 Slog.w(TAG, "showAssistFromActivity failed: caller " + caller 897 + " is not visible"); 898 return false; 899 } 900 } 901 return mAssistUtils.showSessionForActiveService(args, SHOW_SOURCE_APPLICATION, 902 null /* showCallback */, token); 903 } finally { 904 Binder.restoreCallingIdentity(ident); 905 } 906 } 907 908 @Override isRootVoiceInteraction(IBinder token)909 public boolean isRootVoiceInteraction(IBinder token) { 910 synchronized (mGlobalLock) { 911 final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); 912 return r != null && r.rootVoiceInteraction; 913 } 914 } 915 916 @Override startLocalVoiceInteraction(IBinder callingActivity, Bundle options)917 public void startLocalVoiceInteraction(IBinder callingActivity, Bundle options) { 918 Slog.i(TAG, "Activity tried to startLocalVoiceInteraction"); 919 synchronized (mGlobalLock) { 920 final Task topRootTask = mService.getTopDisplayFocusedRootTask(); 921 final ActivityRecord activity = topRootTask != null 922 ? topRootTask.getTopNonFinishingActivity() : null; 923 if (ActivityRecord.forTokenLocked(callingActivity) != activity) { 924 throw new SecurityException("Only focused activity can call startVoiceInteraction"); 925 } 926 if (mService.mRunningVoice != null || activity.getTask().voiceSession != null 927 || activity.voiceSession != null) { 928 Slog.w(TAG, "Already in a voice interaction, cannot start new voice interaction"); 929 return; 930 } 931 if (activity.pendingVoiceInteractionStart) { 932 Slog.w(TAG, "Pending start of voice interaction already."); 933 return; 934 } 935 activity.pendingVoiceInteractionStart = true; 936 } 937 LocalServices.getService(VoiceInteractionManagerInternal.class) 938 .startLocalVoiceInteraction(callingActivity, options); 939 } 940 941 @Override stopLocalVoiceInteraction(IBinder callingActivity)942 public void stopLocalVoiceInteraction(IBinder callingActivity) { 943 LocalServices.getService(VoiceInteractionManagerInternal.class) 944 .stopLocalVoiceInteraction(callingActivity); 945 } 946 947 @Override setShowWhenLocked(IBinder token, boolean showWhenLocked)948 public void setShowWhenLocked(IBinder token, boolean showWhenLocked) { 949 final long origId = Binder.clearCallingIdentity(); 950 try { 951 synchronized (mGlobalLock) { 952 final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); 953 if (r != null) { 954 r.setShowWhenLocked(showWhenLocked); 955 } 956 } 957 } finally { 958 Binder.restoreCallingIdentity(origId); 959 } 960 } 961 962 @Override setInheritShowWhenLocked(IBinder token, boolean inheritShowWhenLocked)963 public void setInheritShowWhenLocked(IBinder token, boolean inheritShowWhenLocked) { 964 final long origId = Binder.clearCallingIdentity(); 965 try { 966 synchronized (mGlobalLock) { 967 final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); 968 if (r != null) { 969 r.setInheritShowWhenLocked(inheritShowWhenLocked); 970 } 971 } 972 } finally { 973 Binder.restoreCallingIdentity(origId); 974 } 975 } 976 977 @Override setTurnScreenOn(IBinder token, boolean turnScreenOn)978 public void setTurnScreenOn(IBinder token, boolean turnScreenOn) { 979 final long origId = Binder.clearCallingIdentity(); 980 try { 981 synchronized (mGlobalLock) { 982 final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); 983 if (r != null) { 984 r.setTurnScreenOn(turnScreenOn); 985 } 986 } 987 } finally { 988 Binder.restoreCallingIdentity(origId); 989 } 990 } 991 992 @Override reportActivityFullyDrawn(IBinder token, boolean restoredFromBundle)993 public void reportActivityFullyDrawn(IBinder token, boolean restoredFromBundle) { 994 final long origId = Binder.clearCallingIdentity(); 995 try { 996 synchronized (mGlobalLock) { 997 final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); 998 if (r != null) { 999 r.reportFullyDrawnLocked(restoredFromBundle); 1000 } 1001 } 1002 } finally { 1003 Binder.restoreCallingIdentity(origId); 1004 } 1005 } 1006 1007 @Override overridePendingTransition(IBinder token, String packageName, int enterAnim, int exitAnim)1008 public void overridePendingTransition(IBinder token, String packageName, 1009 int enterAnim, int exitAnim) { 1010 final long origId = Binder.clearCallingIdentity(); 1011 synchronized (mGlobalLock) { 1012 final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); 1013 if (r != null && r.isState(Task.ActivityState.RESUMED, Task.ActivityState.PAUSING)) { 1014 r.mDisplayContent.mAppTransition.overridePendingAppTransition( 1015 packageName, enterAnim, exitAnim, null, null, 1016 r.mOverrideTaskTransition); 1017 } 1018 } 1019 Binder.restoreCallingIdentity(origId); 1020 } 1021 1022 @Override setVrMode(IBinder token, boolean enabled, ComponentName packageName)1023 public int setVrMode(IBinder token, boolean enabled, ComponentName packageName) { 1024 mService.enforceSystemHasVrFeature(); 1025 1026 final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class); 1027 final ActivityRecord r; 1028 synchronized (mGlobalLock) { 1029 r = ActivityRecord.isInRootTaskLocked(token); 1030 } 1031 if (r == null) { 1032 throw new IllegalArgumentException(); 1033 } 1034 1035 final int err; 1036 if ((err = vrService.hasVrPackage(packageName, r.mUserId)) != VrManagerInternal.NO_ERROR) { 1037 return err; 1038 } 1039 1040 // Clear the binder calling uid since this path may call moveToTask(). 1041 final long callingId = Binder.clearCallingIdentity(); 1042 try { 1043 synchronized (mGlobalLock) { 1044 r.requestedVrComponent = (enabled) ? packageName : null; 1045 1046 // Update associated state if this activity is currently focused. 1047 if (r.isFocusedActivityOnDisplay()) { 1048 mService.applyUpdateVrModeLocked(r); 1049 } 1050 return 0; 1051 } 1052 } finally { 1053 Binder.restoreCallingIdentity(callingId); 1054 } 1055 } 1056 1057 @Override setDisablePreviewScreenshots(IBinder token, boolean disable)1058 public void setDisablePreviewScreenshots(IBinder token, boolean disable) { 1059 final long origId = Binder.clearCallingIdentity(); 1060 try { 1061 synchronized (mGlobalLock) { 1062 final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); 1063 if (r != null) { 1064 r.setDisablePreviewScreenshots(disable); 1065 } 1066 } 1067 } finally { 1068 Binder.restoreCallingIdentity(origId); 1069 } 1070 } 1071 restartActivityProcessIfVisible(IBinder token)1072 void restartActivityProcessIfVisible(IBinder token) { 1073 ActivityTaskManagerService.enforceTaskPermission("restartActivityProcess"); 1074 final long callingId = Binder.clearCallingIdentity(); 1075 try { 1076 synchronized (mGlobalLock) { 1077 final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); 1078 if (r != null) { 1079 r.restartProcessIfVisible(); 1080 } 1081 } 1082 } finally { 1083 Binder.restoreCallingIdentity(callingId); 1084 } 1085 } 1086 1087 @Override invalidateHomeTaskSnapshot(IBinder token)1088 public void invalidateHomeTaskSnapshot(IBinder token) { 1089 synchronized (mGlobalLock) { 1090 final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); 1091 if (r != null && r.isActivityTypeHome()) { 1092 mService.mWindowManager.mTaskSnapshotController.removeSnapshotCache( 1093 r.getTask().mTaskId); 1094 } 1095 } 1096 } 1097 1098 @Override dismissKeyguard(IBinder token, IKeyguardDismissCallback callback, CharSequence message)1099 public void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback, 1100 CharSequence message) { 1101 if (message != null) { 1102 mService.mAmInternal.enforceCallingPermission( 1103 android.Manifest.permission.SHOW_KEYGUARD_MESSAGE, "dismissKeyguard"); 1104 } 1105 final long callingId = Binder.clearCallingIdentity(); 1106 try { 1107 synchronized (mGlobalLock) { 1108 mService.mKeyguardController.dismissKeyguard(token, callback, message); 1109 } 1110 } finally { 1111 Binder.restoreCallingIdentity(callingId); 1112 } 1113 } 1114 1115 @Override registerRemoteAnimations(IBinder token, RemoteAnimationDefinition definition)1116 public void registerRemoteAnimations(IBinder token, RemoteAnimationDefinition definition) { 1117 mService.mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, 1118 "registerRemoteAnimations"); 1119 definition.setCallingPidUid(Binder.getCallingPid(), Binder.getCallingUid()); 1120 final long origId = Binder.clearCallingIdentity(); 1121 try { 1122 synchronized (mGlobalLock) { 1123 final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); 1124 if (r != null) { 1125 r.registerRemoteAnimations(definition); 1126 } 1127 } 1128 } finally { 1129 Binder.restoreCallingIdentity(origId); 1130 } 1131 } 1132 1133 @Override unregisterRemoteAnimations(IBinder token)1134 public void unregisterRemoteAnimations(IBinder token) { 1135 mService.mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, 1136 "unregisterRemoteAnimations"); 1137 final long origId = Binder.clearCallingIdentity(); 1138 try { 1139 synchronized (mGlobalLock) { 1140 final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); 1141 if (r != null) { 1142 r.unregisterRemoteAnimations(); 1143 } 1144 } 1145 } finally { 1146 Binder.restoreCallingIdentity(origId); 1147 } 1148 } 1149 1150 @Override onBackPressedOnTaskRoot(IBinder token, IRequestFinishCallback callback)1151 public void onBackPressedOnTaskRoot(IBinder token, IRequestFinishCallback callback) { 1152 final long origId = Binder.clearCallingIdentity(); 1153 try { 1154 final Intent baseActivityIntent; 1155 final boolean launchedFromHome; 1156 1157 synchronized (mGlobalLock) { 1158 final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); 1159 if (r == null) return; 1160 1161 if (mService.mWindowOrganizerController.mTaskOrganizerController 1162 .handleInterceptBackPressedOnTaskRoot(r.getRootTask())) { 1163 // This task is handled by a task organizer that has requested the back pressed 1164 // callback. 1165 return; 1166 } 1167 1168 final Intent baseIntent = r.getTask().getBaseIntent(); 1169 final boolean activityIsBaseActivity = baseIntent != null 1170 && r.mActivityComponent.equals(baseIntent.getComponent()); 1171 baseActivityIntent = activityIsBaseActivity ? r.intent : null; 1172 launchedFromHome = r.isLaunchSourceType(ActivityRecord.LAUNCH_SOURCE_TYPE_HOME); 1173 } 1174 1175 // If the activity is one of the main entry points for the application, then we should 1176 // refrain from finishing the activity and instead move it to the back to keep it in 1177 // memory. The requirements for this are: 1178 // 1. The current activity is the base activity for the task. 1179 // 2. a. If the activity was launched by the home process, we trust that its intent 1180 // was resolved, so we check if the it is a main intent for the application. 1181 // b. Otherwise, we query Package Manager to verify whether the activity is a 1182 // launcher activity for the application. 1183 if (baseActivityIntent != null 1184 && ((launchedFromHome && ActivityRecord.isMainIntent(baseActivityIntent)) 1185 || isLauncherActivity(baseActivityIntent.getComponent()))) { 1186 moveActivityTaskToBack(token, false /* nonRoot */); 1187 return; 1188 } 1189 1190 // The default option for handling the back button is to finish the Activity. 1191 try { 1192 callback.requestFinish(); 1193 } catch (RemoteException e) { 1194 Slog.e(TAG, "Failed to invoke request finish callback", e); 1195 } 1196 } finally { 1197 Binder.restoreCallingIdentity(origId); 1198 } 1199 } 1200 1201 /** 1202 * Queries PackageManager to see if the given activity is one of the main entry point for the 1203 * application. This should not be called with the WM lock held. 1204 */ 1205 @SuppressWarnings("unchecked") isLauncherActivity(@onNull ComponentName activity)1206 private boolean isLauncherActivity(@NonNull ComponentName activity) { 1207 final Intent queryIntent = new Intent(Intent.ACTION_MAIN); 1208 queryIntent.addCategory(Intent.CATEGORY_LAUNCHER); 1209 queryIntent.setPackage(activity.getPackageName()); 1210 try { 1211 final ParceledListSlice<ResolveInfo> resolved = 1212 mService.getPackageManager().queryIntentActivities( 1213 queryIntent, null, 0, mContext.getUserId()); 1214 if (resolved == null) return false; 1215 for (final ResolveInfo ri : resolved.getList()) { 1216 if (ri.getComponentInfo().getComponentName().equals(activity)) { 1217 return true; 1218 } 1219 } 1220 } catch (RemoteException e) { 1221 Slog.e(TAG, "Failed to query intent activities", e); 1222 } 1223 return false; 1224 } 1225 } 1226