Merge "Import translations. DO NOT MERGE ANYWHERE" into sc-v2-dev
diff --git a/cpp/security/vehicle_binding_util/tests/Android.bp b/cpp/security/vehicle_binding_util/tests/Android.bp
index d47b8e5..69a61cf 100644
--- a/cpp/security/vehicle_binding_util/tests/Android.bp
+++ b/cpp/security/vehicle_binding_util/tests/Android.bp
@@ -37,7 +37,6 @@
     name: "vehicle_binding_integration_test",
     test_suites: [
         "device-tests",
-        "vts"
     ],
     require_root: true,
     defaults: ["vehicle_binding_util_defaults"],
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/service/src/com/android/car/cluster/ClusterHomeService.java b/service/src/com/android/car/cluster/ClusterHomeService.java
index b032d93..3af771a 100644
--- a/service/src/com/android/car/cluster/ClusterHomeService.java
+++ b/service/src/com/android/car/cluster/ClusterHomeService.java
@@ -84,6 +84,7 @@
     private Insets mInsets = Insets.NONE;
     private int mUiType = ClusterHomeManager.UI_TYPE_CLUSTER_HOME;
     private Intent mLastIntent;
+    private int mLastIntentUserId = UserHandle.USER_SYSTEM;
 
     private final RemoteCallbackList<IClusterHomeCallback> mClientCallbacks =
             new RemoteCallbackList<>();
@@ -126,6 +127,7 @@
     private void initClusterDisplay() {
         int clusterDisplayId = mOccupantZoneService.getDisplayIdForDriver(
                 CarOccupantZoneManager.DISPLAY_TYPE_INSTRUMENT_CLUSTER);
+        Slogf.d(TAG, "initClusterDisplay: displayId=%d", clusterDisplayId);
         if (clusterDisplayId == Display.INVALID_DISPLAY) {
             Slogf.i(TAG, "No cluster display is defined");
         }
@@ -152,7 +154,7 @@
         ActivityOptions activityOptions = ActivityOptions.makeBasic()
                 .setLaunchDisplayId(clusterDisplayId);
         mFixedActivityService.startFixedActivityModeForDisplayAndUser(
-                mLastIntent, activityOptions, clusterDisplayId, UserHandle.USER_SYSTEM);
+                mLastIntent, activityOptions, clusterDisplayId, mLastIntentUserId);
     }
 
     private final ICarOccupantZoneCallback mOccupantZoneCallback =
@@ -292,6 +294,7 @@
     @Override
     public boolean startFixedActivityModeAsUser(Intent intent,
             Bundle activityOptionsBundle, int userId) {
+        Slogf.d(TAG, "startFixedActivityModeAsUser: intent=%s, userId=%d", intent, userId);
         enforcePermission(Car.PERMISSION_CAR_INSTRUMENT_CLUSTER_CONTROL);
         if (!mServiceEnabled) throw new IllegalStateException("Service is not enabled");
         if (mClusterDisplayId == Display.INVALID_DISPLAY) {
@@ -302,6 +305,7 @@
         ActivityOptions activityOptions = ActivityOptions.fromBundle(activityOptionsBundle);
         activityOptions.setLaunchDisplayId(mClusterDisplayId);
         mLastIntent = intent;
+        mLastIntentUserId = userId;
         return mFixedActivityService.startFixedActivityModeForDisplayAndUser(
                 intent, activityOptions, mClusterDisplayId, userId);
     }
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;