Stop fixed activity mode when the display is removed.
- Tried to install DisplayListener and remove the removed display only,
But, because it turns out DisplayListener is so slow, I'd like to
check displays inside launchIfNecessary().
Bug: 200040668
Test: check if the logcat has the warning to launch an Activity on the invalid display.
Change-Id: Iae79cdfdb8dc31c958bae4afbd0db17f0dddd321
Merged-In: Iae79cdfdb8dc31c958bae4afbd0db17f0dddd321
diff --git a/service/src/com/android/car/am/FixedActivityService.java b/service/src/com/android/car/am/FixedActivityService.java
index bbc76ff..c969fc1 100644
--- a/service/src/com/android/car/am/FixedActivityService.java
+++ b/service/src/com/android/car/am/FixedActivityService.java
@@ -61,6 +61,7 @@
import com.android.car.R;
import com.android.car.user.CarUserService;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import java.util.List;
@@ -417,12 +418,20 @@
return false;
}
for (int i = mRunningActivities.size() - 1; i >= 0; i--) {
+ int displayIdForActivity = mRunningActivities.keyAt(i);
+ Display display = mDm.getDisplay(displayIdForActivity);
+ if (display == null) {
+ Slog.e(TAG_AM, "Stop fixed activity for non-available display"
+ + displayIdForActivity);
+ mRunningActivities.removeAt(i);
+ continue;
+ }
+
RunningActivityInfo activityInfo = mRunningActivities.valueAt(i);
activityInfo.isVisible = false;
if (isUserAllowedToLaunchActivity(activityInfo.userId)) {
continue;
}
- final int displayIdForActivity = mRunningActivities.keyAt(i);
if (activityInfo.taskId != INVALID_TASK_ID) {
Slog.i(TAG_AM, "Finishing fixed activity on user switching:"
+ activityInfo);
@@ -432,12 +441,6 @@
Slog.e(TAG_AM, "remote exception from AM", e);
}
CarServiceUtils.runOnMain(() -> {
- Display display = mDm.getDisplay(displayIdForActivity);
- if (display == null) {
- Slog.e(TAG_AM, "Display not available, cannot launnch window:"
- + displayIdForActivity);
- return;
- }
Presentation p = new Presentation(mContext, display,
android.R.style.Theme_Black_NoTitleBar_Fullscreen,
// TYPE_PRESENTATION can't be used in the internal display.
@@ -546,7 +549,8 @@
}
}
- private void launchIfNecessary() {
+ @VisibleForTesting
+ void launchIfNecessary() {
launchIfNecessary(Display.INVALID_DISPLAY);
}
@@ -609,6 +613,13 @@
return true;
}
+ @VisibleForTesting
+ RunningActivityInfo getRunningFixedActivity(int displayId) {
+ synchronized (mLock) {
+ return mRunningActivities.get(displayId);
+ }
+ }
+
/**
* Checks {@link InstrumentClusterRenderingService#startFixedActivityModeForDisplayAndUser(
* Intent, ActivityOptions, int)}
diff --git a/tests/carservice_unit_test/src/com/android/car/am/FixedActivityServiceTest.java b/tests/carservice_unit_test/src/com/android/car/am/FixedActivityServiceTest.java
index b22831d..4a9f0c7 100644
--- a/tests/carservice_unit_test/src/com/android/car/am/FixedActivityServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/am/FixedActivityServiceTest.java
@@ -87,6 +87,8 @@
private CarUserService mCarUserService;
@Mock
private CarPowerManager mCarPowerManager;
+ @Mock
+ private Display mValidDisplay;
private FixedActivityService mFixedActivityService;
@@ -102,6 +104,7 @@
when(mContext.getPackageManager()).thenReturn(mPackageManager);
doReturn(mCarUserService).when(() -> CarLocalServices.getService(CarUserService.class));
doReturn(mCarPowerManager).when(() -> CarLocalServices.createCarPowerManager(mContext));
+ when(mDisplayManager.getDisplay(mValidDisplayId)).thenReturn(mValidDisplay);
mFixedActivityService = new FixedActivityService(mContext, mActivityManager, mUserManager,
mDisplayManager);
}
@@ -252,6 +255,7 @@
assertThat(ret).isTrue();
int anotherValidDisplayId = mValidDisplayId + 1;
+ when(mDisplayManager.getDisplay(anotherValidDisplayId)).thenReturn(mValidDisplay);
ret = mFixedActivityService.startFixedActivityModeForDisplayAndUser(anotherIntent,
options, anotherValidDisplayId, userId);
verify(mContext).startActivityAsUser(eq(anotherIntent), any(Bundle.class),
@@ -272,6 +276,46 @@
}
@Test
+ public void testStartFixedActivityModeForDisplayAndUser_unavailableDisplay() {
+ int userId = 10;
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ ActivityOptions options = new ActivityOptions(new Bundle());
+ int unavailableDisplayId = mValidDisplayId + 1;
+
+ boolean started = mFixedActivityService.startFixedActivityModeForDisplayAndUser(
+ intent, options, unavailableDisplayId, userId);
+ assertThat(started).isFalse();
+ }
+
+ @Test
+ public void testStartFixedActivityModeForDisplayAndUser_displayRemoved()
+ throws Exception {
+ int displayToBeRemoved = mValidDisplayId + 1;
+ when(mDisplayManager.getDisplay(displayToBeRemoved)).thenReturn(
+ mValidDisplay, // for startFixedActivityModeForDisplayAndUser
+ mValidDisplay, // for launchIf
+ null);
+ int userId = 10;
+ ActivityOptions options = new ActivityOptions(new Bundle());
+ Intent intent = expectComponentAvailable("test_package", "com.test.dude", userId);
+ mockAmGetCurrentUser(userId);
+ expectNoActivityStack();
+
+ boolean started = mFixedActivityService.startFixedActivityModeForDisplayAndUser(
+ intent, options, displayToBeRemoved, userId);
+ assertThat(started).isTrue();
+ assertThat(mFixedActivityService.getRunningFixedActivity(displayToBeRemoved)).isNotNull();
+
+ // The display is still valid.
+ mFixedActivityService.launchIfNecessary();
+ assertThat(mFixedActivityService.getRunningFixedActivity(displayToBeRemoved)).isNotNull();
+
+ // The display is removed.
+ mFixedActivityService.launchIfNecessary();
+ assertThat(mFixedActivityService.getRunningFixedActivity(displayToBeRemoved)).isNull();
+ }
+
+ @Test
public void testStartFixedActivityModeForDisplayAndUser_notAllowedUser() {
int currentUserId = 10;
int notAllowedUserId = 11;