Merge "Revert "Include CarManagedProvisioning in car.mk"" into sc-v2-dev
diff --git a/car-internal-lib/src/com/android/car/internal/ICarServiceHelper.aidl b/car-internal-lib/src/com/android/car/internal/ICarServiceHelper.aidl
index 22148eb..cabbc07 100644
--- a/car-internal-lib/src/com/android/car/internal/ICarServiceHelper.aidl
+++ b/car-internal-lib/src/com/android/car/internal/ICarServiceHelper.aidl
@@ -55,4 +55,10 @@
      * Creates the given user, even when it's disallowed by DevicePolicyManager.
      */
     UserInfo createUserEvenWhenDisallowed(String name, String userType, int flags);
+
+    /**
+     * Designates the given {@code activity} to be launched in {@code TaskDisplayArea} of
+     * {@code featureId} in the display of {@code displayId}.
+     */
+    int setPersistentActivity(in ComponentName activity, int displayId, int featureId);
 }
diff --git a/car-lib/src/android/car/Car.java b/car-lib/src/android/car/Car.java
index 23ed24d..6647a32 100644
--- a/car-lib/src/android/car/Car.java
+++ b/car-lib/src/android/car/Car.java
@@ -32,6 +32,7 @@
 import android.car.admin.CarDevicePolicyManager;
 import android.car.annotation.MandatoryFeature;
 import android.car.annotation.OptionalFeature;
+import android.car.app.CarActivityManager;
 import android.car.cluster.CarInstrumentClusterManager;
 import android.car.cluster.ClusterActivityState;
 import android.car.cluster.ClusterHomeManager;
@@ -373,6 +374,14 @@
     @OptionalFeature
     public static final String CAR_TELEMETRY_SERVICE = "car_telemetry_service";
 
+    /**
+     * Service name for {@link android.car.app.CarActivityManager}
+     *
+     * @hide
+     */
+    @MandatoryFeature
+    public static final String CAR_ACTIVITY_SERVICE = "car_activity_service";
+
     /** Permission necessary to access car's mileage information.
      *  @hide
      */
@@ -868,6 +877,14 @@
     public static final String PERMISSION_COLLECT_CAR_WATCHDOG_METRICS =
             "android.car.permission.COLLECT_CAR_WATCHDOG_METRICS";
 
+    /**
+     * Permission necessary to control launching applications in Car.
+     *
+     * @hide
+     */
+    public static final String PERMISSION_CONTROL_CAR_APP_LAUNCH =
+            "android.car.permission.CONTROL_CAR_APP_LAUNCH";
+
     /** @hide */
     @IntDef({CONNECTION_TYPE_EMBEDDED})
     @Retention(RetentionPolicy.SOURCE)
@@ -904,25 +921,6 @@
     public static final String CAR_EXTRA_BROWSE_SERVICE_FOR_SESSION =
             "android.media.session.BROWSE_SERVICE";
 
-    /**
-     * If some specific Activity should be launched on the designated TDA all the time, include this
-     * integer extra in the first launching Intent and ActivityOption with the launch TDA.
-     * If the value is {@link #LAUNCH_PERSISTENT_ADD}, CarLaunchParamsModifier will memorize
-     * the Activity and the TDA pair, and assign the TDA in the following Intents for the Activity.
-     * If there is any assigned Activity on the TDA, it'll be replaced with the new Activity.
-     * If the value is {@Link #LAUNCH_PERSISTENT_DELETE}, it'll remove the stored info for the given
-     * Activity.
-     *
-     * @hide
-     */
-    public static final String CAR_EXTRA_LAUNCH_PERSISTENT =
-            "android.car.intent.extra.launchparams.PERSISTENT";
-
-    /** @hide */
-    public static final int LAUNCH_PERSISTENT_DELETE = 0;
-    /** @hide */
-    public static final int LAUNCH_PERSISTENT_ADD = 1;
-
     /** @hide */
     public static final String CAR_SERVICE_INTERFACE_NAME = CommonConstants.CAR_SERVICE_INTERFACE;
 
@@ -1919,6 +1917,9 @@
             case CAR_TELEMETRY_SERVICE:
                 manager = new CarTelemetryManager(this, binder);
                 break;
+            case CAR_ACTIVITY_SERVICE:
+                manager = new CarActivityManager(this, binder);
+                break;
             default:
                 // Experimental or non-existing
                 String className = null;
diff --git a/car-lib/src/android/car/app/CarActivityManager.java b/car-lib/src/android/car/app/CarActivityManager.java
new file mode 100644
index 0000000..dd521f9
--- /dev/null
+++ b/car-lib/src/android/car/app/CarActivityManager.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.app;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.app.Activity;
+import android.car.Car;
+import android.car.CarManagerBase;
+import android.car.user.CarUserManager;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * API to manage {@link android.app.Activity} in Car.
+ *
+ * @hide
+ */
+public final class CarActivityManager extends CarManagerBase {
+    private static final String TAG = CarUserManager.class.getSimpleName();
+
+    /** Indicates that the operation was successful. */
+    public static final int RESULT_SUCCESS = 0;
+    /** Indicates that the operation was failed with the unknown reason. */
+    public static final int RESULT_FAILURE = -1;
+    /**
+     * Indicates that the operation was failed because the requester isn't the current user or
+     * the system user
+     */
+    public static final int RESULT_INVALID_USER = -2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "RESULT_", value = {
+            RESULT_SUCCESS,
+            RESULT_FAILURE,
+            RESULT_INVALID_USER,
+    })
+    @Target({ElementType.TYPE_USE})
+    public @interface ResultTypeEnum {}
+
+    /**
+     * Internal error code for throwing {@link ActivityNotFoundException} from service.
+     * @hide
+     */
+    public static final int ERROR_CODE_ACTIVITY_NOT_FOUND = -101;
+
+    private final ICarActivityService mService;
+
+    /**
+     * @hide
+     */
+    public CarActivityManager(@NonNull Car car, @NonNull IBinder service) {
+        this(car, ICarActivityService.Stub.asInterface(service));
+    }
+
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public CarActivityManager(@NonNull Car car, @NonNull ICarActivityService service) {
+        super(car);
+
+        mService = service;
+    }
+
+    /**
+     * Designates the given {@code activity} to be launched in {@code TaskDisplayArea} of
+     * {@code featureId} in the display of {@code displayId}.
+     * <p>Note: this will not affect the existing {@link Activity}.
+     * Note: You can map assign {@code Activity} to one {@code TaskDisplayArea} only. If
+     * you assign it to the multiple {@code TaskDisplayArea}s, then the last one wins.
+     * Note: The requester should be the current user or the system user, if not, the operation will
+     * be failed with {@code RESULT_INVALID_USER}.
+     *
+     * @param activity {@link Activity} to designate
+     * @param displayId {@code Display} where {@code TaskDisplayArea} is located in
+     * @param featureId {@code TaskDisplayArea} where {@link Activity} is launched in, if it is
+     *         {@code DisplayAreaOrganizer.FEATURE_UNDEFINED}, then it'll remove the existing one.
+     * @return {@code ResultTypeEnum}. {@code RESULT_SUCCESS} if the operation is successful,
+     *         otherwise, {@code RESULT_XXX} depending on the type of the error.
+     * @throws {@link IllegalArgumentException} if {@code displayId} or {@code featureId} is
+     *         invalid. {@link ActivityNotFoundException} if {@code activity} is not found
+     *         when it tries to remove.
+     */
+    @RequiresPermission(Car.PERMISSION_CONTROL_CAR_APP_LAUNCH)
+    @ResultTypeEnum
+    public int setPersistentActivity(
+            @NonNull ComponentName activity, int displayId, int featureId) {
+        try {
+            return mService.setPersistentActivity(activity, displayId, featureId);
+        } catch (IllegalArgumentException | IllegalStateException | SecurityException e) {
+            throw e;
+        } catch (ServiceSpecificException e) {
+            return handleServiceSpecificFromCarService(e);
+        } catch (RemoteException | RuntimeException e) {
+            return handleExceptionFromCarService(e, RESULT_FAILURE);
+        }
+    }
+
+    /** @hide */
+    @Override
+    protected void onCarDisconnected() {
+        // nothing to do
+    }
+
+    private int handleServiceSpecificFromCarService(ServiceSpecificException e)
+            throws ActivityNotFoundException {
+        if (e.errorCode == ERROR_CODE_ACTIVITY_NOT_FOUND) {
+            throw new ActivityNotFoundException(e.getMessage());
+        }
+        // don't know what this is
+        throw new IllegalStateException(e);
+    }
+}
diff --git a/car-lib/src/android/car/app/ICarActivityService.aidl b/car-lib/src/android/car/app/ICarActivityService.aidl
new file mode 100644
index 0000000..9f3e764
--- /dev/null
+++ b/car-lib/src/android/car/app/ICarActivityService.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.app;
+
+import android.content.ComponentName;
+
+/** @hide */
+interface ICarActivityService {
+    /**
+     * Designates the given {@code activity} to be launched in {@code TaskDisplayArea} of
+     * {@code featureId} in the display of {@code displayId}.
+     */
+    int setPersistentActivity(in ComponentName activity, int displayId, int featureId) = 0;
+}
+
diff --git a/car-maps-placeholder/res/values-te/strings.xml b/car-maps-placeholder/res/values-te/strings.xml
index 96c809e..3ecc950 100644
--- a/car-maps-placeholder/res/values-te/strings.xml
+++ b/car-maps-placeholder/res/values-te/strings.xml
@@ -17,5 +17,5 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name" msgid="6575346965016311017">"Maps"</string>
-    <string name="error_text" msgid="5575174711944349180">"మ్యాప్స్ అప్లికేషన్ ఇన్‌స్టాల్ చేయబడలేదు. దయచేసి మీ కారుని తయారు చేసినవారిని సంప్రదించండి"</string>
+    <string name="error_text" msgid="5575174711944349180">"మ్యాప్స్ అప్లికేషన్ ఏదీ ఇన్‌స్టాల్ చేయలేదు. దయచేసి మీ కారు తయారీదారు సంస్థను సంప్రదించండి."</string>
 </resources>
diff --git a/car-test-lib/Android.bp b/car-test-lib/Android.bp
index bb72590..bad376e 100644
--- a/car-test-lib/Android.bp
+++ b/car-test-lib/Android.bp
@@ -48,5 +48,6 @@
         "android.hardware.automotive.vehicle-V2.0-java",
         "mockito-target-extended",
         "compatibility-device-util-axt",
+        "android.test.mock",
     ],
 }
diff --git a/car-test-lib/src/android/car/test/util/BroadcastingFakeContext.java b/car-test-lib/src/android/car/test/util/BroadcastingFakeContext.java
new file mode 100644
index 0000000..014e283
--- /dev/null
+++ b/car-test-lib/src/android/car/test/util/BroadcastingFakeContext.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.test.util;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.content.BroadcastReceiver;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.test.mock.MockContext;
+
+import com.google.common.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * A fake implementation for {@link android.content.Context}, that helps broadcast {@link Intent}s
+ * to registered {@link BroadcastReceiver} instances.
+ */
+// TODO(b/202420937): Add unit tests for this class.
+public final class BroadcastingFakeContext extends MockContext {
+    private BroadcastReceiver mReceiver;
+    private IntentFilter mIntentFilter;
+    private Handler mHandler;
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+        mReceiver = receiver;
+        mIntentFilter = filter;
+
+        return null;
+    }
+
+    @Override
+    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+            String broadcastPermission, Handler scheduler) {
+        mReceiver = receiver;
+        mIntentFilter = filter;
+        mHandler = scheduler;
+
+        return null;
+    }
+
+    @Override
+    public void sendBroadcast(Intent intent) {
+        if (mHandler == null) {
+            mReceiver.onReceive(this, intent);
+            return;
+        }
+
+        CountDownLatch latch = new CountDownLatch(1);
+        mHandler.getLooper().getQueue().addIdleHandler(() -> {
+            latch.countDown();
+            return false;
+        });
+
+        mHandler.post(() -> mReceiver.onReceive(this, intent));
+
+        // wait until the queue is idle
+        try {
+            latch.await();
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new IllegalStateException(
+                    "Interrupted while waiting for Broadcast Intent to be received");
+        }
+    }
+
+    @Override
+    public void unregisterReceiver(BroadcastReceiver receiver) {
+        if (receiver == mReceiver) {
+            mReceiver = null;
+            mIntentFilter = null;
+            mHandler = null;
+        }
+    }
+
+    public void verifyReceiverNotRegistered() {
+        assertThat(mIntentFilter).isNull();
+        assertThat(mReceiver).isNull();
+        assertThat(mHandler).isNull();
+    }
+
+    public void verifyReceiverRegistered(String expectedAction) {
+        assertThat(mIntentFilter.actionsIterator()).isNotNull();
+        ArrayList<String> actions = Lists.newArrayList(mIntentFilter.actionsIterator());
+        assertWithMessage("IntentFilter actions").that(actions).contains(expectedAction);
+        assertWithMessage("Registered BroadcastReceiver").that(mReceiver).isNotNull();
+    }
+}
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherRRO/res/values/config.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherRRO/res/values/config.xml
index c912ab9..059f7b1 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherRRO/res/values/config.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherRRO/res/values/config.xml
@@ -25,4 +25,13 @@
         <item>com.android.car.carlauncher/.AppGridActivity</item>
         <item>com.android.car.notification/.CarNotificationCenterActivity</item>
     </string-array>
+
+    <string-array name="config_ignoreOpeningForegroundDA" translatable="false">
+        <item>com.android.car.carlauncher/.CarLauncher</item>
+        <item>com.android.car.carlauncher/.ControlBarActivity</item>
+        <item>com.android.car.settings/.FallbackHome</item>
+        <item>com.google.android.gms/.auth.uiflows.common.UnpackingRedirectActivity</item>
+        <item>com.google.android.gms/.auth.auto.SignInMethodActivity</item>
+        <item>com.google.android.gms/.auth.uiflows.minutemaid.MinuteMaidActivity</item>
+    </string-array>
 </resources>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherRRO/res/xml/overlays.xml b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherRRO/res/xml/overlays.xml
index 44c23f8..d15e291 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitLauncherRRO/res/xml/overlays.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitLauncherRRO/res/xml/overlays.xml
@@ -64,4 +64,5 @@
 
     <item target="array/config_homeCardModuleClasses" value="@array/config_homeCardModuleClasses"/>
     <item target="array/config_foregroundDAComponents" value="@array/config_foregroundDAComponents"/>
+    <item target="array/config_ignoreOpeningForegroundDA" value="@array/config_ignoreOpeningForegroundDA"/>
 </overlay>
\ No newline at end of file
diff --git a/car_product/overlay/frameworks/base/core/res/res/values-bn/strings.xml b/car_product/overlay/frameworks/base/core/res/res/values-bn/strings.xml
index d977eee..31e0c0f 100644
--- a/car_product/overlay/frameworks/base/core/res/res/values-bn/strings.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/values-bn/strings.xml
@@ -19,6 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="owner_name" msgid="3416113395996003764">"ড্রাইভার"</string>
     <string name="permlab_accessCoarseLocation" msgid="2494909511737161237">"শুধুমাত্র অ্যাপটি খোলা থাকলে আপনার আনুমানিক লোকেশন অ্যাক্সেস করা"</string>
-    <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (2297855099350562159) -->
-    <skip />
+    <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="2297855099350562159">"মাইক্রোফোন চালু করুন"</string>
 </resources>
diff --git a/car_product/overlay/frameworks/base/core/res/res/values-fr-rCA/strings.xml b/car_product/overlay/frameworks/base/core/res/res/values-fr-rCA/strings.xml
index eb9354f..8b0f9ed 100644
--- a/car_product/overlay/frameworks/base/core/res/res/values-fr-rCA/strings.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/values-fr-rCA/strings.xml
@@ -19,6 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="owner_name" msgid="3416113395996003764">"Conducteur"</string>
     <string name="permlab_accessCoarseLocation" msgid="2494909511737161237">"accéder à votre position approximative seulement en avant-plan"</string>
-    <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (2297855099350562159) -->
-    <skip />
+    <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="2297855099350562159">"Activer le microphone"</string>
 </resources>
diff --git a/car_product/overlay/frameworks/base/core/res/res/values-gu/strings.xml b/car_product/overlay/frameworks/base/core/res/res/values-gu/strings.xml
index d92cc32..9cf7aca 100644
--- a/car_product/overlay/frameworks/base/core/res/res/values-gu/strings.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/values-gu/strings.xml
@@ -19,6 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="owner_name" msgid="3416113395996003764">"ડ્રાઇવર"</string>
     <string name="permlab_accessCoarseLocation" msgid="2494909511737161237">"ફૉરગ્રાઉન્ડમાં ફક્ત અંદાજિત સ્થાન ઍક્સેસ કરો"</string>
-    <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (2297855099350562159) -->
-    <skip />
+    <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="2297855099350562159">"માઇક્રોફોન ચાલુ કરો"</string>
 </resources>
diff --git a/car_product/overlay/frameworks/base/core/res/res/values-kn/strings.xml b/car_product/overlay/frameworks/base/core/res/res/values-kn/strings.xml
index bb5ea0f..359c573 100644
--- a/car_product/overlay/frameworks/base/core/res/res/values-kn/strings.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/values-kn/strings.xml
@@ -19,6 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="owner_name" msgid="3416113395996003764">"ಡ್ರೈವರ್"</string>
     <string name="permlab_accessCoarseLocation" msgid="2494909511737161237">"ಮುನ್ನೆಲೆಯಲ್ಲಿ ಮಾತ್ರ ಅಂದಾಜು ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಿ"</string>
-    <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (2297855099350562159) -->
-    <skip />
+    <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="2297855099350562159">"ಮೈಕ್ರೋಫೋನ್ ಆನ್ ಮಾಡಿ"</string>
 </resources>
diff --git a/car_product/overlay/frameworks/base/core/res/res/values-mr/strings.xml b/car_product/overlay/frameworks/base/core/res/res/values-mr/strings.xml
index c087704..96f9785 100644
--- a/car_product/overlay/frameworks/base/core/res/res/values-mr/strings.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/values-mr/strings.xml
@@ -19,6 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="owner_name" msgid="3416113395996003764">"ड्रायव्हर"</string>
     <string name="permlab_accessCoarseLocation" msgid="2494909511737161237">"फक्त फोअरग्राउंडमध्ये अंदाजे स्थान अ‍ॅक्सेस करा"</string>
-    <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (2297855099350562159) -->
-    <skip />
+    <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="2297855099350562159">"मायक्रोफोन सुरू करा"</string>
 </resources>
diff --git a/car_product/overlay/frameworks/base/core/res/res/values-ur/strings.xml b/car_product/overlay/frameworks/base/core/res/res/values-ur/strings.xml
index f3cf815..256885e 100644
--- a/car_product/overlay/frameworks/base/core/res/res/values-ur/strings.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/values-ur/strings.xml
@@ -19,6 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="owner_name" msgid="3416113395996003764">"ڈرائیور"</string>
     <string name="permlab_accessCoarseLocation" msgid="2494909511737161237">"صرف پیش منظر میں تخمینی مقام تک رسائی"</string>
-    <!-- no translation found for sensor_privacy_start_use_dialog_turn_on_button (2297855099350562159) -->
-    <skip />
+    <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="2297855099350562159">"مائیکروفون آن کریں"</string>
 </resources>
diff --git a/service/AndroidManifest.xml b/service/AndroidManifest.xml
index f8a5e9c..9cf986d 100644
--- a/service/AndroidManifest.xml
+++ b/service/AndroidManifest.xml
@@ -892,6 +892,14 @@
                 android:label="@string/car_permission_label_template_renderer"
                 android:description="@string/car_permission_desc_template_renderer"/>
 
+    <!-- Allows an application to control launching applications in Car.
+         <p>Protection level: signature|privileged
+    -->
+    <permission android:name="android.car.permission.CONTROL_CAR_APP_LAUNCH"
+        android:description="@string/car_permission_desc_control_car_app_launch"
+        android:label="@string/car_permission_label_control_car_app_launch"
+        android:protectionLevel="signature|privileged" />
+
     <uses-permission android:name="android.permission.CALL_PHONE"/>
     <uses-permission android:name="android.permission.DEVICE_POWER"/>
     <uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS"/>
diff --git a/service/res/values-bn/strings.xml b/service/res/values-bn/strings.xml
index eff0449..32f7b2a 100644
--- a/service/res/values-bn/strings.xml
+++ b/service/res/values-bn/strings.xml
@@ -182,20 +182,12 @@
     <string name="factory_reset_later_button" msgid="2401829720674483843">"পরে রিসেট করুন"</string>
     <string name="factory_reset_later_text" msgid="5896142140528784784">"ইনফোটেইনমেন্ট সিস্টেম পরের বার গাড়ি চালু হলে রিসেট হবে।"</string>
     <string name="factory_reset_driving_text" msgid="6702298505761254553">"রিসেট শুরু করতে গাড়িকে পার্কিং অবস্থায় থাকতে হবে।"</string>
-    <!-- no translation found for resource_overuse_notification_title (3385149030747234969) -->
-    <skip />
-    <!-- no translation found for resource_overuse_notification_text_disable_app (4538000369374274293) -->
-    <skip />
-    <!-- no translation found for resource_overuse_notification_text_prioritize_app (4782324719261106243) -->
-    <skip />
-    <!-- no translation found for resource_overuse_notification_text_uninstall_app (531108846448668467) -->
-    <skip />
-    <!-- no translation found for resource_overuse_notification_button_disable_app (5511548570206345274) -->
-    <skip />
-    <!-- no translation found for resource_overuse_notification_button_prioritize_app (5327141954014335559) -->
-    <skip />
-    <!-- no translation found for resource_overuse_notification_button_uninstall_app (7327141273608850448) -->
-    <skip />
-    <!-- no translation found for resource_overuse_toast_disable_app_now (3182983639177825069) -->
-    <skip />
+    <string name="resource_overuse_notification_title" msgid="3385149030747234969">"<xliff:g id="ID_1">^1</xliff:g> আপনার সিস্টেমের পারফর্ম্যান্সে প্রভাব ফেলছে"</string>
+    <string name="resource_overuse_notification_text_disable_app" msgid="4538000369374274293">"সিস্টেমের পারফর্ম্যান্স উন্নত করতে অ্যাপ বন্ধ করুন। সেটিংস থেকে আপনি আবার অ্যাপ চালু করতে পারবেন।"</string>
+    <string name="resource_overuse_notification_text_prioritize_app" msgid="4782324719261106243">"ব্যবহার করা চালিয়ে যেতে অ্যাপকে অগ্রাধিকার দিন।"</string>
+    <string name="resource_overuse_notification_text_uninstall_app" msgid="531108846448668467">"সিস্টেমের পারফর্ম্যান্স উন্নত করতে অ্যাপটি আনইনস্টল করুন।"</string>
+    <string name="resource_overuse_notification_button_disable_app" msgid="5511548570206345274">"অ্যাপ বন্ধ করুন"</string>
+    <string name="resource_overuse_notification_button_prioritize_app" msgid="5327141954014335559">"অ্যাপকে অগ্রাধিকার দিন"</string>
+    <string name="resource_overuse_notification_button_uninstall_app" msgid="7327141273608850448">"অ্যাপ আনইনস্টল করুন"</string>
+    <string name="resource_overuse_toast_disable_app_now" msgid="3182983639177825069">"<xliff:g id="ID_1">^1</xliff:g> বন্ধ করা হয়েছে। সেটিংস থেকে আপনি আবার এটি চালু করতে পারবেন।"</string>
 </resources>
diff --git a/service/res/values-de/strings.xml b/service/res/values-de/strings.xml
index afb1467..c9ad990 100644
--- a/service/res/values-de/strings.xml
+++ b/service/res/values-de/strings.xml
@@ -182,12 +182,12 @@
     <string name="factory_reset_later_button" msgid="2401829720674483843">"Später zurücksetzen"</string>
     <string name="factory_reset_later_text" msgid="5896142140528784784">"Das Infotainmentsystem wird beim nächsten Start zurückgesetzt."</string>
     <string name="factory_reset_driving_text" msgid="6702298505761254553">"Zum Zurücksetzen muss das Auto stehen."</string>
-    <string name="resource_overuse_notification_title" msgid="3385149030747234969">"<xliff:g id="ID_1">^1</xliff:g> beeinträchtigt deine Systemleistung"</string>
+    <string name="resource_overuse_notification_title" msgid="3385149030747234969">"<xliff:g id="ID_1">^1</xliff:g> beeinträchtigt die Systemleistung"</string>
     <string name="resource_overuse_notification_text_disable_app" msgid="4538000369374274293">"Wenn du die Systemleistung verbessern möchtest, deaktiviere die App. Du kannst sie in den Einstellungen später wieder aktivieren."</string>
     <string name="resource_overuse_notification_text_prioritize_app" msgid="4782324719261106243">"Wenn du die App weiterhin nutzen möchtest, priorisiere sie."</string>
     <string name="resource_overuse_notification_text_uninstall_app" msgid="531108846448668467">"Wenn du die Systemleistung verbessern möchtest, deinstalliere die App."</string>
     <string name="resource_overuse_notification_button_disable_app" msgid="5511548570206345274">"App deaktivieren"</string>
     <string name="resource_overuse_notification_button_prioritize_app" msgid="5327141954014335559">"App priorisieren"</string>
     <string name="resource_overuse_notification_button_uninstall_app" msgid="7327141273608850448">"App deinstallieren"</string>
-    <string name="resource_overuse_toast_disable_app_now" msgid="3182983639177825069">"<xliff:g id="ID_1">^1</xliff:g> wurde deaktiviert. Du kannst sie in den Einstellungen wieder aktivieren."</string>
+    <string name="resource_overuse_toast_disable_app_now" msgid="3182983639177825069">"<xliff:g id="ID_1">^1</xliff:g> wurde deaktiviert. Du kannst die App in den Einstellungen wieder aktivieren."</string>
 </resources>
diff --git a/service/res/values-gu/strings.xml b/service/res/values-gu/strings.xml
index 3cd0a0d..feb17b3 100644
--- a/service/res/values-gu/strings.xml
+++ b/service/res/values-gu/strings.xml
@@ -182,20 +182,12 @@
     <string name="factory_reset_later_button" msgid="2401829720674483843">"પછીથી રીસેટ કરો"</string>
     <string name="factory_reset_later_text" msgid="5896142140528784784">"આગલી વખતે જ્યારે કારને ચાલુ કરવામાં આવે ત્યારે ઇન્ફોટેનમેન્ટ સિસ્ટમ રીસેટ થશે."</string>
     <string name="factory_reset_driving_text" msgid="6702298505761254553">"રીસેટ શરૂ કરવા માટે કાર પાર્ક કરેલી હોવી જરૂરી છે."</string>
-    <!-- no translation found for resource_overuse_notification_title (3385149030747234969) -->
-    <skip />
-    <!-- no translation found for resource_overuse_notification_text_disable_app (4538000369374274293) -->
-    <skip />
-    <!-- no translation found for resource_overuse_notification_text_prioritize_app (4782324719261106243) -->
-    <skip />
-    <!-- no translation found for resource_overuse_notification_text_uninstall_app (531108846448668467) -->
-    <skip />
-    <!-- no translation found for resource_overuse_notification_button_disable_app (5511548570206345274) -->
-    <skip />
-    <!-- no translation found for resource_overuse_notification_button_prioritize_app (5327141954014335559) -->
-    <skip />
-    <!-- no translation found for resource_overuse_notification_button_uninstall_app (7327141273608850448) -->
-    <skip />
-    <!-- no translation found for resource_overuse_toast_disable_app_now (3182983639177825069) -->
-    <skip />
+    <string name="resource_overuse_notification_title" msgid="3385149030747234969">"<xliff:g id="ID_1">^1</xliff:g> તમારી સિસ્ટમની કાર્યક્ષમતાને અસર કરી રહી છે"</string>
+    <string name="resource_overuse_notification_text_disable_app" msgid="4538000369374274293">"સિસ્ટમની કાર્યક્ષમતા બહેતર બનાવવા માટે, ઍપ બંધ કરો. તમે સેટિંગમાંથી ઍપ ફરીથી ચાલુ કરી શકશો."</string>
+    <string name="resource_overuse_notification_text_prioritize_app" msgid="4782324719261106243">"ઍપનો ઉપયોગ કરવાનું ચાલુ રાખવા માટે, ઍપને પ્રાધાન્યતા આપો."</string>
+    <string name="resource_overuse_notification_text_uninstall_app" msgid="531108846448668467">"સિસ્ટમની કાર્યક્ષમતા બહેતર બનાવવા માટે, ઍપ અનઇન્સ્ટૉલ કરો."</string>
+    <string name="resource_overuse_notification_button_disable_app" msgid="5511548570206345274">"ઍપ બંધ કરો"</string>
+    <string name="resource_overuse_notification_button_prioritize_app" msgid="5327141954014335559">"ઍપને પ્રાધાન્યતા આપો"</string>
+    <string name="resource_overuse_notification_button_uninstall_app" msgid="7327141273608850448">"ઍપ અનઇન્સ્ટૉલ કરો"</string>
+    <string name="resource_overuse_toast_disable_app_now" msgid="3182983639177825069">"<xliff:g id="ID_1">^1</xliff:g> બંધ કરવામાં આવી છે. તમે સેટિંગમાંથી તેને ફરીથી ચાલુ કરી શકો છો."</string>
 </resources>
diff --git a/service/res/values-kn/strings.xml b/service/res/values-kn/strings.xml
index f44622e..30f5010 100644
--- a/service/res/values-kn/strings.xml
+++ b/service/res/values-kn/strings.xml
@@ -182,20 +182,12 @@
     <string name="factory_reset_later_button" msgid="2401829720674483843">"ನಂತರ ರೀಸೆಟ್ ಮಾಡಿ"</string>
     <string name="factory_reset_later_text" msgid="5896142140528784784">"ಮುಂದಿನ ಬಾರಿ ಕಾರ್ ಆರಂಭವಾದಾಗ ಇನ್‌ಫೋಟೈನ್‌ಮೆಂಟ್ ಸಿಸ್ಟಂ ಅನ್ನು ರೀಸೆಟ್ ಮಾಡಲಾಗುತ್ತದೆ."</string>
     <string name="factory_reset_driving_text" msgid="6702298505761254553">"ರೀಸೆಟ್ ಅನ್ನು ಪ್ರಾರಂಭಿಸಲು ಕಾರ್ ಅನ್ನು ನಿಲ್ಲಿಸಿರಬೇಕು."</string>
-    <!-- no translation found for resource_overuse_notification_title (3385149030747234969) -->
-    <skip />
-    <!-- no translation found for resource_overuse_notification_text_disable_app (4538000369374274293) -->
-    <skip />
-    <!-- no translation found for resource_overuse_notification_text_prioritize_app (4782324719261106243) -->
-    <skip />
-    <!-- no translation found for resource_overuse_notification_text_uninstall_app (531108846448668467) -->
-    <skip />
-    <!-- no translation found for resource_overuse_notification_button_disable_app (5511548570206345274) -->
-    <skip />
-    <!-- no translation found for resource_overuse_notification_button_prioritize_app (5327141954014335559) -->
-    <skip />
-    <!-- no translation found for resource_overuse_notification_button_uninstall_app (7327141273608850448) -->
-    <skip />
-    <!-- no translation found for resource_overuse_toast_disable_app_now (3182983639177825069) -->
-    <skip />
+    <string name="resource_overuse_notification_title" msgid="3385149030747234969">"<xliff:g id="ID_1">^1</xliff:g> ನಿಮ್ಮ ಸಿಸ್ಟಂ ಕಾರ್ಯಕ್ಷಮತೆಯ ಮೇಲೆ ಪರಿಣಾಮ ಬೀರುತ್ತಿದೆ"</string>
+    <string name="resource_overuse_notification_text_disable_app" msgid="4538000369374274293">"ಸಿಸ್ಟಂ ಕಾರ್ಯಕ್ಷಮತೆಯನ್ನು ಸುಧಾರಿಸಲು ಆ್ಯಪ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ. ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ ನೀವು ಆ್ಯಪ್ ಅನ್ನು ಪುನಃ ಸಕ್ರಿಯಗೊಳಿಸಬಹುದು."</string>
+    <string name="resource_overuse_notification_text_prioritize_app" msgid="4782324719261106243">"ಆ್ಯಪ್ ಬಳಸುವುದನ್ನು ಮುಂದುವರಿಸಲು ಆ್ಯಪ್ ಅನ್ನು ಆದ್ಯತೆಗೊಳಿಸಿ."</string>
+    <string name="resource_overuse_notification_text_uninstall_app" msgid="531108846448668467">"ಸಿಸ್ಟಂ ಕಾರ್ಯಕ್ಷಮತೆಯನ್ನು ಸುಧಾರಿಸಲು ಆ್ಯಪ್ ಅನ್ನು ಅನ್‌ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಿ."</string>
+    <string name="resource_overuse_notification_button_disable_app" msgid="5511548570206345274">"ಆ್ಯಪ್ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿ"</string>
+    <string name="resource_overuse_notification_button_prioritize_app" msgid="5327141954014335559">"ಆ್ಯಪ್ ಅನ್ನು ಆದ್ಯತೆಗೊಳಿಸಿ"</string>
+    <string name="resource_overuse_notification_button_uninstall_app" msgid="7327141273608850448">"ಆ್ಯಪ್‌ ಅನ್‌ಇನ್‌ಸ್ಟಾಲ್ ಮಾಡಿ"</string>
+    <string name="resource_overuse_toast_disable_app_now" msgid="3182983639177825069">"<xliff:g id="ID_1">^1</xliff:g> ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ. ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ ನೀವು ಇದನ್ನು ಪುನಃ ಸಕ್ರಿಯಗೊಳಿಸಬಹುದು."</string>
 </resources>
diff --git a/service/res/values-mr/strings.xml b/service/res/values-mr/strings.xml
index 7173d57..abc96a9 100644
--- a/service/res/values-mr/strings.xml
+++ b/service/res/values-mr/strings.xml
@@ -182,20 +182,12 @@
     <string name="factory_reset_later_button" msgid="2401829720674483843">"नंतर रीसेट करा"</string>
     <string name="factory_reset_later_text" msgid="5896142140528784784">"पुढील वेळी कार सुरू झाल्यावर इंफोटेनमेंट सिस्टम रीसेट होईल."</string>
     <string name="factory_reset_driving_text" msgid="6702298505761254553">"रीसेट सुरू करण्यासाठी कार पार्क केलेली असावी."</string>
-    <!-- no translation found for resource_overuse_notification_title (3385149030747234969) -->
-    <skip />
-    <!-- no translation found for resource_overuse_notification_text_disable_app (4538000369374274293) -->
-    <skip />
-    <!-- no translation found for resource_overuse_notification_text_prioritize_app (4782324719261106243) -->
-    <skip />
-    <!-- no translation found for resource_overuse_notification_text_uninstall_app (531108846448668467) -->
-    <skip />
-    <!-- no translation found for resource_overuse_notification_button_disable_app (5511548570206345274) -->
-    <skip />
-    <!-- no translation found for resource_overuse_notification_button_prioritize_app (5327141954014335559) -->
-    <skip />
-    <!-- no translation found for resource_overuse_notification_button_uninstall_app (7327141273608850448) -->
-    <skip />
-    <!-- no translation found for resource_overuse_toast_disable_app_now (3182983639177825069) -->
-    <skip />
+    <string name="resource_overuse_notification_title" msgid="3385149030747234969">"तुमच्या सिस्टीमच्या परफॉर्मन्सवर <xliff:g id="ID_1">^1</xliff:g> मुळे परिणाम होत आहे"</string>
+    <string name="resource_overuse_notification_text_disable_app" msgid="4538000369374274293">"सिस्टीमच्या परफॉर्मन्समध्ये सुधारणा करण्यासाठी ॲप बंद करा. तुम्ही सेटिंग्ज मध्ये ॲप पुन्हा एकदा सुरू करू शकता."</string>
+    <string name="resource_overuse_notification_text_prioritize_app" msgid="4782324719261106243">"ॲप वापरणे सुरू ठेवण्यासाठी ॲपला प्राधान्य द्या."</string>
+    <string name="resource_overuse_notification_text_uninstall_app" msgid="531108846448668467">"सिस्टीमच्या परफॉर्मन्समध्ये सुधारणा करण्यासाठी ॲप अनइंस्टॉल करा."</string>
+    <string name="resource_overuse_notification_button_disable_app" msgid="5511548570206345274">"अ‍ॅप बंद करा"</string>
+    <string name="resource_overuse_notification_button_prioritize_app" msgid="5327141954014335559">"ॲपला प्राधान्य द्या"</string>
+    <string name="resource_overuse_notification_button_uninstall_app" msgid="7327141273608850448">"अ‍ॅप अनइंस्टॉल करा"</string>
+    <string name="resource_overuse_toast_disable_app_now" msgid="3182983639177825069">"<xliff:g id="ID_1">^1</xliff:g> बंद केले आहे. तुम्‍ही सेटिंग्ज मध्ये ते पुन्‍हा सुरू करू शकता."</string>
 </resources>
diff --git a/service/res/values-te/strings.xml b/service/res/values-te/strings.xml
index 48b71ad..ad75dde 100644
--- a/service/res/values-te/strings.xml
+++ b/service/res/values-te/strings.xml
@@ -19,7 +19,7 @@
     <string name="car_permission_label" msgid="2215078736675564541">"కారు సమాచారం"</string>
     <string name="car_permission_desc" msgid="3584369074931334964">"మీ కారుకు సంబంధించిన సమాచారాన్ని యాక్సెస్ చేయండి"</string>
     <string name="car_permission_label_camera" msgid="3725702064841827180">"కారు కెమెరాను యాక్సెస్ చేయగలవు"</string>
-    <string name="car_permission_desc_camera" msgid="917024932164501426">"మీ కారు యొక్క కామెరా(లు)ని యాక్సెస్ చేయండి."</string>
+    <string name="car_permission_desc_camera" msgid="917024932164501426">"మీ కారు కామెరా(ల)ను యాక్సెస్ చేయడం."</string>
     <string name="car_permission_label_energy" msgid="7409144323527821558">"కారు శక్తి సమాచారాన్ని యాక్సెస్ చేయగలవు"</string>
     <string name="car_permission_desc_energy" msgid="3392963810053235407">"మీ కారు శక్తి సమాచారాన్ని యాక్సెస్ చేయండి."</string>
     <string name="car_permission_label_adjust_range_remaining" msgid="839033553999920138">"కారు యొక్క మిగిలిన ప్రయాణ దూరాన్ని సర్దుబాటు చేయండి"</string>
@@ -84,9 +84,9 @@
     <string name="car_permission_label_diag_clear" msgid="4783070510879698157">"సమస్య విశ్లేషణ డేటాను క్లియర్ చేయగలవు"</string>
     <string name="car_permission_desc_diag_clear" msgid="7453222114866042786">"కారు నుండి సమస్య విశ్లేషణ డేటాను క్లియర్ చేయగలవు."</string>
     <string name="car_permission_label_vms_publisher" msgid="3049934078926106641">"VMS ప్రచురణకర్త"</string>
-    <string name="car_permission_desc_vms_publisher" msgid="5589489298597386828">"VMS మెసేజ్‌లను ప్రచురించండి"</string>
+    <string name="car_permission_desc_vms_publisher" msgid="5589489298597386828">"VMS మెసేజ్‌లను పబ్లిష్ చేయండి"</string>
     <string name="car_permission_label_vms_subscriber" msgid="5648841182059222299">"VMS సభ్యులు"</string>
-    <string name="car_permission_desc_vms_subscriber" msgid="7551009457847673620">"VMS మెసేజ్‌లను పొందడానికి సభ్యత్వం తీసుకోండి"</string>
+    <string name="car_permission_desc_vms_subscriber" msgid="7551009457847673620">"VMS మెసేజ్‌లను పొందడానికి సబ్‌స్క్రయిబ్ చేయండి"</string>
     <string name="car_permission_label_bind_vms_client" msgid="4889732900973280313">"VMS క్లయింట్ సేవ"</string>
     <string name="car_permission_desc_bind_vms_client" msgid="4062835325264330564">"VMS క్లయింట్‌లను ఆచరించండి"</string>
     <string name="car_permission_label_storage_monitoring" msgid="2327639346522530549">"ఫ్లాష్ నిల్వ పర్యవేక్షణ"</string>
diff --git a/service/res/values/strings.xml b/service/res/values/strings.xml
index 416981b..70a6ecf 100644
--- a/service/res/values/strings.xml
+++ b/service/res/values/strings.xml
@@ -548,6 +548,11 @@
     <!-- Permission text: app can render templates provided by another app [CHAR LIMIT=NONE] -->
     <string name="car_permission_desc_template_renderer">Render templates.</string>
 
+    <!-- Permission text: app can control launching applications in Car [CHAR LIMIT=NONE] -->
+    <string name="car_permission_label_control_car_app_launch">control launching applications</string>
+    <!-- Permission text: app can control launching applications in Car [CHAR LIMIT=NONE] -->
+    <string name="car_permission_desc_control_car_app_launch">Control launching applications.</string>
+
     <!-- The default name of device enrolled as trust device [CHAR LIMIT=NONE] -->
     <string name="trust_device_default_name">My Device</string>
 
diff --git a/service/src/com/android/car/CarFeatureController.java b/service/src/com/android/car/CarFeatureController.java
index f5df9e4..528353d 100644
--- a/service/src/com/android/car/CarFeatureController.java
+++ b/service/src/com/android/car/CarFeatureController.java
@@ -63,6 +63,7 @@
             Car.APP_FOCUS_SERVICE,
             Car.AUDIO_SERVICE,
             Car.BLUETOOTH_SERVICE,
+            Car.CAR_ACTIVITY_SERVICE,
             Car.CAR_BUGREPORT_SERVICE,
             Car.CAR_DEVICE_POLICY_SERVICE,
             Car.CAR_DRIVING_STATE_SERVICE,
diff --git a/service/src/com/android/car/CarLog.java b/service/src/com/android/car/CarLog.java
index 4585eda..3cec3bb 100644
--- a/service/src/com/android/car/CarLog.java
+++ b/service/src/com/android/car/CarLog.java
@@ -43,6 +43,7 @@
     public static final String TAG_SERVICE = "CAR.SERVICE";
     public static final String TAG_STORAGE = "CAR.STORAGE";
     public static final String TAG_TELEMETRY = "CAR.TELEMETRY";
+    public static final String TAG_TIME = "CAR.TIME";
     public static final String TAG_WATCHDOG = "CAR.WATCHDOG";
 
     /**
diff --git a/service/src/com/android/car/CarServiceUtils.java b/service/src/com/android/car/CarServiceUtils.java
index 2dddeeb..c4f77cf 100644
--- a/service/src/com/android/car/CarServiceUtils.java
+++ b/service/src/com/android/car/CarServiceUtils.java
@@ -154,8 +154,17 @@
     }
 
     public static float[] toFloatArray(List<Float> list) {
-        final int size = list.size();
-        final float[] array = new float[size];
+        int size = list.size();
+        float[] array = new float[size];
+        for (int i = 0; i < size; ++i) {
+            array[i] = list.get(i);
+        }
+        return array;
+    }
+
+    public static long[] toLongArray(List<Long> list) {
+        int size = list.size();
+        long[] array = new long[size];
         for (int i = 0; i < size; ++i) {
             array[i] = list.get(i);
         }
@@ -163,8 +172,8 @@
     }
 
     public static int[] toIntArray(List<Integer> list) {
-        final int size = list.size();
-        final int[] array = new int[size];
+        int size = list.size();
+        int[] array = new int[size];
         for (int i = 0; i < size; ++i) {
             array[i] = list.get(i);
         }
@@ -172,8 +181,8 @@
     }
 
     public static byte[] toByteArray(List<Byte> list) {
-        final int size = list.size();
-        final byte[] array = new byte[size];
+        int size = list.size();
+        byte[] array = new byte[size];
         for (int i = 0; i < size; ++i) {
             array[i] = list.get(i);
         }
diff --git a/service/src/com/android/car/FastPairGattServer.java b/service/src/com/android/car/FastPairGattServer.java
index 6fc537d..b5b9881 100644
--- a/service/src/com/android/car/FastPairGattServer.java
+++ b/service/src/com/android/car/FastPairGattServer.java
@@ -222,8 +222,6 @@
                         .sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset,
                                 mEncryptedResponse);
                 processPairingKey(value);
-                mBluetoothGattServer
-                        .notifyCharacteristicChanged(device, mPasskeyCharacteristic, false);
 
             } else {
                 Log.w(TAG, "onWriteOther" + characteristic.getUuid());
@@ -569,6 +567,8 @@
             return;
         }
         mPasskeyCharacteristic.setValue(mEncryptedResponse);
+        mBluetoothGattServer
+                .notifyCharacteristicChanged(mRemoteGattDevice, mPasskeyCharacteristic, false);
     }
 
     /**
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index d590083..4b7c75d 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -54,6 +54,7 @@
 
 import com.android.car.admin.CarDevicePolicyService;
 import com.android.car.admin.FactoryResetActivity;
+import com.android.car.am.CarActivityService;
 import com.android.car.am.FixedActivityService;
 import com.android.car.audio.CarAudioService;
 import com.android.car.cluster.ClusterHomeService;
@@ -136,6 +137,7 @@
     private final ClusterHomeService mClusterHomeService;
     private final CarEvsService mCarEvsService;
     private final CarTelemetryService mCarTelemetryService;
+    private final CarActivityService mCarActivityService;
 
     private final CarServiceBase[] mAllServices;
 
@@ -358,6 +360,8 @@
         } else {
             mCarTelemetryService = null;
         }
+        mCarActivityService = constructWithTrace(t, CarActivityService.class,
+                () -> new CarActivityService(serviceContext));
 
         // Be careful with order. Service depending on other service should be inited later.
         List<CarServiceBase> allServices = new ArrayList<>();
@@ -394,6 +398,7 @@
         addServiceIfNonNull(allServices, mClusterHomeService);
         addServiceIfNonNull(allServices, mCarEvsService);
         addServiceIfNonNull(allServices, mCarTelemetryService);
+        allServices.add(mCarActivityService);
 
         // Always put mCarExperimentalFeatureServiceController in last.
         addServiceIfNonNull(allServices, mCarExperimentalFeatureServiceController);
@@ -463,6 +468,7 @@
             mSystemInterface.setCarServiceHelper(carServiceHelper);
             mCarOccupantZoneService.setCarServiceHelper(carServiceHelper);
             mCarUserService.setCarServiceHelper(carServiceHelper);
+            mCarActivityService.setICarServiceHelper(carServiceHelper);
 
             bundle = new Bundle();
             bundle.putBinder(ICAR_SYSTEM_SERVER_CLIENT, mICarSystemServerClientImpl.asBinder());
@@ -631,6 +637,8 @@
                 return mCarEvsService;
             case Car.CAR_TELEMETRY_SERVICE:
                 return mCarTelemetryService;
+            case Car.CAR_ACTIVITY_SERVICE:
+                return mCarActivityService;
             default:
                 IBinder service = null;
                 if (mCarExperimentalFeatureServiceController != null) {
diff --git a/service/src/com/android/car/am/CarActivityService.java b/service/src/com/android/car/am/CarActivityService.java
new file mode 100644
index 0000000..61d309e
--- /dev/null
+++ b/service/src/com/android/car/am/CarActivityService.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.am;
+
+import android.app.ActivityManager;
+import android.car.Car;
+import android.car.app.CarActivityManager;
+import android.car.app.ICarActivityService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.IndentingPrintWriter;
+
+import com.android.car.CarServiceBase;
+import com.android.car.internal.ICarServiceHelper;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Service responsible for Activities in Car.
+ */
+public final class CarActivityService extends ICarActivityService.Stub
+        implements CarServiceBase {
+
+    private final Context mContext;
+
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    ICarServiceHelper mICarServiceHelper;
+
+    public CarActivityService(Context context) {
+        mContext = context;
+    }
+
+    @Override
+    public void init() {}
+
+    @Override
+    public void release() {}
+
+    /**
+     * Sets {@code ICarServiceHelper}.
+     */
+    public void setICarServiceHelper(ICarServiceHelper helper) {
+        synchronized (mLock) {
+            mICarServiceHelper = helper;
+        }
+    }
+
+    @Override
+    public int setPersistentActivity(ComponentName activity, int displayId, int featureId) throws
+            RemoteException {
+        if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
+                Car.PERMISSION_CONTROL_CAR_APP_LAUNCH)) {
+            throw new SecurityException("Requires " + Car.PERMISSION_CONTROL_CAR_APP_LAUNCH);
+        }
+        int caller = getCaller();
+        if (caller != UserHandle.USER_SYSTEM && caller != ActivityManager.getCurrentUser()) {
+            return CarActivityManager.RESULT_INVALID_USER;
+        }
+
+        ICarServiceHelper helper;
+        synchronized (mLock) {
+            helper = mICarServiceHelper;
+        }
+        if (helper == null) {
+            throw new IllegalStateException("ICarServiceHelper isn't connected yet");
+        }
+        return helper.setPersistentActivity(activity, displayId, featureId);
+    }
+
+    @VisibleForTesting
+    int getCaller() {  // Non static for mocking.
+        return UserHandle.getUserId(Binder.getCallingUid());
+    }
+
+    @Override
+    public void dump(IndentingPrintWriter writer) {}
+}
diff --git a/service/src/com/android/car/hal/TimeHalService.java b/service/src/com/android/car/hal/TimeHalService.java
new file mode 100644
index 0000000..a166850
--- /dev/null
+++ b/service/src/com/android/car/hal/TimeHalService.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.hal;
+
+import static android.hardware.automotive.vehicle.V2_0.VehicleProperty.EPOCH_TIME;
+
+import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.automotive.vehicle.V2_0.VehicleArea;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropertyStatus;
+import android.util.IndentingPrintWriter;
+
+import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
+
+import java.io.PrintWriter;
+import java.time.Instant;
+import java.util.Collection;
+import java.util.List;
+
+/** Writes the Android System time to EPOCH_TIME in the VHAL, if supported. */
+public final class TimeHalService extends HalServiceBase {
+
+    private static final int[] SUPPORTED_PROPERTIES = new int[]{EPOCH_TIME};
+
+    private final Context mContext;
+
+    private final VehicleHal mHal;
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) {
+                updateProperty(System.currentTimeMillis());
+            }
+        }
+    };
+
+    private boolean mReceiverRegistered;
+
+    @Nullable
+    private Instant mLastAndroidTimeReported;
+
+    private boolean mAndroidTimeSupported;
+
+    TimeHalService(Context context, VehicleHal hal) {
+        mContext = requireNonNull(context);
+        mHal = requireNonNull(hal);
+    }
+
+    @Override
+    public void init() {
+        if (!mAndroidTimeSupported) {
+            return;
+        }
+
+        updateProperty(System.currentTimeMillis());
+
+        IntentFilter filter = new IntentFilter(Intent.ACTION_TIME_CHANGED);
+        mContext.registerReceiver(mReceiver, filter);
+        mReceiverRegistered = true;
+    }
+
+    @Override
+    public void release() {
+        if (mReceiverRegistered) {
+            mContext.unregisterReceiver(mReceiver);
+            mReceiverRegistered = false;
+        }
+
+        mAndroidTimeSupported = false;
+        mLastAndroidTimeReported = null;
+    }
+
+    @Override
+    public int[] getAllSupportedProperties() {
+        return SUPPORTED_PROPERTIES;
+    }
+
+    @Override
+    public void takeProperties(Collection<VehiclePropConfig> properties) {
+        for (VehiclePropConfig property : properties) {
+            switch (property.prop) {
+                case EPOCH_TIME:
+                    mAndroidTimeSupported = true;
+                    return;
+            }
+        }
+    }
+
+    @Override
+    public void onHalEvents(List<VehiclePropValue> values) {
+    }
+
+    public boolean isAndroidTimeSupported() {
+        return mAndroidTimeSupported;
+    }
+
+    private void updateProperty(long timeMillis) {
+        VehiclePropValue propValue = new VehiclePropValue();
+        propValue.prop = EPOCH_TIME;
+        propValue.areaId = VehicleArea.GLOBAL;
+        propValue.status = VehiclePropertyStatus.AVAILABLE;
+        propValue.timestamp = timeMillis;
+        propValue.value.int64Values.add(timeMillis);
+
+        mHal.set(propValue);
+        mLastAndroidTimeReported = Instant.ofEpochMilli(timeMillis);
+    }
+
+    @Override
+    @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO)
+    public void dump(PrintWriter printWriter) {
+        IndentingPrintWriter writer = new IndentingPrintWriter(printWriter);
+        writer.println("*ExternalTime HAL*");
+        writer.increaseIndent();
+        writer.printf(
+                "mLastAndroidTimeReported: %d millis",
+                mLastAndroidTimeReported.toEpochMilli());
+        writer.decreaseIndent();
+        writer.flush();
+    }
+}
diff --git a/service/src/com/android/car/hal/VehicleHal.java b/service/src/com/android/car/hal/VehicleHal.java
index 726f34f..6b67b8d 100644
--- a/service/src/com/android/car/hal/VehicleHal.java
+++ b/service/src/com/android/car/hal/VehicleHal.java
@@ -19,6 +19,7 @@
 import static com.android.car.CarServiceUtils.toByteArray;
 import static com.android.car.CarServiceUtils.toFloatArray;
 import static com.android.car.CarServiceUtils.toIntArray;
+import static com.android.car.CarServiceUtils.toLongArray;
 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
 
 import static java.lang.Integer.toHexString;
@@ -97,6 +98,7 @@
     private final DiagnosticHalService mDiagnosticHal;
     private final ClusterHalService mClusterHalService;
     private final EvsHalService mEvsHal;
+    private final TimeHalService mTimeHalService;
 
     private final Object mLock = new Object();
 
@@ -136,6 +138,8 @@
         mDiagnosticHal = new DiagnosticHalService(this);
         mClusterHalService = new ClusterHalService(this);
         mEvsHal = new EvsHalService(this);
+        mTimeHalService = new TimeHalService(context, this);
+        //TODO(b/202396546): Dedupe this assignment with the other one in constructor below
         mAllServices.addAll(Arrays.asList(mPowerHal,
                 mInputHal,
                 mDiagnosticHal,
@@ -143,6 +147,7 @@
                 mUserHal,
                 mClusterHalService,
                 mEvsHal,
+                mTimeHalService,
                 mPropertyHal)); // mPropertyHal should be the last.
         mHalClient = new HalClient(vehicle, mHandlerThread.getLooper(),
                 /* callback= */ this);
@@ -160,6 +165,7 @@
             UserHalService userHal,
             DiagnosticHalService diagnosticHal,
             ClusterHalService clusterHalService,
+            TimeHalService timeHalService,
             HalClient halClient,
             HandlerThread handlerThread) {
         mHandlerThread = handlerThread;
@@ -172,12 +178,14 @@
         mDiagnosticHal = diagnosticHal;
         mClusterHalService = clusterHalService;
         mEvsHal = new EvsHalService(this);
+        mTimeHalService = timeHalService;
         mAllServices.addAll(Arrays.asList(mPowerHal,
                 mInputHal,
                 mDiagnosticHal,
                 mVmsHal,
                 mUserHal,
                 mEvsHal,
+                mTimeHalService,
                 mPropertyHal));
         mHalClient = halClient;
     }
@@ -322,6 +330,10 @@
         return mEvsHal;
     }
 
+    public TimeHalService getTimeHalService() {
+        return mTimeHalService;
+    }
+
     private void assertServiceOwnerLocked(HalServiceBase service, int property) {
         if (service != mPropertyHandlers.get(property)) {
             throw new IllegalArgumentException("Property 0x" + toHexString(property)
@@ -517,18 +529,25 @@
         VehiclePropValue propValue;
         propValue = mHalClient.getValue(requestedPropValue);
 
-        if (clazz == Integer.class || clazz == int.class) {
+        if (clazz == Long.class || clazz == long.class) {
+            return (T) propValue.value.int64Values.get(0);
+        } else if (clazz == Integer.class || clazz == int.class) {
             return (T) propValue.value.int32Values.get(0);
         } else if (clazz == Boolean.class || clazz == boolean.class) {
             return (T) Boolean.valueOf(propValue.value.int32Values.get(0) == 1);
         } else if (clazz == Float.class || clazz == float.class) {
             return (T) propValue.value.floatValues.get(0);
+        } else if (clazz == Long[].class) {
+            Long[] longArray = new Long[propValue.value.int64Values.size()];
+            return (T) propValue.value.int32Values.toArray(longArray);
         } else if (clazz == Integer[].class) {
             Integer[] intArray = new Integer[propValue.value.int32Values.size()];
             return (T) propValue.value.int32Values.toArray(intArray);
         } else if (clazz == Float[].class) {
             Float[] floatArray = new Float[propValue.value.floatValues.size()];
             return (T) propValue.value.floatValues.toArray(floatArray);
+        } else if (clazz == long[].class) {
+            return (T) toLongArray(propValue.value.int64Values);
         } else if (clazz == int[].class) {
             return (T) toIntArray(propValue.value.int32Values);
         } else if (clazz == float[].class) {
diff --git a/service/src/com/android/car/pm/CarPackageManagerService.java b/service/src/com/android/car/pm/CarPackageManagerService.java
index fb11fd8..e732a50 100644
--- a/service/src/com/android/car/pm/CarPackageManagerService.java
+++ b/service/src/com/android/car/pm/CarPackageManagerService.java
@@ -273,6 +273,9 @@
         mEnableActivityBlocking = res.getBoolean(R.bool.enableActivityBlockingForSafety);
         String blockingActivity = res.getString(R.string.activityBlockingActivity);
         mActivityBlockingActivity = ComponentName.unflattenFromString(blockingActivity);
+        if (mEnableActivityBlocking && mActivityBlockingActivity == null) {
+            Slogf.wtf(TAG, "mActivityBlockingActivity can't be null when enabled");
+        }
         mAllowedAppInstallSources = Arrays.asList(
                 res.getStringArray(R.array.allowedAppInstallSources));
         mVendorServiceController = new VendorServiceController(
@@ -1308,7 +1311,7 @@
 
     private void blockTopActivityIfNecessary(TopTaskInfoContainer topTask) {
         synchronized (mLock) {
-            if (!topTask.topActivity.equals(mActivityBlockingActivity)
+            if (!Objects.equals(mActivityBlockingActivity, topTask.topActivity)
                     && mTopActivityWithDialogPerDisplay.contains(topTask.displayId)
                     && !topTask.topActivity.equals(
                             mTopActivityWithDialogPerDisplay.get(topTask.displayId))) {
diff --git a/service/src/com/android/car/telemetry/databroker/DataBrokerImpl.java b/service/src/com/android/car/telemetry/databroker/DataBrokerImpl.java
index 39a2f17..94f1b11 100644
--- a/service/src/com/android/car/telemetry/databroker/DataBrokerImpl.java
+++ b/service/src/com/android/car/telemetry/databroker/DataBrokerImpl.java
@@ -143,10 +143,12 @@
         mPublisherFactory = publisherFactory;
         mResultStore = resultStore;
         mScriptExecutorListener = new ScriptExecutorListener(this);
-        mPublisherFactory.setFailureConsumer(this::onPublisherFailure);
+        mPublisherFactory.setFailureListener(this::onPublisherFailure);
     }
 
-    private void onPublisherFailure(AbstractPublisher publisher, Throwable error) {
+    private void onPublisherFailure(
+            AbstractPublisher publisher, List<TelemetryProto.MetricsConfig> affectedConfigs,
+            Throwable error) {
         // TODO(b/193680465): disable MetricsConfig and log the error
         Slog.w(CarLog.TAG_TELEMETRY, "publisher failed", error);
     }
diff --git a/service/src/com/android/car/telemetry/proto/stats_log.proto b/service/src/com/android/car/telemetry/proto/stats_log.proto
index 04308d2..ec76cb8 100644
--- a/service/src/com/android/car/telemetry/proto/stats_log.proto
+++ b/service/src/com/android/car/telemetry/proto/stats_log.proto
@@ -99,3 +99,15 @@
 
   reserved 1, 10;
 }
+
+message StatsdStatsReport {
+  message ConfigStats {
+    optional int32 uid = 1;
+    optional int64 id = 2;
+    optional bool is_valid = 9;
+  }
+
+  repeated ConfigStats config_stats = 3;
+
+  reserved 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19;
+}
diff --git a/service/src/com/android/car/telemetry/publisher/AbstractPublisher.java b/service/src/com/android/car/telemetry/publisher/AbstractPublisher.java
index e91831e..088859b 100644
--- a/service/src/com/android/car/telemetry/publisher/AbstractPublisher.java
+++ b/service/src/com/android/car/telemetry/publisher/AbstractPublisher.java
@@ -16,9 +16,10 @@
 
 package com.android.car.telemetry.publisher;
 
+import com.android.car.telemetry.TelemetryProto;
 import com.android.car.telemetry.databroker.DataSubscriber;
 
-import java.util.function.BiConsumer;
+import java.util.List;
 
 /**
  * Abstract class for publishers. It is 1-1 with data source and manages sending data to
@@ -28,15 +29,25 @@
  * configuration. Single publisher instance can send data as several
  * {@link com.android.car.telemetry.TelemetryProto.Publisher} to subscribers.
  *
- * <p>Child classes must be called from the telemetry thread.
+ * <p>The methods must be called from the telemetry thread.
  */
 public abstract class AbstractPublisher {
-    // TODO(b/199211673): provide list of bad MetricsConfigs to failureConsumer.
-    /** Consumes the publisher failures, such as failing to connect to a underlying service. */
-    private final BiConsumer<AbstractPublisher, Throwable> mFailureConsumer;
+    private final PublisherFailureListener mFailureListener;
 
-    AbstractPublisher(BiConsumer<AbstractPublisher, Throwable> failureConsumer) {
-        mFailureConsumer = failureConsumer;
+    /**
+     * Listener for publisher failures, such as failing to connect to a underlying service or
+     * invalid Publisher configuration. When publishers fail, the affected configs should be
+     * disabled, because the associated scripts cannot receive data from the failed publishers.
+     */
+    public interface PublisherFailureListener {
+        /** Called by publishers when they fail. */
+        void onPublisherFailure(
+                AbstractPublisher publisher,
+                List<TelemetryProto.MetricsConfig> affectedConfigs, Throwable error);
+    }
+
+    AbstractPublisher(PublisherFailureListener failureListener) {
+        mFailureListener = failureListener;
     }
 
     /**
@@ -69,10 +80,11 @@
     public abstract boolean hasDataSubscriber(DataSubscriber subscriber);
 
     /**
-     * Notifies the failure consumer that this publisher cannot recover from the hard failure.
-     * For example, it cannot connect to the underlying service.
+     * Notifies the failure Listener that this publisher failed. See
+     * {@link PublisherFailureListener} for details.
      */
-    protected void notifyFailureConsumer(Throwable error) {
-        mFailureConsumer.accept(this, error);
+    protected void onPublisherFailure(
+            List<TelemetryProto.MetricsConfig> affectedConfigs, Throwable error) {
+        mFailureListener.onPublisherFailure(this, affectedConfigs, error);
     }
 }
diff --git a/service/src/com/android/car/telemetry/publisher/CarTelemetrydPublisher.java b/service/src/com/android/car/telemetry/publisher/CarTelemetrydPublisher.java
index 490ab83..c71e728 100644
--- a/service/src/com/android/car/telemetry/publisher/CarTelemetrydPublisher.java
+++ b/service/src/com/android/car/telemetry/publisher/CarTelemetrydPublisher.java
@@ -34,7 +34,7 @@
 import com.android.server.utils.Slogf;
 
 import java.util.ArrayList;
-import java.util.function.BiConsumer;
+import java.util.stream.Collectors;
 
 /**
  * Publisher for cartelemtryd service (aka ICarTelemetry).
@@ -69,9 +69,8 @@
         }
     };
 
-    CarTelemetrydPublisher(BiConsumer<AbstractPublisher, Throwable> failureConsumer,
-            Handler telemetryHandler) {
-        super(failureConsumer);
+    CarTelemetrydPublisher(PublisherFailureListener failureListener, Handler telemetryHandler) {
+        super(failureListener);
         this.mTelemetryHandler = telemetryHandler;
     }
 
@@ -83,7 +82,9 @@
                 mCarTelemetryInternal.asBinder().unlinkToDeath(this::onBinderDied, BINDER_FLAGS);
                 mCarTelemetryInternal = null;
             }
-            notifyFailureConsumer(new IllegalStateException("ICarTelemetryInternal binder died"));
+            onPublisherFailure(
+                    getMetricsConfigs(),
+                    new IllegalStateException("ICarTelemetryInternal binder died"));
         });
     }
 
@@ -94,15 +95,21 @@
         }
         IBinder binder = ServiceManager.checkService(SERVICE_NAME);
         if (binder == null) {
-            notifyFailureConsumer(new IllegalStateException(
-                    "Failed to connect to the ICarTelemetryInternal: service is not ready"));
+            onPublisherFailure(
+                    getMetricsConfigs(),
+                    new IllegalStateException(
+                            "Failed to connect to the ICarTelemetryInternal: service is not "
+                                    + "ready"));
             return;
         }
         try {
             binder.linkToDeath(this::onBinderDied, BINDER_FLAGS);
         } catch (RemoteException e) {
-            notifyFailureConsumer(new IllegalStateException(
-                    "Failed to connect to the ICarTelemetryInternal: linkToDeath failed", e));
+            onPublisherFailure(
+                    getMetricsConfigs(),
+                    new IllegalStateException(
+                            "Failed to connect to the ICarTelemetryInternal: linkToDeath failed",
+                            e));
             return;
         }
         mCarTelemetryInternal = ICarTelemetryInternal.Stub.asInterface(binder);
@@ -111,12 +118,19 @@
         } catch (RemoteException e) {
             binder.unlinkToDeath(this::onBinderDied, BINDER_FLAGS);
             mCarTelemetryInternal = null;
-            notifyFailureConsumer(new IllegalStateException(
-                    "Failed to connect to the ICarTelemetryInternal: Cannot set CarData listener",
-                    e));
+            onPublisherFailure(
+                    getMetricsConfigs(),
+                    new IllegalStateException(
+                            "Failed to connect to the ICarTelemetryInternal: Cannot set CarData "
+                                    + "listener", e));
         }
     }
 
+    private ArrayList<TelemetryProto.MetricsConfig> getMetricsConfigs() {
+        return new ArrayList<>(mSubscribers.stream().map(DataSubscriber::getMetricsConfig).collect(
+                Collectors.toSet()));
+    }
+
     /**
      * Disconnects from ICarTelemetryInternal service.
      *
diff --git a/service/src/com/android/car/telemetry/publisher/PublisherFactory.java b/service/src/com/android/car/telemetry/publisher/PublisherFactory.java
index d777847..0c210b6 100644
--- a/service/src/com/android/car/telemetry/publisher/PublisherFactory.java
+++ b/service/src/com/android/car/telemetry/publisher/PublisherFactory.java
@@ -22,7 +22,6 @@
 import com.android.car.telemetry.TelemetryProto;
 
 import java.io.File;
-import java.util.function.BiConsumer;
 
 /**
  * Lazy factory class for Publishers. It's expected to have a single factory instance.
@@ -43,7 +42,7 @@
     private CarTelemetrydPublisher mCarTelemetrydPublisher;
     private StatsPublisher mStatsPublisher;
 
-    private BiConsumer<AbstractPublisher, Throwable> mFailureConsumer;
+    private AbstractPublisher.PublisherFailureListener mFailureListener;
 
     public PublisherFactory(
             CarPropertyService carPropertyService,
@@ -64,19 +63,19 @@
                 case TelemetryProto.Publisher.VEHICLE_PROPERTY_FIELD_NUMBER:
                     if (mVehiclePropertyPublisher == null) {
                         mVehiclePropertyPublisher = new VehiclePropertyPublisher(
-                                mCarPropertyService, mFailureConsumer, mTelemetryHandler);
+                                mCarPropertyService, mFailureListener, mTelemetryHandler);
                     }
                     return mVehiclePropertyPublisher;
                 case TelemetryProto.Publisher.CARTELEMETRYD_FIELD_NUMBER:
                     if (mCarTelemetrydPublisher == null) {
                         mCarTelemetrydPublisher = new CarTelemetrydPublisher(
-                                mFailureConsumer, mTelemetryHandler);
+                                mFailureListener, mTelemetryHandler);
                     }
                     return mCarTelemetrydPublisher;
                 case TelemetryProto.Publisher.STATS_FIELD_NUMBER:
                     if (mStatsPublisher == null) {
                         mStatsPublisher = new StatsPublisher(
-                                mFailureConsumer, mStatsManager, mRootDirectory);
+                                mFailureListener, mStatsManager, mRootDirectory, mTelemetryHandler);
                     }
                     return mStatsPublisher;
                 default:
@@ -102,11 +101,12 @@
     }
 
     /**
-     * Sets the publisher failure consumer for all the publishers. This is expected to be called
-     * before {@link #getPublisher} method. This is not the best approach, but it suits for this
+     * Sets the publisher failure listener for all the publishers. This is expected to be called
+     * before {@link #getPublisher} method, because the listener is set after
+     * {@code PublisherFactory} initialized. This is not the best approach, but it suits for this
      * case.
      */
-    public void setFailureConsumer(BiConsumer<AbstractPublisher, Throwable> consumer) {
-        mFailureConsumer = consumer;
+    public void setFailureListener(AbstractPublisher.PublisherFailureListener listener) {
+        mFailureListener = listener;
     }
 }
diff --git a/service/src/com/android/car/telemetry/publisher/StatsManagerImpl.java b/service/src/com/android/car/telemetry/publisher/StatsManagerImpl.java
index b184f28..ef54b3c 100644
--- a/service/src/com/android/car/telemetry/publisher/StatsManagerImpl.java
+++ b/service/src/com/android/car/telemetry/publisher/StatsManagerImpl.java
@@ -41,4 +41,9 @@
     public void removeConfig(long configKey) throws StatsUnavailableException {
         mStatsManager.removeConfig(configKey);
     }
+
+    @Override
+    public byte[] getStatsMetadata() throws StatsUnavailableException {
+        return mStatsManager.getStatsMetadata();
+    }
 }
diff --git a/service/src/com/android/car/telemetry/publisher/StatsManagerProxy.java b/service/src/com/android/car/telemetry/publisher/StatsManagerProxy.java
index 046ac44..5507e28 100644
--- a/service/src/com/android/car/telemetry/publisher/StatsManagerProxy.java
+++ b/service/src/com/android/car/telemetry/publisher/StatsManagerProxy.java
@@ -29,4 +29,7 @@
 
     /** See {@link StatsManager#removeConfig(long)}. */
     void removeConfig(long configKey) throws StatsUnavailableException;
+
+    /** See {@link StatsManager#getStatsMetadata()}. */
+    byte[] getStatsMetadata() throws StatsUnavailableException;
 }
diff --git a/service/src/com/android/car/telemetry/publisher/StatsPublisher.java b/service/src/com/android/car/telemetry/publisher/StatsPublisher.java
index 00481fe..0d08091 100644
--- a/service/src/com/android/car/telemetry/publisher/StatsPublisher.java
+++ b/service/src/com/android/car/telemetry/publisher/StatsPublisher.java
@@ -20,8 +20,8 @@
 
 import android.app.StatsManager.StatsUnavailableException;
 import android.os.Handler;
-import android.os.Looper;
 import android.os.PersistableBundle;
+import android.os.Process;
 import android.util.LongSparseArray;
 import android.util.Slog;
 
@@ -33,7 +33,6 @@
 import com.android.car.telemetry.TelemetryProto;
 import com.android.car.telemetry.TelemetryProto.Publisher.PublisherCase;
 import com.android.car.telemetry.databroker.DataSubscriber;
-import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
@@ -46,10 +45,9 @@
 import java.io.IOException;
 import java.time.Duration;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.BiConsumer;
 
 /**
  * Publisher for {@link TelemetryProto.StatsPublisher}.
@@ -73,6 +71,8 @@
     @VisibleForTesting
     static final String SAVED_STATS_CONFIGS_FILE = "stats_config_keys_versions";
 
+    // TODO(b/202115033): Flatten the load spike by pulling reports for each MetricsConfigs
+    //                    using separate periodical timers.
     private static final Duration PULL_REPORTS_PERIOD = Duration.ofMinutes(10);
 
     private static final String BUNDLE_CONFIG_KEY_PREFIX = "statsd-publisher-config-id-";
@@ -103,15 +103,12 @@
                                     AtomsProto.ProcessMemoryState.SWAP_IN_BYTES_FIELD_NUMBER))
             .build();
 
-    // TODO(b/197766340): remove unnecessary lock
-    private final Object mLock = new Object();
-
     private final StatsManagerProxy mStatsManager;
     private final File mSavedStatsConfigsFile;
     private final Handler mTelemetryHandler;
 
     // True if the publisher is periodically pulling reports from StatsD.
-    private final AtomicBoolean mIsPullingReports = new AtomicBoolean(false);
+    private boolean mIsPullingReports = false;
 
     /** Assign the method to {@link Runnable}, otherwise the handler fails to remove it. */
     private final Runnable mPullReportsPeriodically = this::pullReportsPeriodically;
@@ -120,28 +117,18 @@
     // than 100 items. We're expecting much less number of subscribers, so these data structures
     // are ok.
     // Maps config_key to the set of DataSubscriber.
-    @GuardedBy("mLock")
     private final LongSparseArray<DataSubscriber> mConfigKeyToSubscribers = new LongSparseArray<>();
 
     private final PersistableBundle mSavedStatsConfigs;
 
-    // TODO(b/198331078): Use telemetry thread
     StatsPublisher(
-            BiConsumer<AbstractPublisher, Throwable> failureConsumer,
-            StatsManagerProxy statsManager,
-            File rootDirectory) {
-        this(failureConsumer, statsManager, rootDirectory, new Handler(Looper.myLooper()));
-    }
-
-    @VisibleForTesting
-    StatsPublisher(
-            BiConsumer<AbstractPublisher, Throwable> failureConsumer,
+            PublisherFailureListener failureListener,
             StatsManagerProxy statsManager,
             File rootDirectory,
-            Handler handler) {
-        super(failureConsumer);
+            Handler telemetryHandler) {
+        super(failureListener);
         mStatsManager = statsManager;
-        mTelemetryHandler = handler;
+        mTelemetryHandler = telemetryHandler;
         mSavedStatsConfigsFile = new File(rootDirectory, SAVED_STATS_CONFIGS_FILE);
         mSavedStatsConfigs = loadBundle();
     }
@@ -182,15 +169,16 @@
         Preconditions.checkArgument(
                 publisherParam.getPublisherCase() == PublisherCase.STATS,
                 "Subscribers only with StatsPublisher are supported by this class.");
-        synchronized (mLock) {
-            long configKey = addStatsConfigLocked(subscriber);
-            mConfigKeyToSubscribers.put(configKey, subscriber);
-        }
 
-        if (!mIsPullingReports.getAndSet(true)) {
+        long configKey = buildConfigKey(subscriber);
+        mConfigKeyToSubscribers.put(configKey, subscriber);
+        addStatsConfig(configKey, subscriber);
+        if (!mIsPullingReports) {
             Slog.d(CarLog.TAG_TELEMETRY, "Stats report will be pulled in "
                     + PULL_REPORTS_PERIOD.toMinutes() + " minutes.");
-            mTelemetryHandler.postDelayed(mPullReportsPeriodically, PULL_REPORTS_PERIOD.toMillis());
+            mIsPullingReports = true;
+            mTelemetryHandler.postDelayed(
+                    mPullReportsPeriodically, PULL_REPORTS_PERIOD.toMillis());
         }
     }
 
@@ -199,7 +187,7 @@
         if (report.getReportsCount() == 0) {
             return;
         }
-        DataSubscriber subscriber = getSubscriberByConfigKey(configKey);
+        DataSubscriber subscriber = mConfigKeyToSubscribers.get(configKey);
         if (subscriber == null) {
             Slog.w(CarLog.TAG_TELEMETRY, "No subscribers found for config " + configKey);
             return;
@@ -238,39 +226,61 @@
         }
     }
 
-    private void pullReportsPeriodically() {
-        for (long configKey : getActiveConfigKeys()) {
-            try {
-                processReport(configKey, StatsLogProto.ConfigMetricsReportList.parseFrom(
-                        mStatsManager.getReports(configKey)));
-            } catch (StatsUnavailableException e) {
-                // If the StatsD is not available, retry in the next pullReportsPeriodically call.
-                break;
-            } catch (InvalidProtocolBufferException e) {
-                // This case should never happen.
-                Slog.w(CarLog.TAG_TELEMETRY,
-                        "Failed to parse report from statsd, configKey=" + configKey);
+    private void processStatsMetadata(StatsLogProto.StatsdStatsReport statsReport) {
+        int myUid = Process.myUid();
+        // configKey and StatsdConfig.id are the same, see this#addStatsConfig().
+        HashSet<Long> activeConfigKeys = new HashSet<>(getActiveConfigKeys());
+        HashSet<TelemetryProto.MetricsConfig> failedConfigs = new HashSet<>();
+        for (int i = 0; i < statsReport.getConfigStatsCount(); i++) {
+            StatsLogProto.StatsdStatsReport.ConfigStats stats = statsReport.getConfigStats(i);
+            if (stats.getUid() != myUid || !activeConfigKeys.contains(stats.getId())) {
+                continue;
+            }
+            if (!stats.getIsValid()) {
+                Slog.w(CarLog.TAG_TELEMETRY, "Config key " + stats.getId() + " is invalid.");
+                failedConfigs.add(mConfigKeyToSubscribers.get(stats.getId()).getMetricsConfig());
             }
         }
+        if (!failedConfigs.isEmpty()) {
+            // Notify DataBroker so it can disable invalid MetricsConfigs.
+            onPublisherFailure(
+                    new ArrayList<>(failedConfigs),
+                    new IllegalStateException("Found invalid configs"));
+        }
+    }
 
-        if (mIsPullingReports.get()) {
+    private void pullReportsPeriodically() {
+        if (mIsPullingReports) {
             Slog.d(CarLog.TAG_TELEMETRY, "Stats report will be pulled in "
                     + PULL_REPORTS_PERIOD.toMinutes() + " minutes.");
             mTelemetryHandler.postDelayed(mPullReportsPeriodically, PULL_REPORTS_PERIOD.toMillis());
         }
+
+        try {
+            // TODO(b/202131100): Get the active list of configs using
+            //                    StatsManager#setActiveConfigsChangedOperation()
+            processStatsMetadata(
+                    StatsLogProto.StatsdStatsReport.parseFrom(mStatsManager.getStatsMetadata()));
+
+            for (long configKey : getActiveConfigKeys()) {
+                processReport(configKey, StatsLogProto.ConfigMetricsReportList.parseFrom(
+                        mStatsManager.getReports(configKey)));
+            }
+        } catch (InvalidProtocolBufferException | StatsUnavailableException e) {
+            // If the StatsD is not available, retry in the next pullReportsPeriodically call.
+            Slog.w(CarLog.TAG_TELEMETRY, e);
+        }
     }
 
     private List<Long> getActiveConfigKeys() {
         ArrayList<Long> result = new ArrayList<>();
-        synchronized (mLock) {
-            for (String key : mSavedStatsConfigs.keySet()) {
-                // filter out all the config versions
-                if (!key.startsWith(BUNDLE_CONFIG_KEY_PREFIX)) {
-                    continue;
-                }
-                // the remaining values are config keys
-                result.add(mSavedStatsConfigs.getLong(key));
+        for (String key : mSavedStatsConfigs.keySet()) {
+            // filter out all the config versions
+            if (!key.startsWith(BUNDLE_CONFIG_KEY_PREFIX)) {
+                continue;
             }
+            // the remaining values are config keys
+            result.add(mSavedStatsConfigs.getLong(key));
         }
         return result;
     }
@@ -290,49 +300,49 @@
                             + publisherParam.getPublisherCase().name());
             return;
         }
-        synchronized (mLock) {
-            long configKey = removeStatsConfigLocked(subscriber);
-            mConfigKeyToSubscribers.remove(configKey);
-        }
-
+        long configKey = removeStatsConfig(subscriber);
+        mConfigKeyToSubscribers.remove(configKey);
         if (mConfigKeyToSubscribers.size() == 0) {
-            mIsPullingReports.set(false);
+            mIsPullingReports = false;
             mTelemetryHandler.removeCallbacks(mPullReportsPeriodically);
         }
     }
 
-    /** Removes all the subscribers from the publisher removes StatsdConfigs from StatsD service. */
+    /**
+     * Removes all the subscribers from the publisher removes StatsdConfigs from StatsD service.
+     */
     @Override
     public void removeAllDataSubscribers() {
-        synchronized (mLock) {
-            for (String key : mSavedStatsConfigs.keySet()) {
-                // filter out all the config versions
-                if (!key.startsWith(BUNDLE_CONFIG_KEY_PREFIX)) {
-                    continue;
-                }
-                // the remaining values are config keys
-                long configKey = mSavedStatsConfigs.getLong(key);
-                try {
-                    mStatsManager.removeConfig(configKey);
-                    String bundleVersion = buildBundleConfigVersionKey(configKey);
-                    mSavedStatsConfigs.remove(key);
-                    mSavedStatsConfigs.remove(bundleVersion);
-                } catch (StatsUnavailableException e) {
-                    Slog.w(CarLog.TAG_TELEMETRY, "Failed to remove config " + configKey
-                            + ". Ignoring the failure. Will retry removing again when"
-                            + " removeAllDataSubscribers() is called.", e);
-                    // If it cannot remove statsd config, it's less likely it can delete it even if
-                    // retry. So we will just ignore the failures. The next call of this method
-                    // will ry deleting StatsD configs again.
-                }
+        for (String key : mSavedStatsConfigs.keySet()) {
+            // filter out all the config versions
+            if (!key.startsWith(BUNDLE_CONFIG_KEY_PREFIX)) {
+                continue;
             }
-            saveBundle();
-            mSavedStatsConfigs.clear();
+            // the remaining values are config keys
+            long configKey = mSavedStatsConfigs.getLong(key);
+            try {
+                mStatsManager.removeConfig(configKey);
+                String bundleVersion = buildBundleConfigVersionKey(configKey);
+                mSavedStatsConfigs.remove(key);
+                mSavedStatsConfigs.remove(bundleVersion);
+            } catch (StatsUnavailableException e) {
+                Slog.w(CarLog.TAG_TELEMETRY, "Failed to remove config " + configKey
+                        + ". Ignoring the failure. Will retry removing again when"
+                        + " removeAllDataSubscribers() is called.", e);
+                // If it cannot remove statsd config, it's less likely it can delete it even if
+                // retry. So we will just ignore the failures. The next call of this method
+                // will ry deleting StatsD configs again.
+            }
         }
-        mIsPullingReports.set(false);
+        saveBundle();
+        mSavedStatsConfigs.clear();
+        mIsPullingReports = false;
         mTelemetryHandler.removeCallbacks(mPullReportsPeriodically);
     }
 
+    /**
+     * Returns true if the publisher has the subscriber.
+     */
     @Override
     public boolean hasDataSubscriber(DataSubscriber subscriber) {
         TelemetryProto.Publisher publisherParam = subscriber.getPublisherParam();
@@ -340,16 +350,16 @@
             return false;
         }
         long configKey = buildConfigKey(subscriber);
-        synchronized (mLock) {
-            return mConfigKeyToSubscribers.indexOfKey(configKey) >= 0;
-        }
+        return mConfigKeyToSubscribers.indexOfKey(configKey) >= 0;
     }
 
-    /** Returns a subscriber for the given statsd config key. Returns null if not found. */
-    private DataSubscriber getSubscriberByConfigKey(long configKey) {
-        synchronized (mLock) {
-            return mConfigKeyToSubscribers.get(configKey);
+    /** Returns all the {@link TelemetryProto.MetricsConfig} associated with added subscribers. */
+    private List<TelemetryProto.MetricsConfig> getMetricsConfigs() {
+        HashSet<TelemetryProto.MetricsConfig> uniqueConfigs = new HashSet<>();
+        for (int i = 0; i < mConfigKeyToSubscribers.size(); i++) {
+            uniqueConfigs.add(mConfigKeyToSubscribers.valueAt(i).getMetricsConfig());
         }
+        return new ArrayList<>(uniqueConfigs);
     }
 
     /**
@@ -374,16 +384,14 @@
      * previously added config_keys in the persistable bundle and only updates StatsD when
      * the MetricsConfig (of CarTelemetryService) has a new version.
      */
-    @GuardedBy("mLock")
-    private long addStatsConfigLocked(DataSubscriber subscriber) {
-        long configKey = buildConfigKey(subscriber);
+    private void addStatsConfig(long configKey, DataSubscriber subscriber) {
         // Store MetricsConfig (of CarTelemetryService) version per handler_function.
         String bundleVersion = buildBundleConfigVersionKey(configKey);
         if (mSavedStatsConfigs.getInt(bundleVersion) != 0) {
             int currentVersion = mSavedStatsConfigs.getInt(bundleVersion);
             if (currentVersion >= subscriber.getMetricsConfig().getVersion()) {
                 // It's trying to add current or older MetricsConfig version, just ignore it.
-                return configKey;
+                return;
             }  // if the subscriber's MetricsConfig version is newer, it will replace the old one.
         }
         String bundleConfigKey = buildBundleConfigKey(subscriber);
@@ -397,18 +405,15 @@
             saveBundle();
         } catch (StatsUnavailableException e) {
             Slog.w(CarLog.TAG_TELEMETRY, "Failed to add config" + configKey, e);
-            // TODO(b/189143813): if StatsManager is not ready, retry N times and hard fail after
-            //                    by notifying DataBroker.
             // We will notify the failure immediately, as we're expecting StatsManager to be stable.
-            notifyFailureConsumer(
+            onPublisherFailure(
+                    getMetricsConfigs(),
                     new IllegalStateException("Failed to add config " + configKey, e));
         }
-        return configKey;
     }
 
     /** Removes StatsdConfig and returns configKey. */
-    @GuardedBy("mLock")
-    private long removeStatsConfigLocked(DataSubscriber subscriber) {
+    private long removeStatsConfig(DataSubscriber subscriber) {
         String bundleConfigKey = buildBundleConfigKey(subscriber);
         long configKey = buildConfigKey(subscriber);
         // Store MetricsConfig (of CarTelemetryService) version per handler_function.
diff --git a/service/src/com/android/car/telemetry/publisher/VehiclePropertyPublisher.java b/service/src/com/android/car/telemetry/publisher/VehiclePropertyPublisher.java
index dbfc002..d723d1c 100644
--- a/service/src/com/android/car/telemetry/publisher/VehiclePropertyPublisher.java
+++ b/service/src/com/android/car/telemetry/publisher/VehiclePropertyPublisher.java
@@ -35,7 +35,6 @@
 import com.android.internal.util.Preconditions;
 
 import java.util.List;
-import java.util.function.BiConsumer;
 
 /**
  * Publisher for Vehicle Property changes, aka {@code CarPropertyService}.
@@ -77,8 +76,8 @@
             };
 
     public VehiclePropertyPublisher(CarPropertyService carPropertyService,
-            BiConsumer<AbstractPublisher, Throwable> failureConsumer, Handler handler) {
-        super(failureConsumer);
+            PublisherFailureListener failureListener, Handler handler) {
+        super(failureListener);
         mCarPropertyService = carPropertyService;
         mTelemetryHandler = handler;
         // Load car property list once, as the list doesn't change runtime.
diff --git a/service/src/com/android/car/watchdog/CarWatchdogService.java b/service/src/com/android/car/watchdog/CarWatchdogService.java
index 93243d4..318d132 100644
--- a/service/src/com/android/car/watchdog/CarWatchdogService.java
+++ b/service/src/com/android/car/watchdog/CarWatchdogService.java
@@ -77,6 +77,7 @@
             "com.android.server.jobscheduler.GARAGE_MODE_ON";
     static final String ACTION_GARAGE_MODE_OFF =
             "com.android.server.jobscheduler.GARAGE_MODE_OFF";
+    static final int MISSING_ARG_VALUE = -1;
     static final TimeSourceInterface SYSTEM_INSTANCE = new TimeSourceInterface() {
         @Override
         public Instant now() {
@@ -106,25 +107,15 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             final String action = intent.getAction();
-            boolean isGarageMode = false;
-            if (action.equals(ACTION_GARAGE_MODE_ON)) {
-                isGarageMode = true;
-            } else if (!action.equals(ACTION_GARAGE_MODE_OFF)) {
-                return;
-            }
-            if (isGarageMode) {
-                mWatchdogStorage.shrinkDatabase();
-            }
-            try {
-                mCarWatchdogDaemonHelper.notifySystemStateChange(StateType.GARAGE_MODE,
-                        isGarageMode ? GarageMode.GARAGE_MODE_ON : GarageMode.GARAGE_MODE_OFF,
-                        /* arg2= */ -1);
-                if (DEBUG) {
-                    Slogf.d(TAG, "Notified car watchdog daemon of garage mode(%s)",
-                            isGarageMode ? "ON" : "OFF");
-                }
-            } catch (RemoteException | RuntimeException e) {
-                Slogf.w(TAG, "Notifying garage mode state change failed: %s", e);
+            switch (action) {
+                case ACTION_GARAGE_MODE_ON:
+                case ACTION_GARAGE_MODE_OFF:
+                    handleGarageModeIntent(action.equals(ACTION_GARAGE_MODE_ON));
+                    break;
+                case Intent.ACTION_USER_REMOVED:
+                    UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
+                    mWatchdogPerfHandler.deleteUser(user.getIdentifier());
+                    break;
             }
         }
     };
@@ -361,6 +352,23 @@
         mWatchdogPerfHandler.setTimeSource(timeSource);
     }
 
+    private void handleGarageModeIntent(boolean isOn) {
+        if (isOn) {
+            mWatchdogStorage.shrinkDatabase();
+        }
+        try {
+            mCarWatchdogDaemonHelper.notifySystemStateChange(StateType.GARAGE_MODE,
+                    isOn ? GarageMode.GARAGE_MODE_ON : GarageMode.GARAGE_MODE_OFF,
+                    /* arg2= */ MISSING_ARG_VALUE);
+            if (DEBUG) {
+                Slogf.d(TAG, "Notified car watchdog daemon of garage mode(%s)",
+                        isOn ? "ON" : "OFF");
+            }
+        } catch (RemoteException | RuntimeException e) {
+            Slogf.w(TAG, e, "Notifying garage mode state change failed");
+        }
+    }
+
     private void postRegisterToDaemonMessage() {
         CarServiceUtils.runOnMain(() -> {
             synchronized (mLock) {
@@ -447,7 +455,7 @@
                 }
                 try {
                     mCarWatchdogDaemonHelper.notifySystemStateChange(StateType.POWER_CYCLE,
-                            powerCycle, /* arg2= */ -1);
+                            powerCycle, /* arg2= */ MISSING_ARG_VALUE);
                     if (DEBUG) {
                         Slogf.d(TAG, "Notified car watchdog daemon of power cycle(%d)", powerCycle);
                     }
@@ -499,6 +507,7 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(ACTION_GARAGE_MODE_ON);
         filter.addAction(ACTION_GARAGE_MODE_OFF);
+        filter.addAction(Intent.ACTION_USER_REMOVED);
 
         mContext.registerReceiverForAllUsers(mBroadcastReceiver, filter, null, null);
     }
diff --git a/service/src/com/android/car/watchdog/WatchdogPerfHandler.java b/service/src/com/android/car/watchdog/WatchdogPerfHandler.java
index 6cf7794..956161b 100644
--- a/service/src/com/android/car/watchdog/WatchdogPerfHandler.java
+++ b/service/src/com/android/car/watchdog/WatchdogPerfHandler.java
@@ -68,7 +68,6 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -401,12 +400,11 @@
     }
 
     private void setPackageKillableStateForAllUsers(String packageName, boolean isKillable) {
-        UserManager userManager = UserManager.get(mContext);
-        List<UserInfo> userInfos = userManager.getAliveUsers();
+        int[] userIds = getAliveUserIds();
         String genericPackageName = null;
         synchronized (mLock) {
-            for (int i = 0; i < userInfos.size(); ++i) {
-                int userId = userInfos.get(i).id;
+            for (int i = 0; i < userIds.length; ++i) {
+                int userId = userIds[i];
                 String name = mPackageInfoHandler.getNameForUserPackage(packageName, userId);
                 if (name == null) {
                     continue;
@@ -449,11 +447,10 @@
             return getPackageKillableStatesForUserId(userHandle.getIdentifier(), pm);
         }
         List<PackageKillableState> packageKillableStates = new ArrayList<>();
-        UserManager userManager = UserManager.get(mContext);
-        List<UserInfo> userInfos = userManager.getAliveUsers();
-        for (int i = 0; i < userInfos.size(); ++i) {
+        int[] userIds = getAliveUserIds();
+        for (int i = 0; i < userIds.length; ++i) {
             packageKillableStates.addAll(
-                    getPackageKillableStatesForUserId(userInfos.get(i).id, pm));
+                    getPackageKillableStatesForUserId(userIds[i], pm));
         }
         if (DEBUG) {
             Slogf.d(TAG, "Returning all package killable states for all users");
@@ -749,6 +746,21 @@
         }
     }
 
+    /** Deletes all data for specific user. */
+    public void deleteUser(@UserIdInt int userId) {
+        synchronized (mLock) {
+            for (int i = mUsageByUserPackage.size() - 1; i >= 0; --i) {
+                if (userId == mUsageByUserPackage.valueAt(i).userId) {
+                    mUsageByUserPackage.removeAt(i);
+                }
+            }
+            mWatchdogStorage.syncUsers(getAliveUserIds());
+        }
+        if (DEBUG) {
+            Slogf.d(TAG, "Resource usage for user id: %d was deleted.", userId);
+        }
+    }
+
     /** Sets the time source. */
     public void setTimeSource(TimeSourceInterface timeSource) {
         synchronized (mLock) {
@@ -801,6 +813,7 @@
     }
 
     private void readFromDatabase() {
+        mWatchdogStorage.syncUsers(getAliveUserIds());
         List<WatchdogStorage.UserPackageSettingsEntry> settingsEntries =
                 mWatchdogStorage.getUserPackageSettings();
         Slogf.i(TAG, "Read %d user package settings from database", settingsEntries.size());
@@ -1225,6 +1238,17 @@
         }
     }
 
+    private int[] getAliveUserIds() {
+        UserManager userManager = UserManager.get(mContext);
+        List<UserHandle> aliveUsers = userManager.getUserHandles(/* excludeDying= */ true);
+        int userSize = aliveUsers.size();
+        int[] userIds = new int[userSize];
+        for (int i = 0; i < userSize; ++i) {
+            userIds[i] = aliveUsers.get(i).getIdentifier();
+        }
+        return userIds;
+    }
+
     private static String getUserPackageUniqueId(int userId, String genericPackageName) {
         return String.valueOf(userId) + ":" + genericPackageName;
     }
diff --git a/service/src/com/android/car/watchdog/WatchdogStorage.java b/service/src/com/android/car/watchdog/WatchdogStorage.java
index 78b6927..42f6313 100644
--- a/service/src/com/android/car/watchdog/WatchdogStorage.java
+++ b/service/src/com/android/car/watchdog/WatchdogStorage.java
@@ -32,6 +32,7 @@
 import android.os.Process;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.IntArray;
 import android.util.Slog;
 
 import com.android.car.CarLog;
@@ -196,6 +197,25 @@
         }
     }
 
+    /**
+     * Deletes all user package settings and resource stats for all non-alive users.
+     *
+     * @param aliveUserIds Array of alive user ids.
+     */
+    public void syncUsers(int[] aliveUserIds) {
+        IntArray aliveUsers = IntArray.wrap(aliveUserIds);
+        for (int i = mUserPackagesByKey.size() - 1; i >= 0; --i) {
+            UserPackage userPackage = mUserPackagesByKey.valueAt(i);
+            if (aliveUsers.indexOf(userPackage.getUserId()) == -1) {
+                mUserPackagesByKey.removeAt(i);
+                mUserPackagesById.remove(userPackage.getUniqueId());
+            }
+        }
+        try (SQLiteDatabase db = mDbHelper.getWritableDatabase()) {
+            UserPackageSettingsTable.syncUserPackagesWithAliveUsers(db, aliveUsers);
+        }
+    }
+
     @VisibleForTesting
     boolean saveIoUsageStats(List<IoUsageStatsEntry> entries, boolean shouldCheckRetention) {
         ZonedDateTime currentDate =
@@ -371,6 +391,24 @@
             Slogf.i(TAG, "Deleted %d user package settings db rows for user %d and package %s",
                     deletedRows, userId, packageName);
         }
+
+        public static void syncUserPackagesWithAliveUsers(SQLiteDatabase db, IntArray aliveUsers) {
+            StringBuilder queryBuilder = new StringBuilder();
+            for (int i = 0; i < aliveUsers.size(); ++i) {
+                if (i == 0) {
+                    queryBuilder.append(COLUMN_USER_ID).append(" NOT IN (");
+                } else {
+                    queryBuilder.append(", ");
+                }
+                queryBuilder.append(aliveUsers.get(i));
+                if (i == aliveUsers.size() - 1) {
+                    queryBuilder.append(")");
+                }
+            }
+            int deletedRows = db.delete(TABLE_NAME, queryBuilder.toString(), new String[]{});
+            Slogf.i(TAG, "Deleted %d user package settings db rows while syncing with alive users",
+                    deletedRows);
+        }
     }
 
     /** Defines the I/O usage entry stored in the IoUsageStatsTable. */
diff --git a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
index 5c5029b..3b709b5 100644
--- a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
+++ b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
@@ -46,6 +46,8 @@
     <uses-permission android:name="android.car.permission.CAR_VENDOR_EXTENSION"/>
     <!-- use for CarServiceTest -->
     <uses-permission android:name="android.car.permission.CAR_UX_RESTRICTIONS_CONFIGURATION"/>
+    <!-- use for AndroidCarApiTest -->
+    <uses-permission android:name="android.car.permission.CONTROL_CAR_APP_LAUNCH"/>
     <uses-permission android:name="android.car.permission.CONTROL_CAR_CLIMATE"/>
     <uses-permission android:name="android.car.permission.READ_CAR_STEERING"/>
     <uses-permission android:name="android.car.permission.STORAGE_MONITORING"/>
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/car_telemetry_test.xml b/tests/EmbeddedKitchenSinkApp/res/layout/car_telemetry_test.xml
index 3110d82..39c53fa 100644
--- a/tests/EmbeddedKitchenSinkApp/res/layout/car_telemetry_test.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/car_telemetry_test.xml
@@ -19,47 +19,64 @@
               android:layout_height="match_parent"
               android:orientation="vertical">
     <LinearLayout
-        android:id="@+id/on_gear_change_layout"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:orientation="horizontal">
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/gear_change_config"/>
         <Button
             android:id="@+id/send_on_gear_change_config"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:text="@string/send_on_gear_change"/>
+            android:text="@string/add_metrics_config"/>
         <Button
             android:id="@+id/remove_on_gear_change_config"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:text="@string/remove_on_gear_change"/>
+            android:text="@string/remove_metrics_config"/>
         <Button
             android:id="@+id/get_on_gear_change_report"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:text="@string/get_on_gear_change"/>
+            android:text="@string/get_report"/>
     </LinearLayout>
 
     <LinearLayout
-        android:id="@+id/on_process_memory_state_layout"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:orientation="horizontal">
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/process_memory_config"/>
         <Button
             android:id="@+id/send_on_process_memory_config"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:text="@string/send_process_memory_state_config"/>
+            android:text="@string/add_metrics_config"/>
         <Button
             android:id="@+id/remove_on_process_memory_config"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:text="@string/remove_process_memory_state_config"/>
+            android:text="@string/remove_metrics_config"/>
         <Button
             android:id="@+id/get_on_process_memory_report"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:text="@string/get_report_process_memory_state_config"/>
+            android:text="@string/get_report"/>
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal">
+        <Button
+            android:id="@+id/show_mem_info_btn"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/show_mem_info"/>
     </LinearLayout>
 
     <TextView
diff --git a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
index 77e3989..4fcb9f3 100644
--- a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
@@ -383,10 +383,10 @@
     <string name="cancel" translatable="false">Cancel</string>
 
     <!-- CarTelemetryService Test -->
-    <string name="send_on_gear_change" translatable="false">Send MetricsConfig on_gear_change</string>
-    <string name="remove_on_gear_change" translatable="false">Remove MetricsConfig on_gear_change</string>
-    <string name="get_on_gear_change" translatable="false">Get Report on_gear_change</string>
-    <string name="send_process_memory_state_config" translatable="false">Send MetricsConfig process_memory</string>
-    <string name="remove_process_memory_state_config" translatable="false">Remove MetricsConfig process_memory</string>
-    <string name="get_report_process_memory_state_config" translatable="false">Get Report process_memory</string>
+    <string name="gear_change_config" translate="false">on_gear_change:</string>
+    <string name="process_memory_config" translate="false">process_memory:</string>
+    <string name="add_metrics_config" translatable="false">Add MetricsConfig</string>
+    <string name="remove_metrics_config" translatable="false">Remove MetricsConfig</string>
+    <string name="get_report" translatable="false">Get Report</string>
+    <string name="show_mem_info" translate="false">Show Memory Info</string>
 </resources>
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/telemetry/CarTelemetryTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/telemetry/CarTelemetryTestFragment.java
index 5a76abc..5f548ed 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/telemetry/CarTelemetryTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/telemetry/CarTelemetryTestFragment.java
@@ -20,6 +20,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.car.telemetry.CarTelemetryManager;
 import android.car.telemetry.MetricsConfigKey;
 import android.hardware.automotive.vehicle.V2_0.VehicleProperty;
@@ -28,7 +29,6 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.Button;
 import android.widget.TextView;
 
 import androidx.fragment.app.Fragment;
@@ -41,6 +41,8 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 
@@ -146,30 +148,30 @@
             @Nullable ViewGroup container,
             @Nullable Bundle savedInstanceState) {
         View view = inflater.inflate(R.layout.car_telemetry_test, container, false);
-
         mOutputTextView = view.findViewById(R.id.output_textview);
-        Button sendGearConfigBtn = view.findViewById(R.id.send_on_gear_change_config);
-        Button getGearReportBtn = view.findViewById(R.id.get_on_gear_change_report);
-        Button removeGearConfigBtn = view.findViewById(R.id.remove_on_gear_change_config);
-
-        sendGearConfigBtn.setOnClickListener(this::onSendGearChangeConfigBtnClick);
-        removeGearConfigBtn.setOnClickListener(this::onRemoveGearChangeConfigBtnClick);
-        getGearReportBtn.setOnClickListener(this::onGetGearChangeReportBtnClick);
-
-        mOutputTextView = view.findViewById(R.id.output_textview);
-        Button sendProcessMemConfigBtn = view.findViewById(R.id.send_on_process_memory_config);
-        Button getProcessMemReportBtn = view.findViewById(R.id.get_on_process_memory_report);
-        Button removeProcessMemConfigBtn = view.findViewById(R.id.remove_on_process_memory_config);
-
-        sendProcessMemConfigBtn.setOnClickListener(this::onSendProcessMemoryConfigBtnClick);
-        removeProcessMemConfigBtn.setOnClickListener(this::onRemoveProcessMemoryConfigBtnClick);
-        getProcessMemReportBtn.setOnClickListener(this::onGetProcessMemoryReportBtnClick);
-
+        view.findViewById(R.id.send_on_gear_change_config)
+                .setOnClickListener(this::onSendGearChangeConfigBtnClick);
+        view.findViewById(R.id.remove_on_gear_change_config)
+                .setOnClickListener(this::onRemoveGearChangeConfigBtnClick);
+        view.findViewById(R.id.get_on_gear_change_report)
+                .setOnClickListener(this::onGetGearChangeReportBtnClick);
+        view.findViewById(R.id.send_on_process_memory_config)
+                .setOnClickListener(this::onSendProcessMemoryConfigBtnClick);
+        view.findViewById(R.id.remove_on_process_memory_config)
+                .setOnClickListener(this::onRemoveProcessMemoryConfigBtnClick);
+        view.findViewById(R.id.get_on_process_memory_report)
+                .setOnClickListener(this::onGetProcessMemoryReportBtnClick);
+        view.findViewById(R.id.show_mem_info_btn).setOnClickListener(this::onShowMemInfoBtnClick);
         return view;
     }
 
     private void showOutput(String s) {
-        mActivity.runOnUiThread(() -> mOutputTextView.setText(s));
+        mActivity.runOnUiThread(() -> {
+            String now = LocalDateTime.now()
+                    .format(DateTimeFormatter.ofPattern("HH:mm:ss.SSS"));
+            mOutputTextView.setText(
+                    now + " : " + s + "\n" + mOutputTextView.getText());
+        });
     }
 
     private void onSendGearChangeConfigBtnClick(View view) {
@@ -184,13 +186,13 @@
     }
 
     private void onGetGearChangeReportBtnClick(View view) {
-        showOutput("Fetching report... If nothing shows up after a few seconds, "
-                + "then no result exists");
+        showOutput("Fetching report for on_gear_change... If nothing shows up within 5 seconds, "
+                + "there is no result yet");
         mCarTelemetryManager.sendFinishedReports(ON_GEAR_CHANGE_KEY_V1);
     }
 
     private void onSendProcessMemoryConfigBtnClick(View view) {
-        showOutput("Sending MetricsConfig that listen for process memory state...");
+        showOutput("Sending MetricsConfig that listens for process memory state...");
         mCarTelemetryManager.addMetricsConfig(PROCESS_MEMORY_KEY_V1,
                 METRICS_CONFIG_PROCESS_MEMORY_V1.toByteArray());
     }
@@ -201,11 +203,27 @@
     }
 
     private void onGetProcessMemoryReportBtnClick(View view) {
-        showOutput("Fetching report for process memory state... If nothing shows up after "
-                + "a few seconds, then no result exists");
+        showOutput("Fetching report for process memory state... If nothing shows up within 5 "
+                + "seconds, there is no result yet");
         mCarTelemetryManager.sendFinishedReports(PROCESS_MEMORY_KEY_V1);
     }
 
+    /** Gets a MemoryInfo object for the device's current memory status. */
+    private ActivityManager.MemoryInfo getAvailableMemory() {
+        ActivityManager activityManager = getActivity().getSystemService(ActivityManager.class);
+        ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
+        activityManager.getMemoryInfo(memoryInfo);
+        return memoryInfo;
+    }
+
+    private void onShowMemInfoBtnClick(View view) {
+        // Use android's "alloc-stress" system tool to create an artificial memory pressure.
+        ActivityManager.MemoryInfo info = getAvailableMemory();
+        showOutput("MemoryInfo availMem=" + (info.availMem / 1024 / 1024) + "/"
+                + (info.totalMem / 1024 / 1024) + "mb, isLowMem=" + info.lowMemory
+                + ", threshold=" + (info.threshold / 1024 / 1024) + "mb");
+    }
+
     @Override
     public void onDestroyView() {
         super.onDestroyView();
@@ -227,7 +245,7 @@
             } catch (IOException e) {
                 bundle = null;
             }
-            showOutput("Result is " + bundle.toString());
+            showOutput("Result for " + key.getName() + ": " + bundle.toString());
         }
 
         @Override
@@ -235,16 +253,34 @@
             try {
                 TelemetryProto.TelemetryError telemetryError =
                         TelemetryProto.TelemetryError.parseFrom(error);
-                showOutput("Error is " + telemetryError);
+                showOutput("Error for " + key.getName() + ": " + telemetryError);
             } catch (InvalidProtocolBufferException e) {
                 showOutput("Unable to parse error result for MetricsConfig " + key.getName()
-                        + ". " + e.getMessage());
+                        + ": " + e.getMessage());
             }
         }
 
         @Override
         public void onAddMetricsConfigStatus(@NonNull MetricsConfigKey key, int statusCode) {
-            showOutput("Add MetricsConfig status: " + statusCode);
+            showOutput("Add MetricsConfig status for " + key.getName() + ": "
+                    + statusCodeToString(statusCode));
+        }
+
+        private String statusCodeToString(int statusCode) {
+            switch (statusCode) {
+                case CarTelemetryManager.ERROR_METRICS_CONFIG_NONE:
+                    return "SUCCESS";
+                case CarTelemetryManager.ERROR_METRICS_CONFIG_ALREADY_EXISTS:
+                    return "ERROR ALREADY_EXISTS";
+                case CarTelemetryManager.ERROR_METRICS_CONFIG_VERSION_TOO_OLD:
+                    return "ERROR VERSION_TOO_OLD";
+                case CarTelemetryManager.ERROR_METRICS_CONFIG_PARSE_FAILED:
+                    return "ERROR PARSE_FAILED";
+                case CarTelemetryManager.ERROR_METRICS_CONFIG_SIGNATURE_VERIFICATION_FAILED:
+                    return "ERROR SIGNATURE_VERIFICATION_FAILED";
+                default:
+                    return "ERROR UNKNOWN";
+            }
         }
     }
 }
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarActivityManagerTest.java b/tests/android_car_api_test/src/android/car/apitest/CarActivityManagerTest.java
new file mode 100644
index 0000000..f1d90be
--- /dev/null
+++ b/tests/android_car_api_test/src/android/car/apitest/CarActivityManagerTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.apitest;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.car.Car;
+import android.car.app.CarActivityManager;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.view.Display;
+
+import org.junit.Before;
+import org.junit.Test;
+
+@MediumTest
+public class CarActivityManagerTest extends CarApiTestBase {
+    private static final String TAG = CarActivityManagerTest.class.getSimpleName();
+
+    // Comes from android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER
+    private static final int FEATURE_DEFAULT_TASK_CONTAINER = 1;
+
+    // Comes from android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED
+    private static final int FEATURE_UNDEFINED = -1;
+
+    private CarActivityManager mCarActivityManager;
+
+    private final ComponentName mTestActivity = new ComponentName("test.pkg", "test.activity");
+
+    @Before
+    public void setUp() throws Exception {
+        mCarActivityManager = (CarActivityManager) getCar().getCarManager(Car.CAR_ACTIVITY_SERVICE);
+        assertThat(mCarActivityManager).isNotNull();
+    }
+
+    @Test
+    public void testSetPersistentActivity() {
+        // Set
+        int retSet = mCarActivityManager.setPersistentActivity(
+                mTestActivity, Display.DEFAULT_DISPLAY, FEATURE_DEFAULT_TASK_CONTAINER);
+        assertThat(retSet).isEqualTo(CarActivityManager.RESULT_SUCCESS);
+
+        // Remove
+        int retRemove = mCarActivityManager.setPersistentActivity(
+                mTestActivity, Display.DEFAULT_DISPLAY, FEATURE_UNDEFINED);
+        assertThat(retRemove).isEqualTo(CarActivityManager.RESULT_SUCCESS);
+    }
+
+    @Test
+    public void testSetPersistentActivity_throwsExceptionForInvalidDisplayId() {
+        int invalidDisplayId = 999999990;
+
+        assertThrows(IllegalArgumentException.class,
+                () -> mCarActivityManager.setPersistentActivity(
+                        mTestActivity, invalidDisplayId, FEATURE_DEFAULT_TASK_CONTAINER));
+    }
+
+    @Test
+    public void testSetPersistentActivity_throwsExceptionForInvalidFeatureId() {
+        int unknownFeatureId = 999999990;
+
+        assertThrows(IllegalArgumentException.class,
+                () -> mCarActivityManager.setPersistentActivity(
+                        mTestActivity, Display.DEFAULT_DISPLAY, unknownFeatureId));
+    }
+
+    @Test
+    public void testSetPersistentActivity_throwsExceptionForUnknownActivity() {
+        // Tries to remove the Activity without registering it.
+        assertThrows(ActivityNotFoundException.class,
+                () -> mCarActivityManager.setPersistentActivity(
+                        mTestActivity, Display.DEFAULT_DISPLAY, FEATURE_UNDEFINED));
+    }
+}
diff --git a/tests/carservice_test/src/com/android/car/watchdog/CarWatchdogServiceTest.java b/tests/carservice_test/src/com/android/car/watchdog/CarWatchdogServiceTest.java
index ba01d41..9a24a7e 100644
--- a/tests/carservice_test/src/com/android/car/watchdog/CarWatchdogServiceTest.java
+++ b/tests/carservice_test/src/com/android/car/watchdog/CarWatchdogServiceTest.java
@@ -77,8 +77,8 @@
     private final Executor mExecutor =
             InstrumentationRegistry.getInstrumentation().getTargetContext().getMainExecutor();
     private final UserInfo[] mUserInfos = new UserInfo[] {
-            new UserInfoBuilder(10).setName("user 1").build(),
-            new UserInfoBuilder(11).setName("user 2").build()
+            new UserInfoBuilder(100).setName("user 1").build(),
+            new UserInfoBuilder(101).setName("user 2").build()
     };
 
     @Mock private Context mMockContext;
@@ -101,8 +101,8 @@
         when(mServiceBinder.queryLocalInterface(anyString())).thenReturn(mCarWatchdogService);
         when(mMockContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
         mockUmGetAllUsers(mUserManager, mUserInfos);
-        mockUmIsUserRunning(mUserManager, 10, true);
-        mockUmIsUserRunning(mUserManager, 11, false);
+        mockUmIsUserRunning(mUserManager, 100, true);
+        mockUmIsUserRunning(mUserManager, 101, false);
 
         mCarWatchdogService.init();
         mWatchdogServiceForSystemImpl = registerCarWatchdogService();
@@ -224,11 +224,11 @@
     }
 
     private void expectRunningUser() {
-        doReturn(10).when(() -> UserHandle.getUserId(Binder.getCallingUid()));
+        doReturn(100).when(() -> UserHandle.getUserId(Binder.getCallingUid()));
     }
 
     private void expectStoppedUser() {
-        doReturn(11).when(() -> UserHandle.getUserId(Binder.getCallingUid()));
+        doReturn(101).when(() -> UserHandle.getUserId(Binder.getCallingUid()));
     }
 
     private final class TestClient {
diff --git a/tests/carservice_unit_test/src/com/android/car/AbstractICarServiceHelperStub.java b/tests/carservice_unit_test/src/com/android/car/AbstractICarServiceHelperStub.java
index 7304e23..5d387cc 100644
--- a/tests/carservice_unit_test/src/com/android/car/AbstractICarServiceHelperStub.java
+++ b/tests/carservice_unit_test/src/com/android/car/AbstractICarServiceHelperStub.java
@@ -16,6 +16,7 @@
 package com.android.car;
 
 import android.annotation.UserIdInt;
+import android.car.app.CarActivityManager;
 import android.content.ComponentName;
 import android.content.pm.UserInfo;
 import android.os.RemoteException;
@@ -73,4 +74,11 @@
                 + ", flags=" + flags + ")");
         return null;
     }
+
+    @Override
+    public int setPersistentActivity(ComponentName activity, int displayId, int featureId) {
+        Log.d(TAG, "setPersistentActivity(activity=" + activity.toShortString()
+                + ", displayId=" + displayId + ", featureId=" + featureId + ")");
+        return CarActivityManager.RESULT_SUCCESS;
+    }
 }
diff --git a/tests/carservice_unit_test/src/com/android/car/am/CarActivityServiceUnitTest.java b/tests/carservice_unit_test/src/com/android/car/am/CarActivityServiceUnitTest.java
new file mode 100644
index 0000000..4051686
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/am/CarActivityServiceUnitTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.am;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+
+import android.car.Car;
+import android.car.app.CarActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.os.UserHandle;
+
+import com.android.car.internal.ICarServiceHelper;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class CarActivityServiceUnitTest {
+
+    // Comes from android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER
+    private static final int FEATURE_DEFAULT_TASK_CONTAINER = 1;
+
+    private CarActivityService mCarActivityService;
+
+    private final ComponentName mTestActivity = new ComponentName("test.pkg", "test.activity");
+
+    @Rule
+    public TestName mTestName = new TestName();
+    @Mock
+    private Context mContext;
+    @Mock
+    private ICarServiceHelper mICarServiceHelper;
+
+    @Before
+    public void setUp() {
+        mCarActivityService = spy(new CarActivityService(mContext));
+
+        int nonCurrentUserId = 9999990;
+        boolean isNonCurrentUserTest = mTestName.getMethodName().contains("NonCurrentUser");
+        int callerId = isNonCurrentUserTest ? nonCurrentUserId : UserHandle.USER_SYSTEM;
+        when(mCarActivityService.getCaller()).thenReturn(callerId);
+    }
+
+    @Test
+    public void setPersistentActivityThrowsException_ifICarServiceHelperIsNotSet() {
+        assertThrows(IllegalStateException.class,
+                () -> mCarActivityService.setPersistentActivity(
+                        mTestActivity, DEFAULT_DISPLAY, FEATURE_DEFAULT_TASK_CONTAINER));
+    }
+
+    @Test
+    public void setPersistentActivityThrowsException_withoutPermission() {
+        mCarActivityService.setICarServiceHelper(mICarServiceHelper);
+        when(mContext.checkCallingOrSelfPermission(eq(Car.PERMISSION_CONTROL_CAR_APP_LAUNCH)))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+
+        assertThrows(SecurityException.class,
+                () -> mCarActivityService.setPersistentActivity(
+                        mTestActivity, DEFAULT_DISPLAY, FEATURE_DEFAULT_TASK_CONTAINER));
+    }
+
+    @Test
+    public void setPersistentActivityInvokesICarServiceHelper() throws RemoteException {
+        int displayId = 9;
+
+        mCarActivityService.setICarServiceHelper(mICarServiceHelper);
+
+        int ret = mCarActivityService.setPersistentActivity(
+                mTestActivity, displayId, FEATURE_DEFAULT_TASK_CONTAINER);
+        assertThat(ret).isEqualTo(CarActivityManager.RESULT_SUCCESS);
+
+        ArgumentCaptor<ComponentName> activityCaptor = ArgumentCaptor.forClass(ComponentName.class);
+        ArgumentCaptor<Integer> displayIdCaptor = ArgumentCaptor.forClass(Integer.class);
+        ArgumentCaptor<Integer> featureIdCaptor = ArgumentCaptor.forClass(Integer.class);
+        verify(mICarServiceHelper).setPersistentActivity(
+                activityCaptor.capture(), displayIdCaptor.capture(), featureIdCaptor.capture());
+
+        assertThat(activityCaptor.getValue()).isEqualTo(mTestActivity);
+        assertThat(displayIdCaptor.getValue()).isEqualTo(displayId);
+        assertThat(featureIdCaptor.getValue()).isEqualTo(FEATURE_DEFAULT_TASK_CONTAINER);
+    }
+
+    @Test
+    public void setPersistentActivityReturnsErrorForNonCurrentUser() throws RemoteException {
+        mCarActivityService.setICarServiceHelper(mICarServiceHelper);
+
+        int ret = mCarActivityService.setPersistentActivity(
+                mTestActivity, DEFAULT_DISPLAY, FEATURE_DEFAULT_TASK_CONTAINER);
+        assertThat(ret).isEqualTo(CarActivityManager.RESULT_INVALID_USER);
+    }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/hal/MockedPowerHalService.java b/tests/carservice_unit_test/src/com/android/car/hal/MockedPowerHalService.java
index 2ab14dc..fd15435 100644
--- a/tests/carservice_unit_test/src/com/android/car/hal/MockedPowerHalService.java
+++ b/tests/carservice_unit_test/src/com/android/car/hal/MockedPowerHalService.java
@@ -49,6 +49,7 @@
                 mock(UserHalService.class),
                 mock(DiagnosticHalService.class),
                 mock(ClusterHalService.class),
+                mock(TimeHalService.class),
                 mock(HalClient.class),
                 CarServiceUtils.getHandlerThread(VehicleHal.class.getSimpleName()));
         return mockedVehicleHal;
diff --git a/tests/carservice_unit_test/src/com/android/car/hal/TimeHalServiceTest.java b/tests/carservice_unit_test/src/com/android/car/hal/TimeHalServiceTest.java
new file mode 100644
index 0000000..adc5153
--- /dev/null
+++ b/tests/carservice_unit_test/src/com/android/car/hal/TimeHalServiceTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.hal;
+
+import static android.hardware.automotive.vehicle.V2_0.VehicleProperty.EPOCH_TIME;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.verify;
+
+import android.car.test.util.BroadcastingFakeContext;
+import android.car.test.util.VehicleHalTestingHelper;
+import android.content.Intent;
+import android.hardware.automotive.vehicle.V2_0.VehicleArea;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropConfig;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
+import android.hardware.automotive.vehicle.V2_0.VehiclePropertyStatus;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.Collections;
+
+@RunWith(MockitoJUnitRunner.class)
+public final class TimeHalServiceTest {
+
+    private static final VehiclePropConfig ANDROID_TIME_PROP =
+            VehicleHalTestingHelper.newConfig(EPOCH_TIME);
+
+    @Mock private VehicleHal mVehicleHal;
+    private BroadcastingFakeContext mFakeContext;
+
+    private TimeHalService mTimeHalService;
+
+    @Before
+    public void setUp() {
+        mFakeContext = new BroadcastingFakeContext();
+        mTimeHalService = new TimeHalService(mFakeContext, mVehicleHal);
+    }
+
+    @Test
+    public void testInitDoesNothing() {
+        mTimeHalService.takeProperties(Collections.emptyList());
+
+        mTimeHalService.init();
+
+        mFakeContext.verifyReceiverNotRegistered();
+    }
+
+    @Test
+    public void testInitRegistersBroadcastReceiver() {
+        mTimeHalService.takeProperties(Collections.singletonList(ANDROID_TIME_PROP));
+
+        mTimeHalService.init();
+
+        assertThat(mTimeHalService.isAndroidTimeSupported()).isTrue();
+        mFakeContext.verifyReceiverRegistered(Intent.ACTION_TIME_CHANGED);
+    }
+
+    @Test
+    public void testInitSendsAndroidTimeUpdate() {
+        mTimeHalService.takeProperties(Collections.singletonList(ANDROID_TIME_PROP));
+        long sysTimeMillis = System.currentTimeMillis();
+
+        mTimeHalService.init();
+
+        assertThat(mTimeHalService.isAndroidTimeSupported()).isTrue();
+        ArgumentCaptor<VehiclePropValue> captor = ArgumentCaptor.forClass(VehiclePropValue.class);
+        verify(mVehicleHal).set(captor.capture());
+        VehiclePropValue propValue = captor.getValue();
+        assertThat(propValue.prop).isEqualTo(EPOCH_TIME);
+        assertThat(propValue.areaId).isEqualTo(VehicleArea.GLOBAL);
+        assertThat(propValue.status).isEqualTo(VehiclePropertyStatus.AVAILABLE);
+        assertThat(propValue.timestamp).isAtLeast(sysTimeMillis);
+        assertThat(propValue.value.int64Values).hasSize(1);
+        assertThat(propValue.value.int64Values.get(0)).isAtLeast(sysTimeMillis);
+    }
+
+    @Test
+    public void testReleaseUnregistersBroadcastReceiver() {
+        mTimeHalService.takeProperties(Collections.singletonList(ANDROID_TIME_PROP));
+        mTimeHalService.init();
+        clearInvocations(mVehicleHal);
+
+        mTimeHalService.release();
+
+        mFakeContext.verifyReceiverNotRegistered();
+        assertThat(mTimeHalService.isAndroidTimeSupported()).isFalse();
+    }
+
+    @Test
+    public void testSendsAndroidTimeUpdateWhenBroadcast() {
+        mTimeHalService.takeProperties(Collections.singletonList(ANDROID_TIME_PROP));
+        mTimeHalService.init();
+        clearInvocations(mVehicleHal);
+        long sysTimeMillis = System.currentTimeMillis();
+
+        mFakeContext.sendBroadcast(new Intent(Intent.ACTION_TIME_CHANGED));
+
+        ArgumentCaptor<VehiclePropValue> captor = ArgumentCaptor.forClass(VehiclePropValue.class);
+        verify(mVehicleHal).set(captor.capture());
+        VehiclePropValue propValue = captor.getValue();
+        assertThat(propValue.prop).isEqualTo(EPOCH_TIME);
+        assertThat(propValue.areaId).isEqualTo(VehicleArea.GLOBAL);
+        assertThat(propValue.status).isEqualTo(VehiclePropertyStatus.AVAILABLE);
+        assertThat(propValue.timestamp).isAtLeast(sysTimeMillis);
+        assertThat(propValue.value.int64Values).hasSize(1);
+        assertThat(propValue.value.int64Values.get(0)).isAtLeast(sysTimeMillis);
+    }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/hal/VehicleHalTest.java b/tests/carservice_unit_test/src/com/android/car/hal/VehicleHalTest.java
index 4aa9a81..2a39046 100644
--- a/tests/carservice_unit_test/src/com/android/car/hal/VehicleHalTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/hal/VehicleHalTest.java
@@ -71,6 +71,7 @@
     @Mock private UserHalService mUserHalService;
     @Mock private DiagnosticHalService mDiagnosticHalService;
     @Mock private ClusterHalService mClusterHalService;
+    @Mock private TimeHalService mTimeHalService;
     @Mock private HalClient mHalClient;
 
     private final HandlerThread mHandlerThread = CarServiceUtils.getHandlerThread(
@@ -86,7 +87,8 @@
     public void setUp() throws Exception {
         mVehicleHal = new VehicleHal(mPowerHalService,
                 mPropertyHalService, mInputHalService, mVmsHalService, mUserHalService,
-                mDiagnosticHalService, mClusterHalService, mHalClient, mHandlerThread);
+                mDiagnosticHalService, mClusterHalService, mTimeHalService, mHalClient,
+                mHandlerThread);
 
         mConfigs.clear();
 
diff --git a/tests/carservice_unit_test/src/com/android/car/telemetry/publisher/CarTelemetrydPublisherTest.java b/tests/carservice_unit_test/src/com/android/car/telemetry/publisher/CarTelemetrydPublisherTest.java
index bc3cd47..36edb67 100644
--- a/tests/carservice_unit_test/src/com/android/car/telemetry/publisher/CarTelemetrydPublisherTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/telemetry/publisher/CarTelemetrydPublisherTest.java
@@ -48,6 +48,8 @@
 import org.mockito.Mockito;
 import org.mockito.junit.MockitoJUnitRunner;
 
+import java.util.List;
+
 @RunWith(MockitoJUnitRunner.class)
 public class CarTelemetrydPublisherTest extends AbstractExtendedMockitoTestCase {
     private static final String SERVICE_NAME = ICarTelemetryInternal.DESCRIPTOR + "/default";
@@ -66,7 +68,10 @@
 
     @Captor private ArgumentCaptor<IBinder.DeathRecipient> mLinkToDeathCallbackCaptor;
 
+    // These 2 variables are set in onPublisherFailure() callback.
     @Nullable private Throwable mPublisherFailure;
+    @Nullable private List<TelemetryProto.MetricsConfig> mFailedConfigs;
+
     private FakeCarTelemetryInternal mFakeCarTelemetryInternal;
     private CarTelemetrydPublisher mPublisher;
 
@@ -165,6 +170,7 @@
         assertThat(mFakeCarTelemetryInternal.mSetListenerCallCount).isEqualTo(1);
         assertThat(mPublisherFailure).hasMessageThat()
                 .contains("ICarTelemetryInternal binder died");
+        assertThat(mFailedConfigs).hasSize(1);  // got all the failed configs
     }
 
     @Test
@@ -175,10 +181,13 @@
 
         assertThat(mPublisherFailure).hasMessageThat()
                 .contains("Cannot set CarData listener");
+        assertThat(mFailedConfigs).hasSize(1);
     }
 
-    private void onPublisherFailure(AbstractPublisher publisher, Throwable error) {
+    private void onPublisherFailure(AbstractPublisher publisher,
+            List<TelemetryProto.MetricsConfig> affectedConfigs, Throwable error) {
         mPublisherFailure = error;
+        mFailedConfigs = affectedConfigs;
     }
 
     private static class FakeCarTelemetryInternal implements ICarTelemetryInternal {
diff --git a/tests/carservice_unit_test/src/com/android/car/telemetry/publisher/StatsPublisherTest.java b/tests/carservice_unit_test/src/com/android/car/telemetry/publisher/StatsPublisherTest.java
index 2ed12bd..2a3b88a 100644
--- a/tests/carservice_unit_test/src/com/android/car/telemetry/publisher/StatsPublisherTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/telemetry/publisher/StatsPublisherTest.java
@@ -31,6 +31,8 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -39,6 +41,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.PersistableBundle;
+import android.os.Process;
 import android.os.SystemClock;
 
 import com.android.car.telemetry.AtomsProto.AppStartMemoryStateCaptured;
@@ -72,6 +75,7 @@
 import java.io.FileInputStream;
 import java.nio.file.Files;
 import java.util.Arrays;
+import java.util.List;
 
 @RunWith(MockitoJUnitRunner.class)
 public class StatsPublisherTest {
@@ -103,9 +107,10 @@
                     .addSubscribers(SUBSCRIBER_2)
                     .build();
 
-    private static final long SUBSCRIBER_1_HASH = -8101507323446050791L;  // Used as ID.
-    private static final long SUBSCRIBER_2_HASH = 2778197004730583271L;  // Used as ID.
+    private static final long SUBSCRIBER_1_HASH = -8101507323446050791L;  // Used as configKey.
+    private static final long SUBSCRIBER_2_HASH = 2778197004730583271L;  // Used as configKey.
 
+    // This StatsdConfig is generated for SUBSCRIBER_1.
     private static final StatsdConfigProto.StatsdConfig STATSD_CONFIG_1 =
             StatsdConfigProto.StatsdConfig.newBuilder()
                     .setId(SUBSCRIBER_1_HASH)
@@ -121,6 +126,7 @@
                     .addAllowedLogSource("AID_SYSTEM")
                     .build();
 
+    // This StatsdConfig is generated for SUBSCRIBER_2.
     private static final StatsdConfigProto.StatsdConfig STATSD_CONFIG_2 =
             StatsdConfigProto.StatsdConfig.newBuilder()
                     .setId(SUBSCRIBER_2_HASH)
@@ -174,7 +180,7 @@
                             .setValueInt(234))
                     .build();
 
-    private static final StatsLogProto.ConfigMetricsReportList STATS_REPORT =
+    private static final StatsLogProto.ConfigMetricsReportList METRICS_REPORT =
             StatsLogProto.ConfigMetricsReportList.newBuilder()
                     .addReports(ConfigMetricsReport.newBuilder()
                             .addMetrics(StatsLogReport.newBuilder()
@@ -194,7 +200,22 @@
                                                             .setField(1))))))
                     .build();
 
-    private static final StatsLogProto.ConfigMetricsReportList EMPTY_STATS_REPORT =
+    // By default the test assumes all the StatsdConfigs are valid.
+    private static final StatsLogProto.StatsdStatsReport CONFIG_STATS_REPORT =
+            StatsLogProto.StatsdStatsReport.newBuilder()
+                    .addConfigStats(StatsLogProto.StatsdStatsReport.ConfigStats.newBuilder()
+                            // in unit tests UID of test and app are the same
+                            .setUid(Process.myUid())
+                            .setId(SUBSCRIBER_1_HASH)  // id is the same as configKey
+                            .setIsValid(true))
+                    .addConfigStats(StatsLogProto.StatsdStatsReport.ConfigStats.newBuilder()
+                            // in unit tests UID of test and app are the same
+                            .setUid(Process.myUid())
+                            .setId(SUBSCRIBER_2_HASH)  // id is the same as configKey
+                            .setIsValid(true))
+                    .build();
+
+    private static final StatsLogProto.ConfigMetricsReportList EMPTY_METRICS_REPORT =
             StatsLogProto.ConfigMetricsReportList.newBuilder().build();
 
     private static final DataSubscriber DATA_SUBSCRIBER_1 =
@@ -205,7 +226,10 @@
 
     private File mRootDirectory;
     private StatsPublisher mPublisher;  // subject
+
+    // These 2 variables are set in onPublisherFailure() callback. Defaults to null.
     private Throwable mPublisherFailure;
+    private List<TelemetryProto.MetricsConfig> mFailedConfigs;
 
     @Mock private StatsManagerProxy mStatsManager;
 
@@ -215,6 +239,7 @@
     public void setUp() throws Exception {
         mRootDirectory = Files.createTempDirectory("telemetry_test").toFile();
         mPublisher = createRestartedPublisher();
+        when(mStatsManager.getStatsMetadata()).thenReturn(CONFIG_STATS_REPORT.toByteArray());
     }
 
     /**
@@ -326,6 +351,7 @@
         mPublisher.addDataSubscriber(DATA_SUBSCRIBER_1);
 
         assertThat(mPublisherFailure).hasMessageThat().contains("Failed to add config");
+        assertThat(mFailedConfigs).hasSize(1);  // got all the failed configs
     }
 
     @Test
@@ -350,7 +376,7 @@
     public void testAfterDispatchItSchedulesANewPullReportTask() throws Exception {
         mPublisher.addDataSubscriber(DATA_SUBSCRIBER_1);
         Message firstMessage = mFakeHandlerWrapper.getQueuedMessages().get(0);
-        when(mStatsManager.getReports(anyLong())).thenReturn(EMPTY_STATS_REPORT.toByteArray());
+        when(mStatsManager.getReports(anyLong())).thenReturn(EMPTY_METRICS_REPORT.toByteArray());
 
         mFakeHandlerWrapper.dispatchQueuedMessages();
 
@@ -373,7 +399,7 @@
         when(subscriber2.getMetricsConfig()).thenReturn(METRICS_CONFIG);
         when(subscriber2.getPublisherParam()).thenReturn(SUBSCRIBER_2.getPublisher());
         mPublisher.addDataSubscriber(subscriber2);
-        when(mStatsManager.getReports(anyLong())).thenReturn(STATS_REPORT.toByteArray());
+        when(mStatsManager.getReports(anyLong())).thenReturn(METRICS_REPORT.toByteArray());
 
         mFakeHandlerWrapper.dispatchQueuedMessages();
 
@@ -393,15 +419,43 @@
             .asList().containsExactly(445678901L);
     }
 
+    @Test
+    public void testOnInvalidConfig_notifiesPublisherFailureListener() throws Exception {
+        DataSubscriber subscriber = spy(new DataSubscriber(null, METRICS_CONFIG, SUBSCRIBER_1));
+        mPublisher.addDataSubscriber(subscriber);
+        reset(mStatsManager);
+        when(mStatsManager.getStatsMetadata()).thenReturn(
+                StatsLogProto.StatsdStatsReport.newBuilder()
+                        .addConfigStats(StatsLogProto.StatsdStatsReport.ConfigStats.newBuilder()
+                                // in unit tests UID of test and app are the same
+                                .setUid(Process.myUid())
+                                .setId(SUBSCRIBER_1_HASH)  // id is the same as configKey
+                                .setIsValid(false))
+                .build().toByteArray());
+        when(mStatsManager.getReports(anyLong())).thenReturn(EMPTY_METRICS_REPORT.toByteArray());
+
+        mFakeHandlerWrapper.dispatchQueuedMessages();
+
+        // subscriber shouldn't get data, because of EMPTY_METRICS_REPORT.
+        verify(subscriber, times(0)).push(any());
+        assertThat(mFailedConfigs).containsExactly(METRICS_CONFIG);
+        assertThat(mPublisherFailure).hasMessageThat().contains("Found invalid configs");
+    }
+
     private PersistableBundle getSavedStatsConfigs() throws Exception {
         File savedConfigsFile = new File(mRootDirectory, StatsPublisher.SAVED_STATS_CONFIGS_FILE);
+        if (!savedConfigsFile.exists()) {
+            return new PersistableBundle();
+        }
         try (FileInputStream fileInputStream = new FileInputStream(savedConfigsFile)) {
             return PersistableBundle.readFromStream(fileInputStream);
         }
     }
 
-    private void onPublisherFailure(AbstractPublisher publisher, Throwable error) {
+    private void onPublisherFailure(AbstractPublisher publisher,
+                List<TelemetryProto.MetricsConfig> affectedConfigs, Throwable error) {
         mPublisherFailure = error;
+        mFailedConfigs = affectedConfigs;
     }
 
     private static void assertThatMessageIsScheduledWithGivenDelay(Message msg, long delayMillis) {
diff --git a/tests/carservice_unit_test/src/com/android/car/telemetry/publisher/VehiclePropertyPublisherTest.java b/tests/carservice_unit_test/src/com/android/car/telemetry/publisher/VehiclePropertyPublisherTest.java
index 508120f..39009bd 100644
--- a/tests/carservice_unit_test/src/com/android/car/telemetry/publisher/VehiclePropertyPublisherTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/telemetry/publisher/VehiclePropertyPublisherTest.java
@@ -204,5 +204,6 @@
         // PersistableBundle object.
     }
 
-    private void onPublisherFailure(AbstractPublisher publisher, Throwable error) { }
+    private void onPublisherFailure(AbstractPublisher publisher,
+                List<TelemetryProto.MetricsConfig> affectedConfigs, Throwable error) { }
 }
diff --git a/tests/carservice_unit_test/src/com/android/car/watchdog/CarWatchdogServiceUnitTest.java b/tests/carservice_unit_test/src/com/android/car/watchdog/CarWatchdogServiceUnitTest.java
index c1e3938..6734322 100644
--- a/tests/carservice_unit_test/src/com/android/car/watchdog/CarWatchdogServiceUnitTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/watchdog/CarWatchdogServiceUnitTest.java
@@ -18,8 +18,8 @@
 
 import static android.automotive.watchdog.internal.ResourceOveruseActionType.KILLED;
 import static android.automotive.watchdog.internal.ResourceOveruseActionType.NOT_KILLED;
-import static android.car.test.mocks.AndroidMockitoHelper.mockUmGetAliveUsers;
 import static android.car.test.mocks.AndroidMockitoHelper.mockUmGetAllUsers;
+import static android.car.test.mocks.AndroidMockitoHelper.mockUmGetUserHandles;
 import static android.car.watchdog.CarWatchdogManager.TIMEOUT_CRITICAL;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
@@ -283,6 +283,15 @@
     }
 
     @Test
+    public void testUserRemovedBroadcast() throws Exception {
+        mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 101, 102);
+        mBroadcastReceiver.onReceive(mMockContext,
+                new Intent().setAction(Intent.ACTION_USER_REMOVED)
+                        .putExtra(Intent.EXTRA_USER, UserHandle.of(100)));
+        verify(mMockWatchdogStorage).syncUsers(new int[] {101, 102});
+    }
+
+    @Test
     public void testGetResourceOveruseStats() throws Exception {
         int uid = Binder.getCallingUid();
         injectPackageInfos(Collections.singletonList(
@@ -1001,7 +1010,7 @@
 
     @Test
     public void testSetKillablePackageAsUser() throws Exception {
-        mockUmGetAliveUsers(mMockUserManager, 11, 12);
+        mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11, 12);
         injectPackageInfos(Arrays.asList(
                 constructPackageManagerPackageInfo("third_party_package", 1103456, null),
                 constructPackageManagerPackageInfo("vendor_package.critical", 1101278, null),
@@ -1029,7 +1038,7 @@
                 () -> mCarWatchdogService.setKillablePackageAsUser("vendor_package.critical",
                         userHandle, /* isKillable= */ true));
 
-        mockUmGetAliveUsers(mMockUserManager, 11, 12, 13);
+        mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11, 12, 13);
         injectPackageInfos(Collections.singletonList(
                 constructPackageManagerPackageInfo("third_party_package", 1303456, null)));
 
@@ -1049,7 +1058,7 @@
 
     @Test
     public void testSetKillablePackageAsUserWithSharedUids() throws Exception {
-        mockUmGetAliveUsers(mMockUserManager, 11, 12);
+        mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11, 12);
         injectPackageInfos(Arrays.asList(
                 constructPackageManagerPackageInfo(
                         "third_party_package.A", 1103456, "third_party_shared_package.A"),
@@ -1094,7 +1103,7 @@
 
     @Test
     public void testSetKillablePackageAsUserForAllUsers() throws Exception {
-        mockUmGetAliveUsers(mMockUserManager, 11, 12);
+        mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11, 12);
         injectPackageInfos(Arrays.asList(
                 constructPackageManagerPackageInfo("third_party_package", 1103456, null),
                 constructPackageManagerPackageInfo("vendor_package.critical", 1101278, null),
@@ -1121,7 +1130,7 @@
                 () -> mCarWatchdogService.setKillablePackageAsUser("vendor_package.critical",
                         UserHandle.ALL, /* isKillable= */ true));
 
-        mockUmGetAliveUsers(mMockUserManager, 11, 12, 13);
+        mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11, 12, 13);
         injectPackageInfos(Collections.singletonList(
                 constructPackageManagerPackageInfo("third_party_package", 1303456, null)));
 
@@ -1141,7 +1150,7 @@
 
     @Test
     public void testSetKillablePackageAsUsersForAllUsersWithSharedUids() throws Exception {
-        mockUmGetAliveUsers(mMockUserManager, 11, 12);
+        mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11, 12);
         injectPackageInfos(Arrays.asList(
                 constructPackageManagerPackageInfo(
                         "third_party_package.A", 1103456, "third_party_shared_package.A"),
@@ -1174,7 +1183,7 @@
                 new PackageKillableState("third_party_package.B", 12,
                         PackageKillableState.KILLABLE_STATE_NO));
 
-        mockUmGetAliveUsers(mMockUserManager, 11, 12, 13);
+        mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11, 12, 13);
         injectPackageInfos(Arrays.asList(
                 constructPackageManagerPackageInfo(
                         "third_party_package.A", 1303456, "third_party_shared_package.A"),
@@ -1192,7 +1201,7 @@
 
     @Test
     public void testGetPackageKillableStatesAsUser() throws Exception {
-        mockUmGetAliveUsers(mMockUserManager, 11, 12);
+        mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11, 12);
         injectPackageInfos(Arrays.asList(
                 constructPackageManagerPackageInfo("third_party_package", 1103456, null),
                 constructPackageManagerPackageInfo("vendor_package.critical", 1101278, null),
@@ -1210,7 +1219,7 @@
 
     @Test
     public void testGetPackageKillableStatesAsUserWithSafeToKillPackages() throws Exception {
-        mockUmGetAliveUsers(mMockUserManager, 11, 12);
+        mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11, 12);
         injectPackageInfos(Arrays.asList(
                 constructPackageManagerPackageInfo("system_package.non_critical.A", 1102459, null),
                 constructPackageManagerPackageInfo("third_party_package", 1103456, null),
@@ -1242,7 +1251,7 @@
 
     @Test
     public void testGetPackageKillableStatesAsUserWithVendorPackagePrefixes() throws Exception {
-        mockUmGetAliveUsers(mMockUserManager, 11);
+        mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11);
         /* Package names which start with "system" are constructed as system packages. */
         injectPackageInfos(Arrays.asList(
                 constructPackageManagerPackageInfo("system_package_as_vendor", 1102459, null)));
@@ -1273,7 +1282,7 @@
 
     @Test
     public void testGetPackageKillableStatesAsUserWithSharedUids() throws Exception {
-        mockUmGetAliveUsers(mMockUserManager, 11, 12);
+        mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11, 12);
         injectPackageInfos(Arrays.asList(
                 constructPackageManagerPackageInfo(
                         "system_package.A", 1103456, "vendor_shared_package.A"),
@@ -1304,7 +1313,7 @@
     @Test
     public void testGetPackageKillableStatesAsUserWithSharedUidsAndSafeToKillPackages()
             throws Exception {
-        mockUmGetAliveUsers(mMockUserManager, 11);
+        mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11);
         injectPackageInfos(Arrays.asList(
                 constructPackageManagerPackageInfo(
                         "vendor_package.non_critical.A", 1103456, "vendor_shared_package.A"),
@@ -1343,7 +1352,7 @@
     @Test
     public void testGetPackageKillableStatesAsUserWithSharedUidsAndSafeToKillSharedPackage()
             throws Exception {
-        mockUmGetAliveUsers(mMockUserManager, 11);
+        mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11);
         injectPackageInfos(Arrays.asList(
                 constructPackageManagerPackageInfo(
                         "vendor_package.non_critical.A", 1103456, "vendor_shared_package.B"),
@@ -1374,7 +1383,7 @@
 
     @Test
     public void testGetPackageKillableStatesAsUserForAllUsers() throws Exception {
-        mockUmGetAliveUsers(mMockUserManager, 11, 12);
+        mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11, 12);
         injectPackageInfos(Arrays.asList(
                 constructPackageManagerPackageInfo("third_party_package", 1103456, null),
                 constructPackageManagerPackageInfo("vendor_package.critical", 1101278, null),
@@ -1395,7 +1404,7 @@
 
     @Test
     public void testGetPackageKillableStatesAsUserForAllUsersWithSharedUids() throws Exception {
-        mockUmGetAliveUsers(mMockUserManager, 11, 12);
+        mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 11, 12);
         injectPackageInfos(Arrays.asList(
                 constructPackageManagerPackageInfo(
                         "system_package.A", 1103456, "vendor_shared_package.A"),
@@ -1897,7 +1906,7 @@
 
     @Test
     public void testPersistStatsOnShutdownEnter() throws Exception {
-        mockUmGetAliveUsers(mMockUserManager, 10, 11, 12);
+        mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 10, 11, 12);
         injectPackageInfos(Arrays.asList(
                 constructPackageManagerPackageInfo(
                         "third_party_package", 1103456, "vendor_shared_package.critical"),
@@ -1961,7 +1970,7 @@
 
     @Test
     public void testPersistIoOveruseStatsOnDateChange() throws Exception {
-        mockUmGetAliveUsers(mMockUserManager, 10);
+        mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 10);
         injectPackageInfos(Arrays.asList(
                 constructPackageManagerPackageInfo("system_package", 1011200, null),
                 constructPackageManagerPackageInfo("third_party_package", 1001100, null)));
@@ -2053,7 +2062,7 @@
 
     @Test
     public void testResetResourceOveruseStatsResetsUserPackageSettings() throws Exception {
-        mockUmGetAliveUsers(mMockUserManager, 100, 101);
+        mockUmGetUserHandles(mMockUserManager, /* excludeDying= */ true, 100, 101);
         injectPackageInfos(Arrays.asList(
                 constructPackageManagerPackageInfo("third_party_package.A", 10001278, null),
                 constructPackageManagerPackageInfo("third_party_package.A", 10101278, null),
@@ -2388,6 +2397,7 @@
          */
         CarServiceUtils.getHandlerThread(CarWatchdogService.class.getSimpleName())
                 .getThreadHandler().post(() -> {});
+        verify(mMockWatchdogStorage, times(wantedInvocations)).syncUsers(any());
         verify(mMockWatchdogStorage, times(wantedInvocations)).getUserPackageSettings();
         verify(mMockWatchdogStorage, times(wantedInvocations)).getTodayIoUsageStats();
     }
diff --git a/tests/carservice_unit_test/src/com/android/car/watchdog/WatchdogStorageUnitTest.java b/tests/carservice_unit_test/src/com/android/car/watchdog/WatchdogStorageUnitTest.java
index 3f16750..e2c12bb 100644
--- a/tests/carservice_unit_test/src/com/android/car/watchdog/WatchdogStorageUnitTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/watchdog/WatchdogStorageUnitTest.java
@@ -346,6 +346,68 @@
     }
 
     @Test
+    public void testSyncUsers() throws Exception {
+        List<WatchdogStorage.UserPackageSettingsEntry> settingsEntries = sampleSettings();
+        List<WatchdogStorage.IoUsageStatsEntry> ioUsageStatsEntries = sampleStatsForToday();
+
+        assertThat(mService.saveUserPackageSettings(settingsEntries)).isTrue();
+        assertThat(mService.saveIoUsageStats(ioUsageStatsEntries)).isTrue();
+
+        mService.syncUsers(/* aliveUserIds= */ new int[] {101});
+
+        settingsEntries.removeIf((s) -> s.userId == 100);
+        ioUsageStatsEntries.removeIf((e) -> e.userId == 100);
+
+        UserPackageSettingsEntrySubject.assertThat(mService.getUserPackageSettings())
+                .containsExactlyElementsIn(settingsEntries);
+
+        IoUsageStatsEntrySubject.assertThat(mService.getTodayIoUsageStats())
+                .containsExactlyElementsIn(ioUsageStatsEntries);
+    }
+
+    @Test
+    public void testSyncUsersWithHistoricalIoOveruseStats() throws Exception {
+        List<WatchdogStorage.UserPackageSettingsEntry> settingsEntries = sampleSettings();
+
+        assertThat(mService.saveUserPackageSettings(settingsEntries)).isTrue();
+        assertThat(mService.saveIoUsageStats(sampleStatsBetweenDates(
+                /* includingStartDaysAgo= */ 1, /* excludingEndDaysAgo= */ 6))).isTrue();
+
+        mService.syncUsers(/* aliveUserIds= */ new int[] {101});
+
+        settingsEntries.removeIf((s) -> s.userId == 100);
+
+        UserPackageSettingsEntrySubject.assertThat(mService.getUserPackageSettings())
+                .containsExactlyElementsIn(settingsEntries);
+
+        IoOveruseStats actualSystemPackage = mService.getHistoricalIoOveruseStats(
+                /* userId= */ 100, "system_package.non_critical.A", /* numDaysAgo= */ 7);
+        IoOveruseStats actualVendorPackage = mService.getHistoricalIoOveruseStats(
+                /* userId= */ 100, "vendor_package.critical.C", /* numDaysAgo= */ 7);
+
+        assertWithMessage("System I/O overuse stats for deleted user")
+                .that(actualSystemPackage).isNull();
+        assertWithMessage("Vendor I/O overuse stats for deleted user")
+                .that(actualVendorPackage).isNull();
+    }
+
+    @Test
+    public void testSyncUsersWithNoDataForDeletedUser() throws Exception {
+        List<WatchdogStorage.UserPackageSettingsEntry> settingsEntries = sampleSettings();
+        List<WatchdogStorage.IoUsageStatsEntry> ioUsageStatsEntries = sampleStatsForToday();
+
+        assertThat(mService.saveUserPackageSettings(settingsEntries)).isTrue();
+        assertThat(mService.saveIoUsageStats(ioUsageStatsEntries)).isTrue();
+
+        mService.syncUsers(/* aliveUserIds= */ new int[] {100, 101});
+
+        UserPackageSettingsEntrySubject.assertThat(mService.getUserPackageSettings())
+                .containsExactlyElementsIn(settingsEntries);
+        IoUsageStatsEntrySubject.assertThat(mService.getTodayIoUsageStats())
+                .containsExactlyElementsIn(ioUsageStatsEntries);
+    }
+
+    @Test
     public void testTruncateStatsOutsideRetentionPeriodOnDateChange() throws Exception {
         injectSampleUserPackageSettings();
         setDate(/* numDaysAgo= */ 1);
@@ -454,7 +516,7 @@
     private static ArrayList<WatchdogStorage.IoUsageStatsEntry> sampleStatsForDate(
             long statsDateEpoch, long duration) {
         ArrayList<WatchdogStorage.IoUsageStatsEntry> entries = new ArrayList<>();
-        for (int i = 100; i < 101; ++i) {
+        for (int i = 100; i <= 101; ++i) {
             entries.add(constructIoUsageStatsEntry(
                     /* userId= */ i, "system_package.non_critical.A", statsDateEpoch, duration,
                     /* remainingWriteBytes= */