blob: d3e3c6f8d9cb0bbcfb9911c565ba8d27a9a079ae [file] [log] [blame]
Keun young Parkb5fab732019-10-03 10:51:12 -07001/*
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 */
16package com.android.car.am;
17
Keun young Park69a21042019-10-15 10:34:11 -070018import static android.app.ActivityTaskManager.INVALID_TASK_ID;
19import static android.os.Process.INVALID_UID;
20
Keun young Parkb5fab732019-10-03 10:51:12 -070021import static com.android.car.CarLog.TAG_AM;
22
23import android.annotation.NonNull;
24import android.annotation.Nullable;
25import android.annotation.UserIdInt;
26import android.app.ActivityManager;
27import android.app.ActivityManager.StackInfo;
28import android.app.ActivityOptions;
29import android.app.IActivityManager;
30import android.app.IProcessObserver;
31import android.app.TaskStackListener;
Keun young Park69a21042019-10-15 10:34:11 -070032import android.car.hardware.power.CarPowerManager;
Keun young Parkb5fab732019-10-03 10:51:12 -070033import android.content.BroadcastReceiver;
34import android.content.ComponentName;
35import android.content.Context;
36import android.content.Intent;
37import android.content.IntentFilter;
38import android.content.pm.ActivityInfo;
39import android.content.pm.PackageInfo;
40import android.content.pm.PackageManager;
Keun young Park69a21042019-10-15 10:34:11 -070041import android.net.Uri;
42import android.os.HandlerThread;
Keun young Parkb5fab732019-10-03 10:51:12 -070043import android.os.RemoteException;
Keun young Park69a21042019-10-15 10:34:11 -070044import android.os.SystemClock;
Keun young Parkb5fab732019-10-03 10:51:12 -070045import android.os.UserHandle;
46import android.os.UserManager;
47import android.util.Log;
48import android.util.SparseArray;
49import android.view.Display;
50
51import com.android.car.CarLocalServices;
52import com.android.car.CarServiceBase;
53import com.android.car.user.CarUserService;
54import com.android.internal.annotations.GuardedBy;
55
56import java.io.PrintWriter;
57import java.util.List;
58
59/**
60 * Monitors top activity for a display and guarantee activity in fixed mode is re-launched if it has
61 * crashed or gone to background for whatever reason.
62 *
63 * <p>This component also monitors the upddate of the target package and re-launch it once
64 * update is complete.</p>
65 */
66public final class FixedActivityService implements CarServiceBase {
67
68 private static final boolean DBG = false;
69
Keun young Park69a21042019-10-15 10:34:11 -070070 private static final long RECHECK_INTERVAL_MS = 500;
71 private static final int MAX_NUMBER_OF_CONSECUTIVE_CRASH_RETRY = 5;
72 // If process keep running without crashing, will reset consecutive crash counts.
73 private static final long CRASH_FORGET_INTERVAL_MS = 2 * 60 * 1000; // 2 mins
74
Keun young Parkb5fab732019-10-03 10:51:12 -070075 private static class RunningActivityInfo {
76 @NonNull
77 public final Intent intent;
78
79 @NonNull
80 public final ActivityOptions activityOptions;
81
82 @UserIdInt
83 public final int userId;
84
Keun young Park69a21042019-10-15 10:34:11 -070085 @GuardedBy("mLock")
Keun young Parkb5fab732019-10-03 10:51:12 -070086 public boolean isVisible;
Keun young Park69a21042019-10-15 10:34:11 -070087 @GuardedBy("mLock")
88 public long lastLaunchTimeMs = 0;
89 @GuardedBy("mLock")
90 public int consecutiveRetries = 0;
91 @GuardedBy("mLock")
92 public int taskId = INVALID_TASK_ID;
93 @GuardedBy("mLock")
94 public int previousTaskId = INVALID_TASK_ID;
95 @GuardedBy("mLock")
96 public boolean inBackground;
97 @GuardedBy("mLock")
98 public boolean failureLogged;
Keun young Parkb5fab732019-10-03 10:51:12 -070099
100 RunningActivityInfo(@NonNull Intent intent, @NonNull ActivityOptions activityOptions,
101 @UserIdInt int userId) {
102 this.intent = intent;
103 this.activityOptions = activityOptions;
104 this.userId = userId;
105 }
106
Keun young Park69a21042019-10-15 10:34:11 -0700107 private void resetCrashCounterLocked() {
108 consecutiveRetries = 0;
109 failureLogged = false;
110 }
111
Keun young Parkb5fab732019-10-03 10:51:12 -0700112 @Override
113 public String toString() {
114 return "RunningActivityInfo{intent:" + intent + ",activityOptions:" + activityOptions
Keun young Park69a21042019-10-15 10:34:11 -0700115 + ",userId:" + userId + ",isVisible:" + isVisible
116 + ",lastLaunchTimeMs:" + lastLaunchTimeMs
117 + ",consecutiveRetries:" + consecutiveRetries + ",taskId:" + taskId + "}";
Keun young Parkb5fab732019-10-03 10:51:12 -0700118 }
119 }
120
121 private final Context mContext;
122
123 private final IActivityManager mAm;
124
125 private final UserManager mUm;
126
127 private final CarUserService.UserCallback mUserCallback = new CarUserService.UserCallback() {
128 @Override
129 public void onUserLockChanged(@UserIdInt int userId, boolean unlocked) {
130 // Nothing to do
131 }
132
133 @Override
134 public void onSwitchUser(@UserIdInt int userId) {
135 synchronized (mLock) {
136 mRunningActivities.clear();
137 }
138 }
139 };
140
141 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
142 @Override
143 public void onReceive(Context context, Intent intent) {
Keun young Park69a21042019-10-15 10:34:11 -0700144 String action = intent.getAction();
Keun young Parkb5fab732019-10-03 10:51:12 -0700145 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
146 || Intent.ACTION_PACKAGE_REPLACED.equals(
147 action)) {
Keun young Park69a21042019-10-15 10:34:11 -0700148 Uri packageData = intent.getData();
149 if (packageData == null) {
150 Log.w(TAG_AM, "null packageData");
151 return;
152 }
153 String packageName = packageData.getSchemeSpecificPart();
154 if (packageName == null) {
155 Log.w(TAG_AM, "null packageName");
156 return;
157 }
158 int uid = intent.getIntExtra(Intent.EXTRA_UID, INVALID_UID);
159 int userId = UserHandle.getUserId(uid);
160 boolean tryLaunch = false;
161 synchronized (mLock) {
162 for (int i = 0; i < mRunningActivities.size(); i++) {
163 RunningActivityInfo info = mRunningActivities.valueAt(i);
164 ComponentName component = info.intent.getComponent();
165 // should do this for all activities as the same package can cover multiple
166 // displays.
167 if (packageName.equals(component.getPackageName())
168 && info.userId == userId) {
169 Log.i(TAG_AM, "Package updated:" + packageName
170 + ",user:" + userId);
171 info.resetCrashCounterLocked();
172 tryLaunch = true;
173 }
174 }
175 }
176 if (tryLaunch) {
177 launchIfNecessary();
178 }
Keun young Parkb5fab732019-10-03 10:51:12 -0700179 }
180 }
181 };
182
183 // It says listener but is actually callback.
184 private final TaskStackListener mTaskStackListener = new TaskStackListener() {
185 @Override
186 public void onTaskStackChanged() {
187 launchIfNecessary();
188 }
Keun young Park69a21042019-10-15 10:34:11 -0700189
190 @Override
191 public void onTaskCreated(int taskId, ComponentName componentName) {
192 launchIfNecessary();
193 }
194
195 @Override
196 public void onTaskRemoved(int taskId) {
197 launchIfNecessary();
198 }
199
200 @Override
201 public void onTaskMovedToFront(int taskId) {
202 launchIfNecessary();
203 }
204
205 @Override
206 public void onTaskRemovalStarted(int taskId) {
207 launchIfNecessary();
208 }
Keun young Parkb5fab732019-10-03 10:51:12 -0700209 };
210
211 private final IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
212 @Override
213 public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
214 launchIfNecessary();
215 }
216
217 @Override
218 public void onForegroundServicesChanged(int pid, int uid, int fgServiceTypes) {
219 // ignore
220 }
221
222 @Override
223 public void onProcessDied(int pid, int uid) {
224 launchIfNecessary();
225 }
226 };
227
Keun young Park69a21042019-10-15 10:34:11 -0700228 private final HandlerThread mHandlerThread = new HandlerThread(
229 FixedActivityService.class.getSimpleName());
230
231 private final Runnable mActivityCheckRunnable = () -> {
232 launchIfNecessary();
233 };
234
Keun young Parkb5fab732019-10-03 10:51:12 -0700235 private final Object mLock = new Object();
236
237 // key: displayId
238 @GuardedBy("mLock")
239 private final SparseArray<RunningActivityInfo> mRunningActivities =
240 new SparseArray<>(/* capacity= */ 1); // default to one cluster only case
241
242 @GuardedBy("mLock")
243 private boolean mEventMonitoringActive;
244
Keun young Park69a21042019-10-15 10:34:11 -0700245 @GuardedBy("mLock")
246 private CarPowerManager mCarPowerManager;
247
248 private final CarPowerManager.CarPowerStateListener mCarPowerStateListener = (state) -> {
249 if (state != CarPowerManager.CarPowerStateListener.ON) {
250 return;
251 }
252 synchronized (mLock) {
253 for (int i = 0; i < mRunningActivities.size(); i++) {
254 RunningActivityInfo info = mRunningActivities.valueAt(i);
255 info.resetCrashCounterLocked();
256 }
257 }
258 launchIfNecessary();
259 };
260
Keun young Parkb5fab732019-10-03 10:51:12 -0700261 public FixedActivityService(Context context) {
262 mContext = context;
263 mAm = ActivityManager.getService();
264 mUm = context.getSystemService(UserManager.class);
Keun young Park69a21042019-10-15 10:34:11 -0700265 mHandlerThread.start();
Keun young Parkb5fab732019-10-03 10:51:12 -0700266 }
267
Keun young Parkb5fab732019-10-03 10:51:12 -0700268 @Override
269 public void init() {
270 // nothing to do
271 }
272
273 @Override
274 public void release() {
275 stopMonitoringEvents();
276 }
277
278 @Override
279 public void dump(PrintWriter writer) {
280 writer.println("*FixedActivityService*");
281 synchronized (mLock) {
282 writer.println("mRunningActivities:" + mRunningActivities
283 + " ,mEventMonitoringActive:" + mEventMonitoringActive);
284 }
285 }
286
Keun young Park69a21042019-10-15 10:34:11 -0700287 private void postRecheck(long delayMs) {
288 mHandlerThread.getThreadHandler().postDelayed(mActivityCheckRunnable, delayMs);
289 }
290
Keun young Parkb5fab732019-10-03 10:51:12 -0700291 private void startMonitoringEvents() {
Keun young Park69a21042019-10-15 10:34:11 -0700292 CarPowerManager carPowerManager;
Keun young Parkb5fab732019-10-03 10:51:12 -0700293 synchronized (mLock) {
294 if (mEventMonitoringActive) {
295 return;
296 }
297 mEventMonitoringActive = true;
Keun young Park69a21042019-10-15 10:34:11 -0700298 carPowerManager = CarLocalServices.createCarPowerManager(mContext);
299 mCarPowerManager = carPowerManager;
Keun young Parkb5fab732019-10-03 10:51:12 -0700300 }
301 CarUserService userService = CarLocalServices.getService(CarUserService.class);
302 userService.addUserCallback(mUserCallback);
303 IntentFilter filter = new IntentFilter();
304 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
305 filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
Keun young Park69a21042019-10-15 10:34:11 -0700306 filter.addDataScheme("package");
Keun young Parkb5fab732019-10-03 10:51:12 -0700307 mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter,
308 /* broadcastPermission= */ null, /* scheduler= */ null);
309 try {
310 mAm.registerTaskStackListener(mTaskStackListener);
311 mAm.registerProcessObserver(mProcessObserver);
312 } catch (RemoteException e) {
313 Log.e(TAG_AM, "remote exception from AM", e);
314 }
Keun young Park69a21042019-10-15 10:34:11 -0700315 try {
316 carPowerManager.setListener(mCarPowerStateListener);
317 } catch (Exception e) {
318 // should not happen
319 Log.e(TAG_AM, "Got exception from CarPowerManager", e);
320 }
Keun young Parkb5fab732019-10-03 10:51:12 -0700321 }
322
323 private void stopMonitoringEvents() {
Keun young Park69a21042019-10-15 10:34:11 -0700324 CarPowerManager carPowerManager;
Keun young Parkb5fab732019-10-03 10:51:12 -0700325 synchronized (mLock) {
326 if (!mEventMonitoringActive) {
327 return;
328 }
329 mEventMonitoringActive = false;
Keun young Park69a21042019-10-15 10:34:11 -0700330 carPowerManager = mCarPowerManager;
331 mCarPowerManager = null;
Keun young Parkb5fab732019-10-03 10:51:12 -0700332 }
Keun young Park69a21042019-10-15 10:34:11 -0700333 if (carPowerManager != null) {
334 carPowerManager.clearListener();
335 }
336 mHandlerThread.getThreadHandler().removeCallbacks(mActivityCheckRunnable);
Keun young Parkb5fab732019-10-03 10:51:12 -0700337 CarUserService userService = CarLocalServices.getService(CarUserService.class);
338 userService.removeUserCallback(mUserCallback);
339 try {
340 mAm.unregisterTaskStackListener(mTaskStackListener);
341 mAm.unregisterProcessObserver(mProcessObserver);
342 } catch (RemoteException e) {
343 Log.e(TAG_AM, "remote exception from AM", e);
344 }
Keun young Parked6ef412019-11-14 18:27:45 -0800345 mContext.unregisterReceiver(mBroadcastReceiver);
Keun young Parkb5fab732019-10-03 10:51:12 -0700346 }
347
348 @Nullable
349 private List<StackInfo> getStackInfos() {
350 try {
351 return mAm.getAllStackInfos();
352 } catch (RemoteException e) {
353 Log.e(TAG_AM, "remote exception from AM", e);
354 }
355 return null;
356 }
357
358 /**
359 * Launches all stored fixed mode activities if necessary.
360 * @param displayId Display id to check if it is visible. If check is not necessary, should pass
361 * {@link Display#INVALID_DISPLAY}.
362 * @return true if fixed Activity for given {@code displayId} is visible / successfully
363 * launched. It will return false for {@link Display#INVALID_DISPLAY} {@code displayId}.
364 */
365 private boolean launchIfNecessary(int displayId) {
366 List<StackInfo> infos = getStackInfos();
367 if (infos == null) {
368 Log.e(TAG_AM, "cannot get StackInfo from AM");
369 return false;
370 }
Keun young Park69a21042019-10-15 10:34:11 -0700371 long now = SystemClock.elapsedRealtime();
Keun young Parkb5fab732019-10-03 10:51:12 -0700372 synchronized (mLock) {
373 if (mRunningActivities.size() == 0) {
374 // it must have been stopped.
375 if (DBG) {
376 Log.i(TAG_AM, "empty activity list", new RuntimeException());
377 }
378 return false;
379 }
380 for (int i = 0; i < mRunningActivities.size(); i++) {
381 mRunningActivities.valueAt(i).isVisible = false;
382 }
383 for (StackInfo stackInfo : infos) {
384 RunningActivityInfo activityInfo = mRunningActivities.get(stackInfo.displayId);
385 if (activityInfo == null) {
386 continue;
387 }
388 int topUserId = stackInfo.taskUserIds[stackInfo.taskUserIds.length - 1];
389 if (activityInfo.intent.getComponent().equals(stackInfo.topActivity)
390 && activityInfo.userId == topUserId && stackInfo.visible) {
391 // top one is matching.
392 activityInfo.isVisible = true;
Keun young Park69a21042019-10-15 10:34:11 -0700393 activityInfo.taskId = stackInfo.taskIds[stackInfo.taskIds.length - 1];
Keun young Parkb5fab732019-10-03 10:51:12 -0700394 continue;
395 }
Keun young Park69a21042019-10-15 10:34:11 -0700396 activityInfo.previousTaskId = stackInfo.taskIds[stackInfo.taskIds.length - 1];
397 Log.i(TAG_AM, "Unmatched top activity will be removed:"
398 + stackInfo.topActivity + " top task id:" + activityInfo.previousTaskId
399 + " user:" + topUserId + " display:" + stackInfo.displayId);
400 activityInfo.inBackground = false;
401 for (int i = 0; i < stackInfo.taskIds.length - 1; i++) {
402 if (activityInfo.taskId == stackInfo.taskIds[i]) {
403 activityInfo.inBackground = true;
404 }
405 }
406 if (!activityInfo.inBackground) {
407 activityInfo.taskId = INVALID_TASK_ID;
Keun young Parkb5fab732019-10-03 10:51:12 -0700408 }
409 }
410 for (int i = 0; i < mRunningActivities.size(); i++) {
411 RunningActivityInfo activityInfo = mRunningActivities.valueAt(i);
Keun young Park69a21042019-10-15 10:34:11 -0700412 long timeSinceLastLaunchMs = now - activityInfo.lastLaunchTimeMs;
Keun young Parkb5fab732019-10-03 10:51:12 -0700413 if (activityInfo.isVisible) {
Keun young Park69a21042019-10-15 10:34:11 -0700414 if (timeSinceLastLaunchMs >= CRASH_FORGET_INTERVAL_MS) {
415 activityInfo.consecutiveRetries = 0;
416 }
Keun young Parkb5fab732019-10-03 10:51:12 -0700417 continue;
418 }
419 if (!isComponentAvailable(activityInfo.intent.getComponent(),
420 activityInfo.userId) || !isUserAllowedToLaunchActivity(
421 activityInfo.userId)) {
422 continue;
423 }
Keun young Park69a21042019-10-15 10:34:11 -0700424 // For 1st call (consecutiveRetries == 0), do not wait as there can be no posting
425 // for recheck.
426 if (activityInfo.consecutiveRetries > 0 && (timeSinceLastLaunchMs
427 < RECHECK_INTERVAL_MS)) {
428 // wait until next check interval comes.
429 continue;
430 }
431 if (activityInfo.consecutiveRetries >= MAX_NUMBER_OF_CONSECUTIVE_CRASH_RETRY) {
432 // re-tried too many times, give up for now.
433 if (!activityInfo.failureLogged) {
434 activityInfo.failureLogged = true;
435 Log.w(TAG_AM, "Too many relaunch failure of fixed activity:"
436 + activityInfo);
437 }
438 continue;
439 }
440
Keun young Parkb5fab732019-10-03 10:51:12 -0700441 Log.i(TAG_AM, "Launching Activity for fixed mode. Intent:" + activityInfo.intent
442 + ",userId:" + UserHandle.of(activityInfo.userId) + ",displayId:"
443 + mRunningActivities.keyAt(i));
Keun young Park69a21042019-10-15 10:34:11 -0700444 // Increase retry count if task is not in background. In case like other app is
445 // launched and the target activity is still in background, do not consider it
446 // as retry.
447 if (!activityInfo.inBackground) {
448 activityInfo.consecutiveRetries++;
449 }
Keun young Parkb5fab732019-10-03 10:51:12 -0700450 try {
Keun young Park69a21042019-10-15 10:34:11 -0700451 postRecheck(RECHECK_INTERVAL_MS);
452 postRecheck(CRASH_FORGET_INTERVAL_MS);
Keun young Parkb5fab732019-10-03 10:51:12 -0700453 mContext.startActivityAsUser(activityInfo.intent,
454 activityInfo.activityOptions.toBundle(),
455 UserHandle.of(activityInfo.userId));
456 activityInfo.isVisible = true;
Keun young Park69a21042019-10-15 10:34:11 -0700457 activityInfo.lastLaunchTimeMs = SystemClock.elapsedRealtime();
Keun young Parkb5fab732019-10-03 10:51:12 -0700458 } catch (Exception e) { // Catch all for any app related issues.
459 Log.w(TAG_AM, "Cannot start activity:" + activityInfo.intent, e);
460 }
461 }
462 RunningActivityInfo activityInfo = mRunningActivities.get(displayId);
463 if (activityInfo == null) {
464 return false;
465 }
466 return activityInfo.isVisible;
467 }
468 }
469
470 private void launchIfNecessary() {
471 launchIfNecessary(Display.INVALID_DISPLAY);
472 }
473
474 private void logComponentNotFound(ComponentName component, @UserIdInt int userId,
475 Exception e) {
476 Log.e(TAG_AM, "Specified Component not found:" + component
477 + " for userid:" + userId, e);
478 }
479
480 private boolean isComponentAvailable(ComponentName component, @UserIdInt int userId) {
481 PackageInfo packageInfo;
482 try {
483 packageInfo = mContext.getPackageManager().getPackageInfoAsUser(
484 component.getPackageName(), PackageManager.GET_ACTIVITIES, userId);
485 } catch (PackageManager.NameNotFoundException e) {
486 logComponentNotFound(component, userId, e);
487 return false;
488 }
489 if (packageInfo == null || packageInfo.activities == null) {
490 // may not be necessary but additional safety check
491 logComponentNotFound(component, userId, new RuntimeException());
492 return false;
493 }
494 String fullName = component.getClassName();
495 String shortName = component.getShortClassName();
496 for (ActivityInfo info : packageInfo.activities) {
497 if (info.name.equals(fullName) || info.name.equals(shortName)) {
498 return true;
499 }
500 }
501 logComponentNotFound(component, userId, new RuntimeException());
502 return false;
503 }
504
505 private boolean isUserAllowedToLaunchActivity(@UserIdInt int userId) {
506 int currentUser = ActivityManager.getCurrentUser();
507 if (userId == currentUser) {
508 return true;
509 }
510 int[] profileIds = mUm.getEnabledProfileIds(currentUser);
511 for (int id : profileIds) {
512 if (id == userId) {
513 return true;
514 }
515 }
516 return false;
517 }
518
519 private boolean isDisplayAllowedForFixedMode(int displayId) {
520 if (displayId == Display.DEFAULT_DISPLAY || displayId == Display.INVALID_DISPLAY) {
521 Log.w(TAG_AM, "Target display cannot be used for fixed mode, displayId:" + displayId,
522 new RuntimeException());
523 return false;
524 }
525 return true;
526 }
527
528 /**
Yuncheol Heo52b36d82019-10-25 16:32:59 -0700529 * Checks {@link InstrumentClusterRenderingService#startFixedActivityModeForDisplayAndUser(
Keun young Parkb5fab732019-10-03 10:51:12 -0700530 * Intent, ActivityOptions, int)}
531 */
532 public boolean startFixedActivityModeForDisplayAndUser(@NonNull Intent intent,
533 @NonNull ActivityOptions options, int displayId, @UserIdInt int userId) {
534 if (!isDisplayAllowedForFixedMode(displayId)) {
535 return false;
536 }
Keun young Park69a21042019-10-15 10:34:11 -0700537 if (options == null) {
538 Log.e(TAG_AM, "startFixedActivityModeForDisplayAndUser, null options");
539 return false;
540 }
Keun young Parkb5fab732019-10-03 10:51:12 -0700541 if (!isUserAllowedToLaunchActivity(userId)) {
542 Log.e(TAG_AM, "startFixedActivityModeForDisplayAndUser, requested user:" + userId
543 + " cannot launch activity, Intent:" + intent);
544 return false;
545 }
546 ComponentName component = intent.getComponent();
547 if (component == null) {
548 Log.e(TAG_AM,
549 "startFixedActivityModeForDisplayAndUser: No component specified for "
550 + "requested Intent"
551 + intent);
552 return false;
553 }
554 if (!isComponentAvailable(component, userId)) {
555 return false;
556 }
557 boolean startMonitoringEvents = false;
558 synchronized (mLock) {
559 if (mRunningActivities.size() == 0) {
560 startMonitoringEvents = true;
561 }
562 RunningActivityInfo activityInfo = mRunningActivities.get(displayId);
Keun young Park69a21042019-10-15 10:34:11 -0700563 boolean replaceEntry = true;
564 if (activityInfo != null && activityInfo.intent.equals(intent)
565 && options.equals(activityInfo.activityOptions)
566 && userId == activityInfo.userId) {
567 replaceEntry = false;
568 if (activityInfo.isVisible) { // already shown.
569 return true;
570 }
571 }
572 if (replaceEntry) {
Keun young Parkb5fab732019-10-03 10:51:12 -0700573 activityInfo = new RunningActivityInfo(intent, options, userId);
574 mRunningActivities.put(displayId, activityInfo);
575 }
576 }
577 boolean launched = launchIfNecessary(displayId);
578 if (!launched) {
579 synchronized (mLock) {
580 mRunningActivities.remove(displayId);
581 }
582 }
583 // If first trial fails, let client know and do not retry as it can be wrong setting.
584 if (startMonitoringEvents && launched) {
585 startMonitoringEvents();
586 }
587 return launched;
588 }
589
590 /** Check {@link InstrumentClusterRenderingService#stopFixedActivityMode(int)} */
591 public void stopFixedActivityMode(int displayId) {
592 if (!isDisplayAllowedForFixedMode(displayId)) {
593 return;
594 }
595 boolean stopMonitoringEvents = false;
596 synchronized (mLock) {
597 mRunningActivities.remove(displayId);
598 if (mRunningActivities.size() == 0) {
599 stopMonitoringEvents = true;
600 }
601 }
602 if (stopMonitoringEvents) {
603 stopMonitoringEvents();
604 }
605 }
606}