1 /* 2 * Copyright (C) 2019 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 package com.android.car.am; 17 18 import static android.app.ActivityTaskManager.INVALID_TASK_ID; 19 import static android.os.Process.INVALID_UID; 20 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG; 21 22 import static com.android.car.CarLog.TAG_AM; 23 24 import android.annotation.NonNull; 25 import android.annotation.Nullable; 26 import android.annotation.UserIdInt; 27 import android.app.ActivityManager; 28 import android.app.ActivityOptions; 29 import android.app.ActivityTaskManager.RootTaskInfo; 30 import android.app.IActivityManager; 31 import android.app.IProcessObserver; 32 import android.app.Presentation; 33 import android.app.TaskStackListener; 34 import android.car.hardware.power.CarPowerManager; 35 import android.car.user.CarUserManager; 36 import android.car.user.CarUserManager.UserLifecycleListener; 37 import android.content.BroadcastReceiver; 38 import android.content.ComponentName; 39 import android.content.Context; 40 import android.content.Intent; 41 import android.content.IntentFilter; 42 import android.content.pm.ActivityInfo; 43 import android.content.pm.PackageInfo; 44 import android.content.pm.PackageManager; 45 import android.hardware.display.DisplayManager; 46 import android.net.Uri; 47 import android.os.HandlerThread; 48 import android.os.RemoteException; 49 import android.os.SystemClock; 50 import android.os.UserHandle; 51 import android.os.UserManager; 52 import android.util.IndentingPrintWriter; 53 import android.util.Log; 54 import android.util.Slog; 55 import android.util.SparseArray; 56 import android.view.Display; 57 58 import com.android.car.CarLocalServices; 59 import com.android.car.CarServiceBase; 60 import com.android.car.CarServiceUtils; 61 import com.android.car.R; 62 import com.android.car.user.CarUserService; 63 import com.android.internal.annotations.GuardedBy; 64 import com.android.internal.annotations.VisibleForTesting; 65 66 import java.util.List; 67 68 /** 69 * Monitors top activity for a display and guarantee activity in fixed mode is re-launched if it has 70 * crashed or gone to background for whatever reason. 71 * 72 * <p>This component also monitors the upddate of the target package and re-launch it once 73 * update is complete.</p> 74 */ 75 public final class FixedActivityService implements CarServiceBase { 76 77 private static final boolean DBG = false; 78 79 private static final long RECHECK_INTERVAL_MS = 500; 80 private static final int MAX_NUMBER_OF_CONSECUTIVE_CRASH_RETRY = 5; 81 // If process keep running without crashing, will reset consecutive crash counts. 82 private static final long CRASH_FORGET_INTERVAL_MS = 2 * 60 * 1000; // 2 mins 83 84 private static class RunningActivityInfo { 85 @NonNull 86 public final Intent intent; 87 88 @NonNull 89 public final ActivityOptions activityOptions; 90 91 @UserIdInt 92 public final int userId; 93 94 @GuardedBy("mLock") 95 public boolean isVisible; 96 @GuardedBy("mLock") 97 public long lastLaunchTimeMs = 0; 98 @GuardedBy("mLock") 99 public int consecutiveRetries = 0; 100 @GuardedBy("mLock") 101 public int taskId = INVALID_TASK_ID; 102 @GuardedBy("mLock") 103 public int previousTaskId = INVALID_TASK_ID; 104 @GuardedBy("mLock") 105 public boolean inBackground; 106 @GuardedBy("mLock") 107 public boolean failureLogged; 108 RunningActivityInfo(@onNull Intent intent, @NonNull ActivityOptions activityOptions, @UserIdInt int userId)109 RunningActivityInfo(@NonNull Intent intent, @NonNull ActivityOptions activityOptions, 110 @UserIdInt int userId) { 111 this.intent = intent; 112 this.activityOptions = activityOptions; 113 this.userId = userId; 114 } 115 resetCrashCounterLocked()116 private void resetCrashCounterLocked() { 117 consecutiveRetries = 0; 118 failureLogged = false; 119 } 120 121 @Override toString()122 public String toString() { 123 return "RunningActivityInfo{intent:" + intent + ",activityOptions:" + activityOptions 124 + ",userId:" + userId + ",isVisible:" + isVisible 125 + ",lastLaunchTimeMs:" + lastLaunchTimeMs 126 + ",consecutiveRetries:" + consecutiveRetries + ",taskId:" + taskId + "}"; 127 } 128 } 129 130 private final Context mContext; 131 132 private final IActivityManager mAm; 133 134 private final DisplayManager mDm; 135 136 private final UserManager mUm; 137 138 private final UserLifecycleListener mUserLifecycleListener = event -> { 139 if (Log.isLoggable(TAG_AM, Log.DEBUG)) { 140 Slog.d(TAG_AM, "onEvent(" + event + ")"); 141 } 142 if (CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING == event.getEventType()) { 143 synchronized (FixedActivityService.this.mLock) { 144 clearRunningActivitiesLocked(); 145 } 146 } 147 }; 148 149 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 150 @Override 151 public void onReceive(Context context, Intent intent) { 152 String action = intent.getAction(); 153 if (Intent.ACTION_PACKAGE_CHANGED.equals(action) 154 || Intent.ACTION_PACKAGE_REPLACED.equals(action) 155 || Intent.ACTION_PACKAGE_ADDED.equals(action) 156 || Intent.ACTION_PACKAGE_REMOVED.equals(action)) { 157 Uri packageData = intent.getData(); 158 if (packageData == null) { 159 Slog.w(TAG_AM, "null packageData"); 160 return; 161 } 162 String packageName = packageData.getSchemeSpecificPart(); 163 if (packageName == null) { 164 Slog.w(TAG_AM, "null packageName"); 165 return; 166 } 167 int uid = intent.getIntExtra(Intent.EXTRA_UID, INVALID_UID); 168 int userId = UserHandle.getUserId(uid); 169 boolean tryLaunch = false; 170 synchronized (mLock) { 171 for (int i = 0; i < mRunningActivities.size(); i++) { 172 RunningActivityInfo info = mRunningActivities.valueAt(i); 173 // Should do this for all activities as it can happen for multiple 174 // displays. Package name is ignored as one package can affect 175 // others. 176 if (info.userId == userId) { 177 Slog.i(TAG_AM, "Package changed:" + packageName 178 + ",user:" + userId + ",action:" + action); 179 info.resetCrashCounterLocked(); 180 tryLaunch = true; 181 break; 182 } 183 } 184 } 185 if (tryLaunch) { 186 launchIfNecessary(); 187 } 188 } 189 } 190 }; 191 192 // It says listener but is actually callback. 193 private final TaskStackListener mTaskStackListener = new TaskStackListener() { 194 @Override 195 public void onTaskStackChanged() { 196 launchIfNecessary(); 197 } 198 199 @Override 200 public void onTaskCreated(int taskId, ComponentName componentName) { 201 launchIfNecessary(); 202 } 203 204 @Override 205 public void onTaskRemoved(int taskId) { 206 launchIfNecessary(); 207 } 208 209 @Override 210 public void onTaskMovedToFront(int taskId) { 211 launchIfNecessary(); 212 } 213 214 @Override 215 public void onTaskRemovalStarted(int taskId) { 216 launchIfNecessary(); 217 } 218 }; 219 220 221 private final IProcessObserver mProcessObserver = new IProcessObserver.Stub() { 222 @Override 223 public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) { 224 launchIfNecessary(); 225 } 226 227 @Override 228 public void onForegroundServicesChanged(int pid, int uid, int fgServiceTypes) { 229 // ignore 230 } 231 232 @Override 233 public void onProcessDied(int pid, int uid) { 234 launchIfNecessary(); 235 } 236 }; 237 238 private final HandlerThread mHandlerThread; 239 240 private final Runnable mActivityCheckRunnable = () -> { 241 launchIfNecessary(); 242 }; 243 244 private final Object mLock = new Object(); 245 246 // key: displayId 247 @GuardedBy("mLock") 248 private final SparseArray<RunningActivityInfo> mRunningActivities = 249 new SparseArray<>(/* capacity= */ 1); // default to one cluster only case 250 251 @GuardedBy("mLock") 252 private final SparseArray<Presentation> mBlockingPresentations = new SparseArray<>(1); 253 254 @GuardedBy("mLock") 255 private boolean mEventMonitoringActive; 256 257 @GuardedBy("mLock") 258 private CarPowerManager mCarPowerManager; 259 260 private final CarPowerManager.CarPowerStateListener mCarPowerStateListener = (state) -> { 261 if (state != CarPowerManager.CarPowerStateListener.ON) { 262 return; 263 } 264 synchronized (mLock) { 265 for (int i = 0; i < mRunningActivities.size(); i++) { 266 RunningActivityInfo info = mRunningActivities.valueAt(i); 267 info.resetCrashCounterLocked(); 268 } 269 } 270 launchIfNecessary(); 271 }; 272 FixedActivityService(Context context)273 public FixedActivityService(Context context) { 274 this(context, ActivityManager.getService(), context.getSystemService(UserManager.class), 275 context.getSystemService(DisplayManager.class)); 276 } 277 FixedActivityService(Context context, IActivityManager activityManager, UserManager userManager, DisplayManager displayManager)278 FixedActivityService(Context context, IActivityManager activityManager, 279 UserManager userManager, DisplayManager displayManager) { 280 mContext = context; 281 mAm = activityManager; 282 mUm = userManager; 283 mDm = displayManager; 284 mHandlerThread = CarServiceUtils.getHandlerThread( 285 FixedActivityService.class.getSimpleName()); 286 } 287 288 @Override init()289 public void init() { 290 // nothing to do 291 } 292 293 @Override release()294 public void release() { 295 stopMonitoringEvents(); 296 } 297 298 @Override dump(IndentingPrintWriter writer)299 public void dump(IndentingPrintWriter writer) { 300 writer.println("*FixedActivityService*"); 301 synchronized (mLock) { 302 writer.println("mRunningActivities:" + mRunningActivities 303 + " ,mEventMonitoringActive:" + mEventMonitoringActive); 304 writer.println("mBlockingPresentations:"); 305 for (int i = 0; i < mBlockingPresentations.size(); i++) { 306 Presentation p = mBlockingPresentations.valueAt(i); 307 if (p == null) { 308 continue; 309 } 310 writer.println("display:" + mBlockingPresentations.keyAt(i) 311 + " showing:" + p.isShowing()); 312 } 313 } 314 } 315 clearRunningActivitiesLocked()316 private void clearRunningActivitiesLocked() { 317 for (int i = mRunningActivities.size() - 1; i >= 0; i--) { 318 RunningActivityInfo info = mRunningActivities.valueAt(i); 319 if (info == null || !isUserAllowedToLaunchActivity(info.userId)) { 320 mRunningActivities.removeAt(i); 321 } 322 } 323 } 324 postRecheck(long delayMs)325 private void postRecheck(long delayMs) { 326 mHandlerThread.getThreadHandler().postDelayed(mActivityCheckRunnable, delayMs); 327 } 328 startMonitoringEvents()329 private void startMonitoringEvents() { 330 CarPowerManager carPowerManager; 331 synchronized (mLock) { 332 if (mEventMonitoringActive) { 333 return; 334 } 335 mEventMonitoringActive = true; 336 carPowerManager = CarLocalServices.createCarPowerManager(mContext); 337 mCarPowerManager = carPowerManager; 338 } 339 CarUserService userService = CarLocalServices.getService(CarUserService.class); 340 userService.addUserLifecycleListener(mUserLifecycleListener); 341 IntentFilter filter = new IntentFilter(); 342 filter.addAction(Intent.ACTION_PACKAGE_CHANGED); 343 filter.addAction(Intent.ACTION_PACKAGE_REPLACED); 344 filter.addAction(Intent.ACTION_PACKAGE_ADDED); 345 filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 346 filter.addDataScheme("package"); 347 mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, 348 /* broadcastPermission= */ null, /* scheduler= */ null); 349 try { 350 mAm.registerTaskStackListener(mTaskStackListener); 351 mAm.registerProcessObserver(mProcessObserver); 352 } catch (RemoteException e) { 353 Slog.e(TAG_AM, "remote exception from AM", e); 354 } 355 try { 356 carPowerManager.setListener(mCarPowerStateListener); 357 } catch (Exception e) { 358 // should not happen 359 Slog.e(TAG_AM, "Got exception from CarPowerManager", e); 360 } 361 } 362 stopMonitoringEvents()363 private void stopMonitoringEvents() { 364 CarPowerManager carPowerManager; 365 synchronized (mLock) { 366 if (!mEventMonitoringActive) { 367 return; 368 } 369 mEventMonitoringActive = false; 370 carPowerManager = mCarPowerManager; 371 mCarPowerManager = null; 372 } 373 if (carPowerManager != null) { 374 carPowerManager.clearListener(); 375 } 376 mHandlerThread.getThreadHandler().removeCallbacks(mActivityCheckRunnable); 377 CarUserService userService = CarLocalServices.getService(CarUserService.class); 378 userService.removeUserLifecycleListener(mUserLifecycleListener); 379 try { 380 mAm.unregisterTaskStackListener(mTaskStackListener); 381 mAm.unregisterProcessObserver(mProcessObserver); 382 } catch (RemoteException e) { 383 Slog.e(TAG_AM, "remote exception from AM", e); 384 } 385 mContext.unregisterReceiver(mBroadcastReceiver); 386 } 387 388 @Nullable getRootTaskInfos()389 private List<RootTaskInfo> getRootTaskInfos() { 390 try { 391 return mAm.getAllRootTaskInfos(); 392 } catch (RemoteException e) { 393 Slog.e(TAG_AM, "remote exception from AM", e); 394 } 395 return null; 396 } 397 398 /** 399 * Launches all stored fixed mode activities if necessary. 400 * @param displayId Display id to check if it is visible. If check is not necessary, should pass 401 * {@link Display#INVALID_DISPLAY}. 402 * @return true if fixed Activity for given {@code displayId} is visible / successfully 403 * launched. It will return false for {@link Display#INVALID_DISPLAY} {@code displayId}. 404 */ launchIfNecessary(int displayId)405 private boolean launchIfNecessary(int displayId) { 406 List<RootTaskInfo> infos = getRootTaskInfos(); 407 if (infos == null) { 408 Slog.e(TAG_AM, "cannot get RootTaskInfo from AM"); 409 return false; 410 } 411 long now = SystemClock.elapsedRealtime(); 412 synchronized (mLock) { 413 if (mRunningActivities.size() == 0) { 414 // it must have been stopped. 415 if (DBG) { 416 Slog.i(TAG_AM, "empty activity list", new RuntimeException()); 417 } 418 return false; 419 } 420 for (int i = mRunningActivities.size() - 1; i >= 0; i--) { 421 int displayIdForActivity = mRunningActivities.keyAt(i); 422 Display display = mDm.getDisplay(displayIdForActivity); 423 if (display == null) { 424 Slog.e(TAG_AM, "Stop fixed activity for non-available display" 425 + displayIdForActivity); 426 mRunningActivities.removeAt(i); 427 continue; 428 } 429 430 RunningActivityInfo activityInfo = mRunningActivities.valueAt(i); 431 activityInfo.isVisible = false; 432 if (isUserAllowedToLaunchActivity(activityInfo.userId)) { 433 continue; 434 } 435 if (activityInfo.taskId != INVALID_TASK_ID) { 436 Slog.i(TAG_AM, "Finishing fixed activity on user switching:" 437 + activityInfo); 438 try { 439 mAm.removeTask(activityInfo.taskId); 440 } catch (RemoteException e) { 441 Slog.e(TAG_AM, "remote exception from AM", e); 442 } 443 CarServiceUtils.runOnMain(() -> { 444 Presentation p = new Presentation(mContext, display, 445 android.R.style.Theme_Black_NoTitleBar_Fullscreen, 446 // TYPE_PRESENTATION can't be used in the internal display. 447 // Select TYPE_KEYGUARD_DIALOG, since it's used in 448 // {@Code KeyguardDisplayManager.KeyguardPresentation}. 449 TYPE_KEYGUARD_DIALOG); 450 p.setContentView(R.layout.activity_continuous_blank); 451 synchronized (mLock) { 452 RunningActivityInfo info = mRunningActivities.get(displayIdForActivity); 453 if (info != null && info.userId == ActivityManager.getCurrentUser()) { 454 Slog.i(TAG_AM, "Do not show Presentation, new req already made"); 455 return; 456 } 457 mBlockingPresentations.append(displayIdForActivity, p); 458 } 459 p.show(); 460 }); 461 } 462 mRunningActivities.removeAt(i); 463 } 464 for (RootTaskInfo taskInfo : infos) { 465 RunningActivityInfo activityInfo = mRunningActivities.get(taskInfo.displayId); 466 if (activityInfo == null) { 467 continue; 468 } 469 int topUserId = taskInfo.childTaskUserIds[taskInfo.childTaskUserIds.length - 1]; 470 if (activityInfo.intent.getComponent().equals(taskInfo.topActivity) 471 && activityInfo.userId == topUserId && taskInfo.visible) { 472 // top one is matching. 473 activityInfo.isVisible = true; 474 activityInfo.taskId = taskInfo.childTaskIds[taskInfo.childTaskIds.length - 1]; 475 continue; 476 } 477 activityInfo.previousTaskId = 478 taskInfo.childTaskIds[taskInfo.childTaskIds.length - 1]; 479 Slog.i(TAG_AM, "Unmatched top activity will be removed:" 480 + taskInfo.topActivity + " top task id:" + activityInfo.previousTaskId 481 + " user:" + topUserId + " display:" + taskInfo.displayId); 482 activityInfo.inBackground = false; 483 for (int i = 0; i < taskInfo.childTaskIds.length - 1; i++) { 484 if (activityInfo.taskId == taskInfo.childTaskIds[i]) { 485 activityInfo.inBackground = true; 486 } 487 } 488 if (!activityInfo.inBackground) { 489 activityInfo.taskId = INVALID_TASK_ID; 490 } 491 } 492 493 for (int i = 0; i < mRunningActivities.size(); i++) { 494 RunningActivityInfo activityInfo = mRunningActivities.valueAt(i); 495 long timeSinceLastLaunchMs = now - activityInfo.lastLaunchTimeMs; 496 if (activityInfo.isVisible) { 497 if (timeSinceLastLaunchMs >= CRASH_FORGET_INTERVAL_MS) { 498 activityInfo.consecutiveRetries = 0; 499 } 500 continue; 501 } 502 if (!isComponentAvailable(activityInfo.intent.getComponent(), 503 activityInfo.userId)) { 504 continue; 505 } 506 // For 1st call (consecutiveRetries == 0), do not wait as there can be no posting 507 // for recheck. 508 if (activityInfo.consecutiveRetries > 0 && (timeSinceLastLaunchMs 509 < RECHECK_INTERVAL_MS)) { 510 // wait until next check interval comes. 511 continue; 512 } 513 if (activityInfo.consecutiveRetries >= MAX_NUMBER_OF_CONSECUTIVE_CRASH_RETRY) { 514 // re-tried too many times, give up for now. 515 if (!activityInfo.failureLogged) { 516 activityInfo.failureLogged = true; 517 Slog.w(TAG_AM, "Too many relaunch failure of fixed activity:" 518 + activityInfo); 519 } 520 continue; 521 } 522 523 Slog.i(TAG_AM, "Launching Activity for fixed mode. Intent:" + activityInfo.intent 524 + ",userId:" + UserHandle.of(activityInfo.userId) + ",displayId:" 525 + mRunningActivities.keyAt(i)); 526 // Increase retry count if task is not in background. In case like other app is 527 // launched and the target activity is still in background, do not consider it 528 // as retry. 529 if (!activityInfo.inBackground) { 530 activityInfo.consecutiveRetries++; 531 } 532 try { 533 postRecheck(RECHECK_INTERVAL_MS); 534 postRecheck(CRASH_FORGET_INTERVAL_MS); 535 mContext.startActivityAsUser(activityInfo.intent, 536 activityInfo.activityOptions.toBundle(), 537 UserHandle.of(activityInfo.userId)); 538 activityInfo.isVisible = true; 539 activityInfo.lastLaunchTimeMs = SystemClock.elapsedRealtime(); 540 } catch (Exception e) { // Catch all for any app related issues. 541 Slog.w(TAG_AM, "Cannot start activity:" + activityInfo.intent, e); 542 } 543 } 544 RunningActivityInfo activityInfo = mRunningActivities.get(displayId); 545 if (activityInfo == null) { 546 return false; 547 } 548 return activityInfo.isVisible; 549 } 550 } 551 552 @VisibleForTesting launchIfNecessary()553 void launchIfNecessary() { 554 launchIfNecessary(Display.INVALID_DISPLAY); 555 } 556 logComponentNotFound(ComponentName component, @UserIdInt int userId, Exception e)557 private void logComponentNotFound(ComponentName component, @UserIdInt int userId, 558 Exception e) { 559 Slog.e(TAG_AM, "Specified Component not found:" + component 560 + " for userid:" + userId, e); 561 } 562 isComponentAvailable(ComponentName component, @UserIdInt int userId)563 private boolean isComponentAvailable(ComponentName component, @UserIdInt int userId) { 564 PackageInfo packageInfo; 565 try { 566 packageInfo = mContext.getPackageManager().getPackageInfoAsUser( 567 component.getPackageName(), PackageManager.GET_ACTIVITIES, userId); 568 } catch (PackageManager.NameNotFoundException e) { 569 logComponentNotFound(component, userId, e); 570 return false; 571 } 572 if (packageInfo == null || packageInfo.activities == null) { 573 // may not be necessary but additional safety check 574 logComponentNotFound(component, userId, new RuntimeException()); 575 return false; 576 } 577 String fullName = component.getClassName(); 578 String shortName = component.getShortClassName(); 579 for (ActivityInfo info : packageInfo.activities) { 580 if (info.name.equals(fullName) || info.name.equals(shortName)) { 581 return true; 582 } 583 } 584 logComponentNotFound(component, userId, new RuntimeException()); 585 return false; 586 } 587 isUserAllowedToLaunchActivity(@serIdInt int userId)588 private boolean isUserAllowedToLaunchActivity(@UserIdInt int userId) { 589 int currentUser = ActivityManager.getCurrentUser(); 590 if (userId == currentUser || userId == UserHandle.USER_SYSTEM) { 591 return true; 592 } 593 int[] profileIds = mUm.getEnabledProfileIds(currentUser); 594 // null can happen in test env when UserManager is mocked. So this check is not necessary 595 // in real env but add it to make test impl easier. 596 if (profileIds == null) { 597 return false; 598 } 599 for (int id : profileIds) { 600 if (id == userId) { 601 return true; 602 } 603 } 604 return false; 605 } 606 isDisplayAllowedForFixedMode(int displayId)607 private boolean isDisplayAllowedForFixedMode(int displayId) { 608 if (displayId == Display.DEFAULT_DISPLAY || displayId == Display.INVALID_DISPLAY) { 609 Slog.w(TAG_AM, "Target display cannot be used for fixed mode, displayId:" + displayId, 610 new RuntimeException()); 611 return false; 612 } 613 return true; 614 } 615 616 @VisibleForTesting getRunningFixedActivity(int displayId)617 RunningActivityInfo getRunningFixedActivity(int displayId) { 618 synchronized (mLock) { 619 return mRunningActivities.get(displayId); 620 } 621 } 622 623 /** 624 * Checks {@link InstrumentClusterRenderingService#startFixedActivityModeForDisplayAndUser( 625 * Intent, ActivityOptions, int)} 626 */ startFixedActivityModeForDisplayAndUser(@onNull Intent intent, @NonNull ActivityOptions options, int displayId, @UserIdInt int userId)627 public boolean startFixedActivityModeForDisplayAndUser(@NonNull Intent intent, 628 @NonNull ActivityOptions options, int displayId, @UserIdInt int userId) { 629 if (!isDisplayAllowedForFixedMode(displayId)) { 630 return false; 631 } 632 if (options == null) { 633 Slog.e(TAG_AM, "startFixedActivityModeForDisplayAndUser, null options"); 634 return false; 635 } 636 if (!isUserAllowedToLaunchActivity(userId)) { 637 Slog.e(TAG_AM, "startFixedActivityModeForDisplayAndUser, requested user:" + userId 638 + " cannot launch activity, Intent:" + intent); 639 return false; 640 } 641 ComponentName component = intent.getComponent(); 642 if (component == null) { 643 Slog.e(TAG_AM, 644 "startFixedActivityModeForDisplayAndUser: No component specified for " 645 + "requested Intent" 646 + intent); 647 return false; 648 } 649 if (!isComponentAvailable(component, userId)) { 650 return false; 651 } 652 boolean startMonitoringEvents = false; 653 synchronized (mLock) { 654 Presentation p = mBlockingPresentations.removeReturnOld(displayId); 655 if (p != null) { 656 p.dismiss(); 657 } 658 if (mRunningActivities.size() == 0) { 659 startMonitoringEvents = true; 660 } 661 RunningActivityInfo activityInfo = mRunningActivities.get(displayId); 662 boolean replaceEntry = true; 663 if (activityInfo != null && activityInfo.intent.equals(intent) 664 && options.equals(activityInfo.activityOptions) 665 && userId == activityInfo.userId) { 666 replaceEntry = false; 667 if (activityInfo.isVisible) { // already shown. 668 return true; 669 } 670 } 671 if (replaceEntry) { 672 activityInfo = new RunningActivityInfo(intent, options, userId); 673 mRunningActivities.put(displayId, activityInfo); 674 } 675 } 676 boolean launched = launchIfNecessary(displayId); 677 if (!launched) { 678 synchronized (mLock) { 679 mRunningActivities.remove(displayId); 680 } 681 } 682 // If first trial fails, let client know and do not retry as it can be wrong setting. 683 if (startMonitoringEvents && launched) { 684 startMonitoringEvents(); 685 } 686 return launched; 687 } 688 689 /** Check {@link InstrumentClusterRenderingService#stopFixedActivityMode(int)} */ stopFixedActivityMode(int displayId)690 public void stopFixedActivityMode(int displayId) { 691 if (!isDisplayAllowedForFixedMode(displayId)) { 692 return; 693 } 694 boolean stopMonitoringEvents = false; 695 synchronized (mLock) { 696 mRunningActivities.remove(displayId); 697 if (mRunningActivities.size() == 0) { 698 stopMonitoringEvents = true; 699 } 700 } 701 if (stopMonitoringEvents) { 702 stopMonitoringEvents(); 703 } 704 } 705 } 706