1 /* 2 * Copyright (C) 2018 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.app.ActivityManager.START_TASK_TO_FRONT; 20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; 21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; 22 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; 23 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; 24 import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION; 25 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; 26 27 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS; 28 import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; 29 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE; 30 import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION; 31 import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_TOP; 32 import static com.android.server.wm.TaskDisplayArea.getRootTaskAbove; 33 34 import android.annotation.Nullable; 35 import android.app.ActivityOptions; 36 import android.content.ComponentName; 37 import android.content.Intent; 38 import android.os.RemoteException; 39 import android.os.Trace; 40 import android.util.Slog; 41 import android.view.IRecentsAnimationRunner; 42 43 import com.android.internal.protolog.common.ProtoLog; 44 import com.android.internal.util.function.pooled.PooledLambda; 45 import com.android.internal.util.function.pooled.PooledPredicate; 46 import com.android.server.wm.ActivityMetricsLogger.LaunchingState; 47 import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks; 48 import com.android.server.wm.TaskDisplayArea.OnRootTaskOrderChangedListener; 49 50 /** 51 * Manages the recents animation, including the reordering of the root tasks for the transition and 52 * cleanup. See {@link com.android.server.wm.RecentsAnimationController}. 53 */ 54 class RecentsAnimation implements RecentsAnimationCallbacks, OnRootTaskOrderChangedListener { 55 private static final String TAG = RecentsAnimation.class.getSimpleName(); 56 57 private final ActivityTaskManagerService mService; 58 private final ActivityTaskSupervisor mTaskSupervisor; 59 private final ActivityStartController mActivityStartController; 60 private final WindowManagerService mWindowManager; 61 private final TaskDisplayArea mDefaultTaskDisplayArea; 62 private final Intent mTargetIntent; 63 private final ComponentName mRecentsComponent; 64 private final @Nullable String mRecentsFeatureId; 65 private final int mRecentsUid; 66 private final @Nullable WindowProcessController mCaller; 67 private final int mUserId; 68 private final int mTargetActivityType; 69 70 /** 71 * The activity which has been launched behind. We need to remember the activity because the 72 * target root task may have other activities, then we are able to restore the launch-behind 73 * state for the exact activity. 74 */ 75 private ActivityRecord mLaunchedTargetActivity; 76 77 // The root task to restore the target root task behind when the animation is finished 78 private Task mRestoreTargetBehindRootTask; 79 RecentsAnimation(ActivityTaskManagerService atm, ActivityTaskSupervisor taskSupervisor, ActivityStartController activityStartController, WindowManagerService wm, Intent targetIntent, ComponentName recentsComponent, @Nullable String recentsFeatureId, int recentsUid, @Nullable WindowProcessController caller)80 RecentsAnimation(ActivityTaskManagerService atm, ActivityTaskSupervisor taskSupervisor, 81 ActivityStartController activityStartController, WindowManagerService wm, 82 Intent targetIntent, ComponentName recentsComponent, @Nullable String recentsFeatureId, 83 int recentsUid, @Nullable WindowProcessController caller) { 84 mService = atm; 85 mTaskSupervisor = taskSupervisor; 86 mDefaultTaskDisplayArea = mService.mRootWindowContainer.getDefaultTaskDisplayArea(); 87 mActivityStartController = activityStartController; 88 mWindowManager = wm; 89 mTargetIntent = targetIntent; 90 mRecentsComponent = recentsComponent; 91 mRecentsFeatureId = recentsFeatureId; 92 mRecentsUid = recentsUid; 93 mCaller = caller; 94 mUserId = atm.getCurrentUserId(); 95 mTargetActivityType = targetIntent.getComponent() != null 96 && recentsComponent.equals(targetIntent.getComponent()) 97 ? ACTIVITY_TYPE_RECENTS 98 : ACTIVITY_TYPE_HOME; 99 } 100 101 /** 102 * Starts the recents activity in background without animation if the record doesn't exist or 103 * the client isn't launched. If the recents activity is already alive, ensure its configuration 104 * is updated to the current one. 105 */ preloadRecentsActivity()106 void preloadRecentsActivity() { 107 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Preload recents with %s", 108 mTargetIntent); 109 Task targetRootTask = mDefaultTaskDisplayArea.getRootTask(WINDOWING_MODE_UNDEFINED, 110 mTargetActivityType); 111 ActivityRecord targetActivity = getTargetActivity(targetRootTask); 112 if (targetActivity != null) { 113 if (targetActivity.mVisibleRequested || targetActivity.isTopRunningActivity()) { 114 // The activity is ready. 115 return; 116 } 117 if (targetActivity.attachedToProcess()) { 118 // The activity may be relaunched if it cannot handle the current configuration 119 // changes. The activity will be paused state if it is relaunched, otherwise it 120 // keeps the original stopped state. 121 targetActivity.ensureActivityConfiguration(0 /* globalChanges */, 122 false /* preserveWindow */, true /* ignoreVisibility */); 123 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Updated config=%s", 124 targetActivity.getConfiguration()); 125 } 126 } else { 127 // Create the activity record. Because the activity is invisible, this doesn't really 128 // start the client. 129 startRecentsActivityInBackground("preloadRecents"); 130 targetRootTask = mDefaultTaskDisplayArea.getRootTask(WINDOWING_MODE_UNDEFINED, 131 mTargetActivityType); 132 targetActivity = getTargetActivity(targetRootTask); 133 if (targetActivity == null) { 134 Slog.w(TAG, "Cannot start " + mTargetIntent); 135 return; 136 } 137 } 138 139 if (!targetActivity.attachedToProcess()) { 140 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Real start recents"); 141 mTaskSupervisor.startSpecificActivity(targetActivity, false /* andResume */, 142 false /* checkConfig */); 143 // Make sure the activity won't be involved in transition. 144 if (targetActivity.getDisplayContent() != null) { 145 targetActivity.getDisplayContent().mUnknownAppVisibilityController 146 .appRemovedOrHidden(targetActivity); 147 } 148 } 149 150 // Invisible activity should be stopped. If the recents activity is alive and its doesn't 151 // need to relaunch by current configuration, then it may be already in stopped state. 152 if (!targetActivity.isState(Task.ActivityState.STOPPING, 153 Task.ActivityState.STOPPED)) { 154 // Add to stopping instead of stop immediately. So the client has the chance to perform 155 // traversal in non-stopped state (ViewRootImpl.mStopped) that would initialize more 156 // things (e.g. the measure can be done earlier). The actual stop will be performed when 157 // it reports idle. 158 targetActivity.addToStopping(true /* scheduleIdle */, true /* idleDelayed */, 159 "preloadRecents"); 160 } 161 } 162 startRecentsActivity(IRecentsAnimationRunner recentsAnimationRunner, long eventTime)163 void startRecentsActivity(IRecentsAnimationRunner recentsAnimationRunner, long eventTime) { 164 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "startRecentsActivity(): intent=%s", mTargetIntent); 165 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RecentsAnimation#startRecentsActivity"); 166 167 // Cancel any existing recents animation running synchronously (do not hold the 168 // WM lock) before starting the newly requested recents animation as they can not coexist 169 if (mWindowManager.getRecentsAnimationController() != null) { 170 mWindowManager.getRecentsAnimationController().forceCancelAnimation( 171 REORDER_MOVE_TO_ORIGINAL_POSITION, "startRecentsActivity"); 172 } 173 174 // If the activity is associated with the root recents task, then try and get that first 175 Task targetRootTask = mDefaultTaskDisplayArea.getRootTask(WINDOWING_MODE_UNDEFINED, 176 mTargetActivityType); 177 ActivityRecord targetActivity = getTargetActivity(targetRootTask); 178 final boolean hasExistingActivity = targetActivity != null; 179 if (hasExistingActivity) { 180 mRestoreTargetBehindRootTask = getRootTaskAbove(targetRootTask); 181 if (mRestoreTargetBehindRootTask == null 182 && targetRootTask.getTopMostTask() == targetActivity.getTask()) { 183 notifyAnimationCancelBeforeStart(recentsAnimationRunner); 184 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, 185 "No root task above target root task=%s", targetRootTask); 186 return; 187 } 188 } 189 190 // Send launch hint if we are actually launching the target. If it's already visible 191 // (shouldn't happen in general) we don't need to send it. 192 if (targetActivity == null || !targetActivity.mVisibleRequested) { 193 mService.mRootWindowContainer.startPowerModeLaunchIfNeeded( 194 true /* forceSend */, targetActivity); 195 } 196 197 final LaunchingState launchingState = 198 mTaskSupervisor.getActivityMetricsLogger().notifyActivityLaunching(mTargetIntent); 199 200 if (mCaller != null) { 201 mCaller.setRunningRecentsAnimation(true); 202 } 203 204 mService.deferWindowLayout(); 205 try { 206 if (hasExistingActivity) { 207 // Move the recents activity into place for the animation if it is not top most 208 mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(targetRootTask); 209 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Moved rootTask=%s behind rootTask=%s", 210 targetRootTask, getRootTaskAbove(targetRootTask)); 211 212 // If there are multiple tasks in the target root task (ie. the root home task, 213 // with 3p and default launchers coexisting), then move the task to the top as a 214 // part of moving the root task to the front 215 final Task task = targetActivity.getTask(); 216 if (targetRootTask.getTopMostTask() != task) { 217 targetRootTask.positionChildAtTop(task); 218 } 219 } else { 220 // No recents activity, create the new recents activity bottom most 221 startRecentsActivityInBackground("startRecentsActivity_noTargetActivity"); 222 223 // Move the recents activity into place for the animation 224 targetRootTask = mDefaultTaskDisplayArea.getRootTask(WINDOWING_MODE_UNDEFINED, 225 mTargetActivityType); 226 targetActivity = getTargetActivity(targetRootTask); 227 mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(targetRootTask); 228 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Moved rootTask=%s behind rootTask=%s", 229 targetRootTask, getRootTaskAbove(targetRootTask)); 230 231 mWindowManager.prepareAppTransitionNone(); 232 mWindowManager.executeAppTransition(); 233 234 // TODO: Maybe wait for app to draw in this particular case? 235 236 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "Started intent=%s", mTargetIntent); 237 } 238 239 // Mark the target activity as launch-behind to bump its visibility for the 240 // duration of the gesture that is driven by the recents component 241 targetActivity.mLaunchTaskBehind = true; 242 mLaunchedTargetActivity = targetActivity; 243 // TODO(b/156772625): Evaluate to send new intents vs. replacing the intent extras. 244 targetActivity.intent.replaceExtras(mTargetIntent); 245 246 // Fetch all the surface controls and pass them to the client to get the animation 247 // started 248 mWindowManager.initializeRecentsAnimation(mTargetActivityType, recentsAnimationRunner, 249 this, mDefaultTaskDisplayArea.getDisplayId(), 250 mTaskSupervisor.mRecentTasks.getRecentTaskIds(), targetActivity); 251 252 // If we updated the launch-behind state, update the visibility of the activities after 253 // we fetch the visible tasks to be controlled by the animation 254 mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); 255 256 ActivityOptions options = null; 257 if (eventTime > 0) { 258 options = ActivityOptions.makeBasic(); 259 options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_RECENTS_ANIMATION, eventTime); 260 } 261 mTaskSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState, 262 START_TASK_TO_FRONT, !hasExistingActivity, targetActivity, options); 263 264 // Register for root task order changes 265 mDefaultTaskDisplayArea.registerRootTaskOrderChangedListener(this); 266 } catch (Exception e) { 267 Slog.e(TAG, "Failed to start recents activity", e); 268 throw e; 269 } finally { 270 mService.continueWindowLayout(); 271 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 272 } 273 } 274 finishAnimation(@ecentsAnimationController.ReorderMode int reorderMode, boolean sendUserLeaveHint)275 private void finishAnimation(@RecentsAnimationController.ReorderMode int reorderMode, 276 boolean sendUserLeaveHint) { 277 synchronized (mService.mGlobalLock) { 278 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, 279 "onAnimationFinished(): controller=%s reorderMode=%d", 280 mWindowManager.getRecentsAnimationController(), reorderMode); 281 282 // Unregister for root task order changes 283 mDefaultTaskDisplayArea.unregisterRootTaskOrderChangedListener(this); 284 285 final RecentsAnimationController controller = 286 mWindowManager.getRecentsAnimationController(); 287 if (controller == null) return; 288 289 // Just to be sure end the launch hint in case the target activity was never launched. 290 // However, if we're keeping the activity and making it visible, we can leave it on. 291 if (reorderMode != REORDER_KEEP_IN_PLACE) { 292 mService.endLaunchPowerMode( 293 ActivityTaskManagerService.POWER_MODE_REASON_START_ACTIVITY); 294 } 295 296 // Once the target is shown, prevent spurious background app switches 297 if (reorderMode == REORDER_MOVE_TO_TOP) { 298 mService.stopAppSwitches(); 299 } 300 301 if (mCaller != null) { 302 mCaller.setRunningRecentsAnimation(false); 303 } 304 305 mWindowManager.inSurfaceTransaction(() -> { 306 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, 307 "RecentsAnimation#onAnimationFinished_inSurfaceTransaction"); 308 mService.deferWindowLayout(); 309 try { 310 mWindowManager.cleanupRecentsAnimation(reorderMode); 311 312 final Task targetRootTask = mDefaultTaskDisplayArea.getRootTask( 313 WINDOWING_MODE_UNDEFINED, mTargetActivityType); 314 // Prefer to use the original target activity instead of top activity because 315 // we may have moved another task to top (starting 3p launcher). 316 final ActivityRecord targetActivity = targetRootTask != null 317 ? targetRootTask.isInTask(mLaunchedTargetActivity) 318 : null; 319 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, 320 "onAnimationFinished(): targetRootTask=%s targetActivity=%s " 321 + "mRestoreTargetBehindRootTask=%s", 322 targetRootTask, targetActivity, mRestoreTargetBehindRootTask); 323 if (targetActivity == null) { 324 return; 325 } 326 327 // Restore the launched-behind state 328 targetActivity.mLaunchTaskBehind = false; 329 330 if (reorderMode == REORDER_MOVE_TO_TOP) { 331 // Bring the target root task to the front 332 mTaskSupervisor.mNoAnimActivities.add(targetActivity); 333 334 if (sendUserLeaveHint) { 335 // Setting this allows the previous app to PiP. 336 mTaskSupervisor.mUserLeaving = true; 337 targetRootTask.moveTaskToFront(targetActivity.getTask(), 338 true /* noAnimation */, null /* activityOptions */, 339 targetActivity.appTimeTracker, 340 "RecentsAnimation.onAnimationFinished()"); 341 } else { 342 targetRootTask.moveToFront("RecentsAnimation.onAnimationFinished()"); 343 } 344 345 if (WM_DEBUG_RECENTS_ANIMATIONS.isLogToAny()) { 346 final Task topRootTask = getTopNonAlwaysOnTopRootTask(); 347 if (topRootTask != targetRootTask) { 348 ProtoLog.w(WM_DEBUG_RECENTS_ANIMATIONS, 349 "Expected target rootTask=%s" 350 + " to be top most but found rootTask=%s", 351 targetRootTask, topRootTask); 352 } 353 } 354 } else if (reorderMode == REORDER_MOVE_TO_ORIGINAL_POSITION){ 355 // Restore the target root task to its previous position 356 final TaskDisplayArea taskDisplayArea = targetActivity.getDisplayArea(); 357 taskDisplayArea.moveRootTaskBehindRootTask(targetRootTask, 358 mRestoreTargetBehindRootTask); 359 if (WM_DEBUG_RECENTS_ANIMATIONS.isLogToAny()) { 360 final Task aboveTargetRootTask = getRootTaskAbove(targetRootTask); 361 if (mRestoreTargetBehindRootTask != null 362 && aboveTargetRootTask != mRestoreTargetBehindRootTask) { 363 ProtoLog.w(WM_DEBUG_RECENTS_ANIMATIONS, 364 "Expected target rootTask=%s to restored behind " 365 + "rootTask=%s but it is behind rootTask=%s", 366 targetRootTask, mRestoreTargetBehindRootTask, 367 aboveTargetRootTask); 368 } 369 } 370 } else { 371 // If there is no recents screenshot animation, we can update the visibility 372 // of target root task immediately because it is visually invisible and the 373 // launch-behind state is restored. That also prevents the next transition 374 // type being disturbed if the visibility is updated after setting the next 375 // transition (the target activity will be one of closing apps). 376 if (!controller.shouldDeferCancelWithScreenshot() 377 && !targetRootTask.isFocusedRootTaskOnDisplay()) { 378 targetRootTask.ensureActivitiesVisible(null /* starting */, 379 0 /* starting */, false /* preserveWindows */); 380 } 381 // Keep target root task in place, nothing changes, so ignore the transition 382 // logic below 383 return; 384 } 385 386 mWindowManager.prepareAppTransitionNone(); 387 mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, false); 388 mService.mRootWindowContainer.resumeFocusedTasksTopActivities(); 389 390 // No reason to wait for the pausing activity in this case, as the hiding of 391 // surfaces needs to be done immediately. 392 mWindowManager.executeAppTransition(); 393 394 final Task rootTask = targetRootTask.getRootTask(); 395 // Client state may have changed during the recents animation, so force 396 // send task info so the client can synchronize its state. 397 rootTask.dispatchTaskInfoChangedIfNeeded(true /* force */); 398 } catch (Exception e) { 399 Slog.e(TAG, "Failed to clean up recents activity", e); 400 throw e; 401 } finally { 402 mTaskSupervisor.mUserLeaving = false; 403 mService.continueWindowLayout(); 404 // Make sure the surfaces are updated with the latest state. Sometimes the 405 // surface placement may be skipped if display configuration is changed (i.e. 406 // {@link DisplayContent#mWaitingForConfig} is true). 407 if (mWindowManager.mRoot.isLayoutNeeded()) { 408 mWindowManager.mRoot.performSurfacePlacement(); 409 } 410 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 411 } 412 }); 413 } 414 } 415 416 @Override onAnimationFinished(@ecentsAnimationController.ReorderMode int reorderMode, boolean sendUserLeaveHint)417 public void onAnimationFinished(@RecentsAnimationController.ReorderMode int reorderMode, 418 boolean sendUserLeaveHint) { 419 finishAnimation(reorderMode, sendUserLeaveHint); 420 } 421 422 @Override onRootTaskOrderChanged(Task rootTask)423 public void onRootTaskOrderChanged(Task rootTask) { 424 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "onRootTaskOrderChanged(): rootTask=%s", rootTask); 425 if (mDefaultTaskDisplayArea.getRootTask(t -> t == rootTask) == null 426 || !rootTask.shouldBeVisible(null)) { 427 // The root task is not visible, so ignore this change 428 return; 429 } 430 final RecentsAnimationController controller = 431 mWindowManager.getRecentsAnimationController(); 432 if (controller == null) { 433 return; 434 } 435 436 // We defer canceling the recents animation until the next app transition in the following 437 // cases: 438 // 1) The next launching task is not being animated by the recents animation 439 // 2) The next task is home activity. (i.e. pressing home key to back home in recents). 440 if ((!controller.isAnimatingTask(rootTask.getTopMostTask()) 441 || controller.isTargetApp(rootTask.getTopNonFinishingActivity())) 442 && controller.shouldDeferCancelUntilNextTransition()) { 443 // Always prepare an app transition since we rely on the transition callbacks to cleanup 444 mWindowManager.prepareAppTransitionNone(); 445 controller.setCancelOnNextTransitionStart(); 446 } 447 } 448 startRecentsActivityInBackground(String reason)449 private void startRecentsActivityInBackground(String reason) { 450 final ActivityOptions options = ActivityOptions.makeBasic(); 451 options.setLaunchActivityType(mTargetActivityType); 452 options.setAvoidMoveToFront(); 453 mTargetIntent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_NO_ANIMATION); 454 455 mActivityStartController 456 .obtainStarter(mTargetIntent, reason) 457 .setCallingUid(mRecentsUid) 458 .setCallingPackage(mRecentsComponent.getPackageName()) 459 .setCallingFeatureId(mRecentsFeatureId) 460 .setActivityOptions(new SafeActivityOptions(options)) 461 .setUserId(mUserId) 462 .execute(); 463 } 464 465 /** 466 * Called only when the animation should be canceled prior to starting. 467 */ notifyAnimationCancelBeforeStart(IRecentsAnimationRunner recentsAnimationRunner)468 static void notifyAnimationCancelBeforeStart(IRecentsAnimationRunner recentsAnimationRunner) { 469 try { 470 recentsAnimationRunner.onAnimationCanceled(null /* taskSnapshot */); 471 } catch (RemoteException e) { 472 Slog.e(TAG, "Failed to cancel recents animation before start", e); 473 } 474 } 475 476 /** 477 * @return The top root task that is not always-on-top. 478 */ getTopNonAlwaysOnTopRootTask()479 private Task getTopNonAlwaysOnTopRootTask() { 480 return mDefaultTaskDisplayArea.getRootTask(task -> 481 !task.getWindowConfiguration().isAlwaysOnTop()); 482 } 483 484 /** 485 * @return the top activity in the {@param targetRootTask} matching the {@param component}, 486 * or just the top activity of the top task if no task matches the component. 487 */ getTargetActivity(Task targetRootTask)488 private ActivityRecord getTargetActivity(Task targetRootTask) { 489 if (targetRootTask == null) { 490 return null; 491 } 492 493 final PooledPredicate p = PooledLambda.obtainPredicate(RecentsAnimation::matchesTarget, 494 this, PooledLambda.__(Task.class)); 495 final Task task = targetRootTask.getTask(p); 496 p.recycle(); 497 return task != null ? task.getTopNonFinishingActivity() : null; 498 } 499 matchesTarget(Task task)500 private boolean matchesTarget(Task task) { 501 return task.mUserId == mUserId 502 && task.getBaseIntent().getComponent().equals(mTargetIntent.getComponent()); 503 } 504 } 505