Merge "CarService should fetch vendor package prefixes from watchdog daemon" into sc-v2-dev
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 1b105cc..926a882 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -12,6 +12,9 @@
           "include-filter": "android.car.apitest.PreInstalledPackagesTest"
         }
       ]
+    },
+    {
+      "name": "SampleCustomInputServiceTest"
     }
   ]
 }
\ No newline at end of file
diff --git a/car-lib/src/android/car/telemetry/CarTelemetryManager.java b/car-lib/src/android/car/telemetry/CarTelemetryManager.java
index 3d3cf50..b0067b1 100644
--- a/car-lib/src/android/car/telemetry/CarTelemetryManager.java
+++ b/car-lib/src/android/car/telemetry/CarTelemetryManager.java
@@ -45,7 +45,7 @@
 
     private static final boolean DEBUG = false;
     private static final String TAG = CarTelemetryManager.class.getSimpleName();
-    private static final int MANIFEST_MAX_SIZE_BYTES = 10 * 1024; // 10 kb
+    private static final int METRICS_CONFIG_MAX_SIZE_BYTES = 10 * 1024; // 10 kb
 
     private final CarTelemetryServiceListener mCarTelemetryServiceListener =
             new CarTelemetryServiceListener(this);
@@ -58,49 +58,51 @@
     private Executor mExecutor;
 
     /**
-     * Status to indicate that manifest was added successfully.
+     * Status to indicate that MetricsConfig was added successfully.
      */
-    public static final int ERROR_NONE = 0;
+    public static final int ERROR_METRICS_CONFIG_NONE = 0;
 
     /**
-     * Status to indicate that add manifest failed because the same manifest based on the
+     * Status to indicate that add MetricsConfig failed because the same MetricsConfig based on the
      * ManifestKey already exists.
      */
-    public static final int ERROR_SAME_MANIFEST_EXISTS = 1;
+    public static final int ERROR_METRICS_CONFIG_ALREADY_EXISTS = 1;
 
     /**
-     * Status to indicate that add manifest failed because a newer version of the manifest exists.
+     * Status to indicate that add MetricsConfig failed because a newer version of the MetricsConfig
+     * exists.
      */
-    public static final int ERROR_NEWER_MANIFEST_EXISTS = 2;
+    public static final int ERROR_METRICS_CONFIG_VERSION_TOO_OLD = 2;
 
     /**
-     * Status to indicate that add manifest failed because CarTelemetryService is unable to parse
-     * the given byte array into a Manifest.
+     * Status to indicate that add MetricsConfig failed because CarTelemetryService is unable to
+     * parse the given byte array into a MetricsConfig.
      */
-    public static final int ERROR_PARSE_MANIFEST_FAILED = 3;
+    public static final int ERROR_METRICS_CONFIG_PARSE_FAILED = 3;
 
     /**
-     * Status to indicate that add manifest failed because of failure to verify the signature of
-     * the manifest.
+     * Status to indicate that add MetricsConfig failed because of failure to verify the signature
+     * of the MetricsConfig.
      */
-    public static final int ERROR_SIGNATURE_VERIFICATION_FAILED = 4;
+    public static final int ERROR_METRICS_CONFIG_SIGNATURE_VERIFICATION_FAILED = 4;
 
     /**
-     * Status to indicate that add manifest failed because of a general error in cars.
+     * Status to indicate that add MetricsConfig failed because of a general error in cars.
      */
-    public static final int ERROR_UNKNOWN = 5;
+    public static final int ERROR_METRICS_CONFIG_UNKNOWN = 5;
 
     /** @hide */
-    @IntDef(prefix = {"ERROR_"}, value = {
-            ERROR_NONE,
-            ERROR_SAME_MANIFEST_EXISTS,
-            ERROR_NEWER_MANIFEST_EXISTS,
-            ERROR_PARSE_MANIFEST_FAILED,
-            ERROR_SIGNATURE_VERIFICATION_FAILED,
-            ERROR_UNKNOWN
+    @IntDef(prefix = {"ERROR_METRICS_CONFIG_"}, value = {
+            ERROR_METRICS_CONFIG_NONE,
+            ERROR_METRICS_CONFIG_ALREADY_EXISTS,
+            ERROR_METRICS_CONFIG_VERSION_TOO_OLD,
+            ERROR_METRICS_CONFIG_PARSE_FAILED,
+            ERROR_METRICS_CONFIG_SIGNATURE_VERIFICATION_FAILED,
+            ERROR_METRICS_CONFIG_UNKNOWN
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface AddManifestError {}
+    public @interface MetricsConfigError {
+    }
 
     /**
      * Application registers {@link CarTelemetryResultsListener} object to receive data from
@@ -110,22 +112,39 @@
      */
     public interface CarTelemetryResultsListener {
         /**
-         * Called by {@link com.android.car.telemetry.CarTelemetryService} to send script result to
-         * the client.
+         * Sends script results to the client. Called by {@link CarTelemetryServiceListener}.
+         *
          * TODO(b/184964661): Publish the documentation for the format of the results.
          *
-         * @param key the {@link ManifestKey} that the result is associated with.
-         * @param result the serialized car telemetry result.
+         * @param key    the {@link MetricsConfigKey} that the result is associated with.
+         * @param result the car telemetry result as serialized bytes.
          */
-        void onResult(@NonNull ManifestKey key, @NonNull byte[] result);
+        void onResult(@NonNull MetricsConfigKey key, @NonNull byte[] result);
 
         /**
-         * Called by {@link com.android.car.telemetry.CarTelemetryService} to send error message to
-         * the client.
+         * Sends script execution errors to the client.
          *
+         * @param key   the {@link MetricsConfigKey} that the error is associated with
          * @param error the serialized car telemetry error.
          */
-        void onError(@NonNull byte[] error);
+        void onError(@NonNull MetricsConfigKey key, @NonNull byte[] error);
+
+        /**
+         * Sends the {@link #addMetricsConfig(MetricsConfigKey, byte[])} status to the client.
+         *
+         * @param key        the {@link MetricsConfigKey} that the status is associated with
+         * @param statusCode See {@link MetricsConfigError}.
+         */
+        void onAddMetricsConfigStatus(@NonNull MetricsConfigKey key,
+                @MetricsConfigError int statusCode);
+
+        /**
+         * Sends the {@link #removeMetricsConfig(MetricsConfigKey)} status to the client.
+         *
+         * @param key     the {@link MetricsConfigKey} that the status is associated with
+         * @param success true for successful removal, false otherwise.
+         */
+        void onRemoveMetricsConfigStatus(@NonNull MetricsConfigKey key, boolean success);
     }
 
     /**
@@ -141,7 +160,7 @@
         }
 
         @Override
-        public void onResult(@NonNull ManifestKey key, @NonNull byte[] result) {
+        public void onResult(@NonNull MetricsConfigKey key, @NonNull byte[] result) {
             CarTelemetryManager manager = mManager.get();
             if (manager == null) {
                 return;
@@ -150,27 +169,66 @@
         }
 
         @Override
-        public void onError(@NonNull byte[] error) {
+        public void onError(@NonNull MetricsConfigKey key, @NonNull byte[] error) {
             CarTelemetryManager manager = mManager.get();
             if (manager == null) {
                 return;
             }
-            manager.onError(error);
+            manager.onError(key, error);
+        }
+
+        @Override
+        public void onAddMetricsConfigStatus(@NonNull MetricsConfigKey key,
+                @MetricsConfigError int statusCode) {
+            CarTelemetryManager manager = mManager.get();
+            if (manager == null) {
+                return;
+            }
+            manager.onAddMetricsConfigStatus(key, statusCode);
+        }
+
+        @Override
+        public void onRemoveMetricsConfigStatus(@NonNull MetricsConfigKey key, boolean success) {
+            CarTelemetryManager manager = mManager.get();
+            if (manager == null) {
+                return;
+            }
+            manager.onRemoveMetricsConfigStatus(key, success);
         }
     }
 
-    private void onResult(ManifestKey key, byte[] result) {
+    private void onResult(MetricsConfigKey key, byte[] result) {
         long token = Binder.clearCallingIdentity();
         synchronized (mLock) {
+            // TODO(b/198824696): listener should be nonnull
             mExecutor.execute(() -> mResultsListener.onResult(key, result));
         }
         Binder.restoreCallingIdentity(token);
     }
 
-    private void onError(byte[] error) {
+    private void onError(MetricsConfigKey key, byte[] error) {
         long token = Binder.clearCallingIdentity();
         synchronized (mLock) {
-            mExecutor.execute(() -> mResultsListener.onError(error));
+            // TODO(b/198824696): listener should be nonnull
+            mExecutor.execute(() -> mResultsListener.onError(key, error));
+        }
+        Binder.restoreCallingIdentity(token);
+    }
+
+    private void onAddMetricsConfigStatus(MetricsConfigKey key, int statusCode) {
+        long token = Binder.clearCallingIdentity();
+        synchronized (mLock) {
+            // TODO(b/198824696): listener should be nonnull
+            mExecutor.execute(() -> mResultsListener.onAddMetricsConfigStatus(key, statusCode));
+        }
+        Binder.restoreCallingIdentity(token);
+    }
+
+    private void onRemoveMetricsConfigStatus(MetricsConfigKey key, boolean success) {
+        long token = Binder.clearCallingIdentity();
+        synchronized (mLock) {
+            // TODO(b/198824696): listener should be nonnull
+            mExecutor.execute(() -> mResultsListener.onRemoveMetricsConfigStatus(key, success));
         }
         Binder.restoreCallingIdentity(token);
     }
@@ -209,7 +267,6 @@
      *
      * @param listener to received data from {@link com.android.car.telemetry.CarTelemetryService}.
      * @throws IllegalStateException if the listener is already set.
-     *
      * @hide
      */
     @RequiresPermission(Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE)
@@ -249,77 +306,77 @@
     }
 
     /**
-     * Called by client to send telemetry manifest. The size of the manifest cannot exceed a
-     * predefined size. Otherwise an exception is thrown.
-     * The {@link ManifestKey} is used to uniquely identify a manifest. If a manifest of the same
-     * name already exists in {@link com.android.car.telemetry.CarTelemetryService}, then the
-     * version will be compared. If the version is strictly higher, the existing manifest will be
-     * replaced by the new one. All cache and intermediate results will be cleared if replaced.
-     * TODO(b/185420981): Update javadoc after CarTelemetryService has concrete implementation.
+     * Sends a telemetry MetricsConfig to CarTelemetryService. The size of the MetricsConfig cannot
+     * exceed a predefined size, otherwise an exception is thrown.
+     * The {@link MetricsConfigKey} is used to uniquely identify a MetricsConfig. If a MetricsConfig
+     * of the same name already exists in {@link com.android.car.telemetry.CarTelemetryService},
+     * the config version will be compared. If the version is strictly higher, the existing
+     * MetricsConfig will be replaced by the new one. All cache and intermediate results will be
+     * cleared if replaced.
+     * The status of this API is sent back asynchronously via {@link CarTelemetryResultsListener}.
      *
-     * @param key      the unique key to identify the manifest.
-     * @param manifest the serialized bytes of a Manifest object.
-     * @return {@link #AddManifestError} to tell the result of the request.
-     * @throws IllegalArgumentException if the manifest size exceeds limit.
-     *
+     * @param key           the unique key to identify the MetricsConfig.
+     * @param metricsConfig the serialized bytes of a MetricsConfig object.
+     * @throws IllegalArgumentException if the MetricsConfig size exceeds limit.
      * @hide
      */
     @RequiresPermission(Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE)
-    public @AddManifestError int addManifest(@NonNull ManifestKey key, @NonNull byte[] manifest) {
-        if (manifest.length > MANIFEST_MAX_SIZE_BYTES) {
-            throw new IllegalArgumentException("Manifest size exceeds limit.");
+    public void addMetricsConfig(@NonNull MetricsConfigKey key, @NonNull byte[] metricsConfig) {
+        if (metricsConfig.length > METRICS_CONFIG_MAX_SIZE_BYTES) {
+            throw new IllegalArgumentException("MetricsConfig size exceeds limit.");
         }
         try {
-            return mService.addManifest(key, manifest);
+            mService.addMetricsConfig(key, metricsConfig);
         } catch (RemoteException e) {
             handleRemoteExceptionFromCarService(e);
         }
-        return ERROR_UNKNOWN;
     }
 
     /**
-     * Removes a manifest from {@link com.android.car.telemetry.CarTelemetryService}. If the
-     * manifest does not exist, nothing will be removed but the status will be indicated in the
-     * return value.
+     * Removes a MetricsConfig from {@link com.android.car.telemetry.CarTelemetryService}. This
+     * will also remove outputs produced by the MetricsConfig. If the MetricsConfig does not exist,
+     * nothing will be removed.
+     * The status of this API is sent back asynchronously via {@link CarTelemetryResultsListener}.
      *
-     * @param key the unique key to identify the manifest. Name and version must be exact.
+     * @param key the unique key to identify the MetricsConfig. Name and version must be exact.
      * @return true for success, false otherwise.
      * @hide
      */
     @RequiresPermission(Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE)
-    public boolean removeManifest(@NonNull ManifestKey key) {
+    public void removeMetricsConfig(@NonNull MetricsConfigKey key) {
         try {
-            return mService.removeManifest(key);
+            mService.removeMetricsConfig(key);
         } catch (RemoteException e) {
             handleRemoteExceptionFromCarService(e);
         }
-        return false;
     }
 
     /**
-     * Removes all manifests from {@link com.android.car.telemetry.CarTelemetryService}.
+     * Removes all MetricsConfigs from {@link com.android.car.telemetry.CarTelemetryService}. This
+     * will also remove all MetricsConfig outputs.
      *
      * @hide
      */
     @RequiresPermission(Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE)
-    public void removeAllManifests() {
+    public void removeAllMetricsConfigs() {
         try {
-            mService.removeAllManifests();
+            mService.removeAllMetricsConfigs();
         } catch (RemoteException e) {
             handleRemoteExceptionFromCarService(e);
         }
     }
 
     /**
-     * An asynchronous API for the client to get script execution results of a specific manifest
-     * from the {@link com.android.car.telemetry.CarTelemetryService} through the listener.
+     * Gets script execution results of a MetricsConfig as from the
+     * {@link com.android.car.telemetry.CarTelemetryService}. This API is asynchronous and the
+     * result is sent back asynchronously via the {@link CarTelemetryResultsListener}.
      * This call is destructive. The returned results will be deleted from CarTelemetryService.
      *
-     * @param key the unique key to identify the manifest.
+     * @param key the unique key to identify the MetricsConfig.
      * @hide
      */
     @RequiresPermission(Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE)
-    public void sendFinishedReports(@NonNull ManifestKey key) {
+    public void sendFinishedReports(@NonNull MetricsConfigKey key) {
         try {
             mService.sendFinishedReports(key);
         } catch (RemoteException e) {
@@ -328,8 +385,8 @@
     }
 
     /**
-     * An asynchronous API for the client to get all script execution results
-     * from the {@link com.android.car.telemetry.CarTelemetryService} through the listener.
+     * Gets all script execution results from {@link com.android.car.telemetry.CarTelemetryService}
+     * asynchronously via the {@link CarTelemetryResultsListener}.
      * This call is destructive. The returned results will be deleted from CarTelemetryService.
      *
      * @hide
@@ -342,20 +399,4 @@
             handleRemoteExceptionFromCarService(e);
         }
     }
-
-    /**
-     * An asynchronous API for the client to get all script execution errors
-     * from the {@link com.android.car.telemetry.CarTelemetryService} through the listener.
-     * This call is destructive. The returned results will be deleted from CarTelemetryService.
-     *
-     * @hide
-     */
-    @RequiresPermission(Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE)
-    public void sendScriptExecutionErrors() {
-        try {
-            mService.sendScriptExecutionErrors();
-        } catch (RemoteException e) {
-            handleRemoteExceptionFromCarService(e);
-        }
-    }
 }
diff --git a/car-lib/src/android/car/telemetry/ICarTelemetryService.aidl b/car-lib/src/android/car/telemetry/ICarTelemetryService.aidl
index 09743d8..8343c34 100644
--- a/car-lib/src/android/car/telemetry/ICarTelemetryService.aidl
+++ b/car-lib/src/android/car/telemetry/ICarTelemetryService.aidl
@@ -1,7 +1,7 @@
 package android.car.telemetry;
 
 import android.car.telemetry.ICarTelemetryServiceListener;
-import android.car.telemetry.ManifestKey;
+import android.car.telemetry.MetricsConfigKey;
 
 /**
  * Internal binder interface for {@code CarTelemetryService}, used by {@code CarTelemetryManager}.
@@ -21,33 +21,29 @@
     void clearListener();
 
     /**
-     * Sends telemetry manifests to CarTelemetryService.
+     * Sends telemetry MetricsConfigs to CarTelemetryService.
      */
-    int addManifest(in ManifestKey key, in byte[] manifest);
+    void addMetricsConfig(in MetricsConfigKey key, in byte[] metricsConfig);
 
     /**
-     * Removes a manifest based on the key.
+     * Removes a MetricsConfig based on the key. This will also remove outputs produced by the
+     * MetricsConfig.
      */
-    boolean removeManifest(in ManifestKey key);
+    void removeMetricsConfig(in MetricsConfigKey key);
 
     /**
-     * Removes all manifests.
+     * Removes all MetricsConfigs. This will also remove all MetricsConfig outputs.
      */
-    void removeAllManifests();
+    void removeAllMetricsConfigs();
 
     /**
-     * Sends script results associated with the given key using the
+     * Sends script results or errors associated with the given key using the
      * {@code ICarTelemetryServiceListener}.
      */
-    void sendFinishedReports(in ManifestKey key);
+    void sendFinishedReports(in MetricsConfigKey key);
 
     /**
-     * Sends all script results associated using the {@code ICarTelemetryServiceListener}.
+     * Sends all script results or errors using the {@code ICarTelemetryServiceListener}.
      */
     void sendAllFinishedReports();
-
-    /**
-     * Sends all errors using the {@code ICarTelemetryServiceListener}.
-     */
-    void sendScriptExecutionErrors();
 }
\ No newline at end of file
diff --git a/car-lib/src/android/car/telemetry/ICarTelemetryServiceListener.aidl b/car-lib/src/android/car/telemetry/ICarTelemetryServiceListener.aidl
index ba4ca2d..4bd61fd 100644
--- a/car-lib/src/android/car/telemetry/ICarTelemetryServiceListener.aidl
+++ b/car-lib/src/android/car/telemetry/ICarTelemetryServiceListener.aidl
@@ -16,7 +16,7 @@
 
 package android.car.telemetry;
 
-import android.car.telemetry.ManifestKey;
+import android.car.telemetry.MetricsConfigKey;
 import java.util.List;
 
 /**
@@ -34,13 +34,29 @@
      * @param key the key that the result is associated with.
      * @param result the serialized bytes of the script execution result message.
      */
-    void onResult(in ManifestKey key, in byte[] result);
+    void onResult(in MetricsConfigKey key, in byte[] result);
 
     /**
-     * Called by {@code CarTelemetryService} to provide telemetry errors. This call is destrutive.
+     * Called by {@code CarTelemetryService} to provide telemetry errors. This call is destructive.
      * The parameter will no longer be stored in {@code CarTelemetryService}.
      *
      * @param error the serialized bytes of an error message.
      */
-    void onError(in byte[] error);
+    void onError(in MetricsConfigKey key, in byte[] error);
+
+    /**
+     * Sends the {@link #addMetricsConfig(MetricsConfigKey, byte[])} status to the client.
+     *
+     * @param key the {@link MetricsConfigKey} that the status is associated with.
+     * @param statusCode indicating add status.
+     */
+     void onAddMetricsConfigStatus(in MetricsConfigKey key, in int statusCode);
+
+    /**
+     * Sends the {@link #remove(MetricsConfigKey)} status to the client.
+     *
+     * @param key the {@link MetricsConfigKey} that the status is associated with.
+     * @param success true for successful removal, false otherwise.
+     */
+     void onRemoveMetricsConfigStatus(in MetricsConfigKey key, in boolean success);
 }
\ No newline at end of file
diff --git a/car-lib/src/android/car/telemetry/ManifestKey.aidl b/car-lib/src/android/car/telemetry/MetricsConfigKey.aidl
similarity index 95%
rename from car-lib/src/android/car/telemetry/ManifestKey.aidl
rename to car-lib/src/android/car/telemetry/MetricsConfigKey.aidl
index 25097df..2c00127 100644
--- a/car-lib/src/android/car/telemetry/ManifestKey.aidl
+++ b/car-lib/src/android/car/telemetry/MetricsConfigKey.aidl
@@ -19,4 +19,4 @@
 /**
  * @hide
  */
-parcelable ManifestKey;
\ No newline at end of file
+parcelable MetricsConfigKey;
\ No newline at end of file
diff --git a/car-lib/src/android/car/telemetry/ManifestKey.java b/car-lib/src/android/car/telemetry/MetricsConfigKey.java
similarity index 73%
rename from car-lib/src/android/car/telemetry/ManifestKey.java
rename to car-lib/src/android/car/telemetry/MetricsConfigKey.java
index b0a69c2..6e4614b 100644
--- a/car-lib/src/android/car/telemetry/ManifestKey.java
+++ b/car-lib/src/android/car/telemetry/MetricsConfigKey.java
@@ -25,7 +25,7 @@
  *
  * @hide
  */
-public final class ManifestKey implements Parcelable {
+public final class MetricsConfigKey implements Parcelable {
 
     @NonNull
     private String mName;
@@ -46,12 +46,12 @@
         out.writeInt(mVersion);
     }
 
-    private ManifestKey(Parcel in) {
+    private MetricsConfigKey(Parcel in) {
         mName = in.readString();
         mVersion = in.readInt();
     }
 
-    public ManifestKey(@NonNull String name, int version) {
+    public MetricsConfigKey(@NonNull String name, int version) {
         mName = name;
         mVersion = version;
     }
@@ -61,16 +61,16 @@
         return 0;
     }
 
-    public static final @NonNull Parcelable.Creator<ManifestKey> CREATOR =
-            new Parcelable.Creator<ManifestKey>() {
+    public static final @NonNull Parcelable.Creator<MetricsConfigKey> CREATOR =
+            new Parcelable.Creator<MetricsConfigKey>() {
                 @Override
-                public ManifestKey createFromParcel(Parcel in) {
-                    return new ManifestKey(in);
+                public MetricsConfigKey createFromParcel(Parcel in) {
+                    return new MetricsConfigKey(in);
                 }
 
                 @Override
-                public ManifestKey[] newArray(int size) {
-                    return new ManifestKey[size];
+                public MetricsConfigKey[] newArray(int size) {
+                    return new MetricsConfigKey[size];
                 }
             };
 }
diff --git a/car-test-lib/src/android/car/testapi/CarTelemetryController.java b/car-test-lib/src/android/car/testapi/CarTelemetryController.java
deleted file mode 100644
index e4df3f3..0000000
--- a/car-test-lib/src/android/car/testapi/CarTelemetryController.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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.testapi;
-
-import android.car.telemetry.ManifestKey;
-
-/**
- * Controller to manipulate and verify {@link android.car.telemetry.CarTelemetryManager} in
- * unit tests.
- */
-public interface CarTelemetryController {
-    /**
-     * Returns {@code true} if a {@link
-     * android.car.telemetry.CarTelemetryManager.CarTelemetryResultsListener} is
-     * registered with the manager, otherwise returns {@code false}.
-     */
-    boolean isListenerSet();
-
-    /**
-     * Returns the number of valid manifests registered with the manager.
-     */
-    int getValidManifestsCount();
-
-    /**
-     * Associate a blob of data with the given key, used for testing the flush reports APIs.
-     */
-    void addDataForKey(ManifestKey key, byte[] data);
-
-    /**
-     * Configure the blob of data to be flushed with the
-     * {@code FakeCarTelemetryService#flushScriptExecutionErrors()} API.
-     */
-    void setErrorData(byte[] error);
-}
diff --git a/car-test-lib/src/android/car/testapi/FakeCar.java b/car-test-lib/src/android/car/testapi/FakeCar.java
index 0508ed0..0356774 100644
--- a/car-test-lib/src/android/car/testapi/FakeCar.java
+++ b/car-test-lib/src/android/car/testapi/FakeCar.java
@@ -127,14 +127,6 @@
         return mService.mCarUxRestrictionService;
     }
 
-    /**
-     * Returns a test controller that can modify and query the underlying service for the {@link
-     * android.car.telemetry.CarTelemetryManager}.
-     */
-    public CarTelemetryController getCarTelemetryController() {
-        return mService.mCarTelemetry;
-    }
-
     private static class FakeCarService extends ICar.Stub {
         @Mock ICarPackageManager.Stub mCarPackageManager;
         @Mock ICarDiagnostic.Stub mCarDiagnostic;
@@ -150,7 +142,6 @@
         private final FakeCarProjectionService mCarProjection;
         private final FakeInstrumentClusterNavigation mInstrumentClusterNavigation;
         private final FakeCarUxRestrictionsService mCarUxRestrictionService;
-        private final FakeCarTelemetryService mCarTelemetry;
 
         FakeCarService(Context context) {
             MockitoAnnotations.initMocks(this);
@@ -160,7 +151,6 @@
             mCarProjection = new FakeCarProjectionService(context);
             mInstrumentClusterNavigation = new FakeInstrumentClusterNavigation();
             mCarUxRestrictionService = new FakeCarUxRestrictionsService();
-            mCarTelemetry = new FakeCarTelemetryService();
         }
 
         @Override
@@ -203,8 +193,6 @@
                     return mCarDrivingState;
                 case Car.CAR_UX_RESTRICTION_SERVICE:
                     return mCarUxRestrictionService;
-                case Car.CAR_TELEMETRY_SERVICE:
-                    return mCarTelemetry;
                 default:
                     Log.w(TAG, "getCarService for unknown service:" + serviceName);
                     return null;
diff --git a/car-test-lib/src/android/car/testapi/FakeCarTelemetryService.java b/car-test-lib/src/android/car/testapi/FakeCarTelemetryService.java
deleted file mode 100644
index 4c6c912..0000000
--- a/car-test-lib/src/android/car/testapi/FakeCarTelemetryService.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * 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.testapi;
-
-import static android.car.telemetry.CarTelemetryManager.ERROR_NEWER_MANIFEST_EXISTS;
-import static android.car.telemetry.CarTelemetryManager.ERROR_NONE;
-import static android.car.telemetry.CarTelemetryManager.ERROR_SAME_MANIFEST_EXISTS;
-
-import android.car.telemetry.CarTelemetryManager.AddManifestError;
-import android.car.telemetry.ICarTelemetryService;
-import android.car.telemetry.ICarTelemetryServiceListener;
-import android.car.telemetry.ManifestKey;
-import android.os.RemoteException;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * A fake implementation of {@link ICarTelemetryService.Stub} to facilitate the use of
- * {@link android.car.telemetry.CarTelemetryManager} in external unit tests.
- *
- * @hide
- */
-public class FakeCarTelemetryService extends ICarTelemetryService.Stub implements
-        CarTelemetryController {
-
-    private byte[] mErrorBytes;
-    private ICarTelemetryServiceListener mListener;
-
-    private final Map<String, Integer> mNameVersionMap = new HashMap<>();
-    private final Map<ManifestKey, byte[]> mManifestMap = new HashMap<>();
-    private final Map<ManifestKey, byte[]> mScriptResultMap = new HashMap<>();
-
-    @Override
-    public void setListener(ICarTelemetryServiceListener listener) {
-        mListener = listener;
-    }
-
-    @Override
-    public void clearListener() {
-        mListener = null;
-    }
-
-    @Override
-    public @AddManifestError int addManifest(ManifestKey key, byte[] manifest) {
-        if (mNameVersionMap.getOrDefault(key.getName(), 0) > key.getVersion()) {
-            return ERROR_NEWER_MANIFEST_EXISTS;
-        } else if (mNameVersionMap.getOrDefault(key.getName(), 0) == key.getVersion()) {
-            return ERROR_SAME_MANIFEST_EXISTS;
-        }
-        mNameVersionMap.put(key.getName(), key.getVersion());
-        mManifestMap.put(key, manifest);
-        return ERROR_NONE;
-    }
-
-    @Override
-    public boolean removeManifest(ManifestKey key) {
-        if (!mManifestMap.containsKey(key)) {
-            return false;
-        }
-        mNameVersionMap.remove(key.getName());
-        mManifestMap.remove(key);
-        return true;
-    }
-
-    @Override
-    public void removeAllManifests() {
-        mNameVersionMap.clear();
-        mManifestMap.clear();
-    }
-
-    @Override
-    public void sendFinishedReports(ManifestKey key) throws RemoteException {
-        if (!mScriptResultMap.containsKey(key)) {
-            return;
-        }
-        mListener.onResult(key, mScriptResultMap.get(key));
-        mScriptResultMap.remove(key);
-    }
-
-    @Override
-    public void sendAllFinishedReports() throws RemoteException {
-        for (Map.Entry<ManifestKey, byte[]> entry : mScriptResultMap.entrySet()) {
-            mListener.onResult(entry.getKey(), entry.getValue());
-        }
-        mScriptResultMap.clear();
-    }
-
-    @Override
-    public void sendScriptExecutionErrors() throws RemoteException {
-        mListener.onError(mErrorBytes);
-    }
-
-    /**************************** CarTelemetryController impl ********************************/
-    @Override
-    public boolean isListenerSet() {
-        return mListener != null;
-    }
-
-    @Override
-    public int getValidManifestsCount() {
-        return mManifestMap.size();
-    }
-
-    @Override
-    public void addDataForKey(ManifestKey key, byte[] data) {
-        mScriptResultMap.put(key, data);
-    }
-
-    @Override
-    public void setErrorData(byte[] error) {
-        mErrorBytes = error;
-    }
-}
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_minimize.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_minimize.xml
new file mode 100644
index 0000000..721cec4
--- /dev/null
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/ic_minimize.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+  ~ 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.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <vector
+            android:width="@dimen/system_bar_icon_drawing_size"
+            android:height="@dimen/system_bar_icon_drawing_size"
+            android:viewportWidth="24"
+            android:viewportHeight="24">
+            <path
+                android:pathData="M12,15.375l-6,-6 1.4,-1.4 4.6,4.6 4.6,-4.6 1.4,1.4z"
+                android:fillColor="@color/car_nav_minimize_icon_fill_color"/>
+        </vector>
+    </item>
+</selector>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/nav_bar_button_background.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/nav_bar_button_background.xml
index de76f55..7c8b669 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/nav_bar_button_background.xml
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/drawable/nav_bar_button_background.xml
@@ -15,19 +15,9 @@
   -->
 <selector xmlns:android="http://schemas.android.com/apk/res/android"
           xmlns:aapt="http://schemas.android.com/aapt">
-    <item android:state_activated="true">
+    <item android:state_selected="true">
         <layer-list>
             <item>
-                <shape android:shape="rectangle">
-                    <size android:width="@dimen/system_bar_button_size"
-                          android:height="@dimen/system_bar_button_size"/>
-                    <corners
-                        android:radius="@dimen/system_bar_button_corner_radius"/>
-                    <solid
-                        android:color="@color/car_nav_icon_fill_color"/>
-                </shape>
-            </item>
-            <item>
                 <ripple android:color="@color/car_ui_ripple_color">
                     <item>
                         <shape android:shape="rectangle">
@@ -36,26 +26,18 @@
                             <corners
                                 android:radius="@dimen/system_bar_button_corner_radius"/>
                             <solid
-                                android:color="@color/car_nav_icon_fill_color"/>
+                                android:color="?android:attr/colorAccent"/>
                         </shape>
                     </item>
                 </ripple>
             </item>
+            <item android:drawable="@drawable/ic_minimize"
+                  android:gravity="center"/>
         </layer-list>
     </item>
     <item>
         <layer-list>
             <item>
-                <shape android:shape="rectangle">
-                    <size android:width="@dimen/system_bar_button_size"
-                          android:height="@dimen/system_bar_button_size"/>
-                    <corners
-                        android:radius="@dimen/system_bar_button_corner_radius"/>
-                    <solid
-                        android:color="@color/car_nav_icon_background_color"/>
-                </shape>
-            </item>
-            <item>
                 <ripple android:color="@color/car_ui_ripple_color">
                     <item>
                         <shape android:shape="rectangle">
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/car_bottom_system_bar.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/car_bottom_system_bar.xml
index 557547a..69c3d30 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/car_bottom_system_bar.xml
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/layout/car_bottom_system_bar.xml
@@ -52,8 +52,9 @@
                 android:id="@+id/grid_nav"
                 style="@style/SystemBarButton"
                 systemui:componentNames="com.android.car.carlauncher/.AppGridActivity"
-                systemui:highlightWhenSelected="true"
                 systemui:icon="@drawable/car_ic_apps"
+                systemui:highlightWhenSelected="true"
+                systemui:toggleSelected="true"
                 systemui:intent="intent:#Intent;component=com.android.car.carlauncher/.AppGridActivity;launchFlags=0x24000000;end"
                 systemui:clearBackStack="true"/>
 
@@ -61,23 +62,25 @@
                 android:id="@+id/standalone_notifications"
                 style="@style/SystemBarButton"
                 systemui:componentNames="com.android.car.notification/.CarNotificationCenterActivity"
-                systemui:highlightWhenSelected="true"
+                systemui:packages="com.android.car.notification"
                 systemui:icon="@drawable/car_ic_notification"
+                systemui:highlightWhenSelected="true"
+                systemui:toggleSelected="true"
                 systemui:intent="intent:#Intent;component=com.android.car.notification/.CarNotificationCenterActivity;launchFlags=0x24000000;end"
                 systemui:longIntent="intent:#Intent;component=com.android.car.bugreport/.BugReportActivity;end"/>
 
             <com.android.systemui.car.systembar.CarSystemBarButton
                 android:id="@+id/hvac"
                 style="@style/SystemBarButton"
-                systemui:highlightWhenSelected="true"
                 systemui:icon="@drawable/car_ic_hvac"
+                systemui:highlightWhenSelected="true"
                 systemui:broadcast="true"/>
 
             <com.android.systemui.car.systembar.AssitantButton
                 android:id="@+id/assist"
                 style="@style/SystemBarButton"
-                systemui:highlightWhenSelected="true"
                 systemui:icon="@drawable/car_ic_mic"
+                systemui:highlightWhenSelected="true"
                 systemui:useDefaultAppIconForRole="true"/>
         </LinearLayout>
 
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/colors.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/colors.xml
index 8792680..3f62d35 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/colors.xml
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/colors.xml
@@ -20,6 +20,7 @@
 
     <color name="car_nav_icon_fill_color">#FFFFFF</color>
     <color name="car_nav_icon_background_color">#282A2D</color>
+    <color name="car_nav_minimize_icon_fill_color">@android:color/black</color>
 
     <drawable name="system_bar_background">#000000</drawable>
     <color name="system_bar_text_color">#E8EAED</color>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/dimens.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/dimens.xml
index 8ed622b..f900e57 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/dimens.xml
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/dimens.xml
@@ -42,6 +42,9 @@
     <dimen name="system_bar_user_icon_drawing_size">44dp</dimen>
     <dimen name="status_bar_system_icon_spacing">32dp</dimen>
 
+    <dimen name="system_bar_minimize_icon_height">17dp</dimen>
+    <dimen name="system_bar_minimize_icon_width">28dp</dimen>
+
     <dimen name="hvac_panel_handle_bar_container_height">64dp</dimen>
     <dimen name="hvac_panel_handle_bar_container_width">728dp</dimen>
     <dimen name="hvac_panel_handle_bar_height">6dp</dimen>
diff --git a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/styles.xml b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/styles.xml
index 6e400e5..b5f46f8 100644
--- a/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/styles.xml
+++ b/car_product/car_ui_portrait/apps/CarUiPortraitSystemUI/res/values/styles.xml
@@ -17,12 +17,19 @@
 <resources
     xmlns:android="http://schemas.android.com/apk/res/android">
 
+    <!--
+        Note on selected/unselected icons:
+        The icon is always tinted with @color/car_nav_icon_fill_color_selected in @layout/car_system_bar_button
+        Unselected: keep this behavior so all icons have consistent color (eg. tint a multi-colored default app icon)
+        Selected: set selected alpha 0, making icon transparent. Use state list nav_bar_button_background to show selected icon (in addition to background).
+    -->
     <style name="SystemBarButton">
         <item name="android:layout_width">@dimen/system_bar_button_size</item>
         <item name="android:layout_height">@dimen/system_bar_button_size</item>
         <item name="android:background">@drawable/nav_bar_button_background</item>
         <item name="android:gravity">center</item>
         <item name="unselectedAlpha">1.0</item>
+        <item name="selectedAlpha">0</item>
     </style>
 
     <style name="TextAppearance.SystemBar.Username"
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/Android.bp b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/Android.bp
index fecf167..92bd722 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/Android.bp
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/Android.bp
@@ -25,5 +25,7 @@
     static_libs: [
         "androidx-constraintlayout_constraintlayout",
         "androidx-constraintlayout_constraintlayout-solver",
+        "car-apps-common",
+        "car-ui-lib",
     ],
 }
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/dialer_ripple_background.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/dialer_ripple_background.xml
new file mode 100644
index 0000000..131afd5
--- /dev/null
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/dialer_ripple_background.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <shape
+            android:shape="oval">
+            <solid
+                android:color="@color/keypad_background_color" />
+            <size
+                android:width="@dimen/dialer_keypad_button_size"
+                android:height="@dimen/dialer_keypad_button_size"/>
+        </shape>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_arrow_right.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_arrow_right.xml
new file mode 100644
index 0000000..f1210dc
--- /dev/null
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_arrow_right.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:viewportWidth="48"
+        android:viewportHeight="48"
+        android:width="44dp"
+        android:height="44dp"
+        android:tint="?attr/colorControlNormal"
+        android:autoMirrored="true">
+
+    <path android:pathData="M0-.25h48v48H0z"/>
+    <path
+        android:fillColor="?android:attr/textColorPrimary"
+        android:pathData="M17.17 32.92l9.17-9.17-9.17-9.17L20 11.75l12 12-12 12z"/>
+</vector>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_backspace.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_backspace.xml
new file mode 100644
index 0000000..6b4f369
--- /dev/null
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_backspace.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ 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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="44dp"
+        android:height="32dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24"
+        android:tint="?attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M22,3L7,3c-0.69,0 -1.23,0.35 -1.59,0.88L0,12l5.41,8.11c0.36,0.53 0.9,0.89 1.59,0.89h15c1.1,0 2,-0.9 2,-2L24,5c0,-1.1 -0.9,-2 -2,-2zM22,19L7.07,19L2.4,12l4.66,-7L22,5v14zM10.41,17L14,13.41 17.59,17 19,15.59 15.41,12 19,8.41 17.59,7 14,10.59 10.41,7 9,8.41 12.59,12 9,15.59z"/>
+</vector>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_phone.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_phone.xml
index b659da1..3e2e824 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_phone.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/ic_phone.xml
@@ -22,7 +22,7 @@
     <path
         android:pathData="M0 0h24v24H0z" />
     <path
-        android:fillColor="#fffafafa"
+        android:fillColor="?android:attr/textColorPrimary"
         android:pathData="M6.62 10.79c1.44 2.83 3.76 5.14 6.59 6.59l2.2-2.2c.27-.27 .67 -.36 1.02-.24 1.12
 .37 2.33 .57 3.57 .57 .55 0 1 .45 1 1V20c0 .55-.45 1-1 1-9.39 0-17-7.61-17-17
 0-.55 .45 -1 1-1h3.5c.55 0 1 .45 1 1 0 1.25 .2 2.45 .57 3.57 .11 .35 .03 .74-.25
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/icon_call_button.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/icon_call_button.xml
index 748a1d0..c07c4b3 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/icon_call_button.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/icon_call_button.xml
@@ -22,8 +22,8 @@
                 android:color="#52CCB0" />
             <corners android:radius="100dp"/>
             <size
-                android:width="424dp"
-                android:height="120dp"/>
+                android:width="416dp"
+                android:height="@dimen/dialer_keypad_button_size"/>
         </shape>
     </item>
     <item android:drawable="@drawable/ic_phone" android:gravity="center"/>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/keypad_default_background.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/keypad_default_background.xml
index bf49159..131afd5 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/keypad_default_background.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/keypad_default_background.xml
@@ -19,10 +19,10 @@
         <shape
             android:shape="oval">
             <solid
-                android:color="#282A2D" />
+                android:color="@color/keypad_background_color" />
             <size
-                android:width="120dp"
-                android:height="120dp"/>
+                android:width="@dimen/dialer_keypad_button_size"
+                android:height="@dimen/dialer_keypad_button_size"/>
         </shape>
     </item>
 </layer-list>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/restricted_dialing_mode_label_background.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/restricted_dialing_mode_label_background.xml
new file mode 100644
index 0000000..ea5a715
--- /dev/null
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/drawable/restricted_dialing_mode_label_background.xml
@@ -0,0 +1,20 @@
+<!--
+  ~ 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.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+    <corners android:radius="4dp"/>
+    <solid android:color="@color/car_red_500a"/>
+</shape>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/dialpad_fragment_with_type_down.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/dialpad_fragment_with_type_down.xml
new file mode 100644
index 0000000..0185f23
--- /dev/null
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/dialpad_fragment_with_type_down.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_marginLeft="100dp">
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="45dp"
+        android:maxLines="1"
+        android:layout_marginBottom="48dp"
+        android:textAppearance="@style/TextAppearance.DialNumber"
+        android:autoSizeMinTextSize="24sp"
+        android:autoSizeMaxTextSize="28sp"
+        android:layout_marginLeft="150dp"
+        android:layout_alignParentTop="true"/>
+
+    <com.android.car.ui.recyclerview.CarUiRecyclerView
+        android:id="@+id/list_view"
+        android:layout_width="536dp"
+        android:layout_height="266dp"
+        android:layout_marginBottom="10dp"
+        android:layout_marginLeft="70dp"
+        android:layout_below="@id/title"/>
+    <fragment
+        android:id="@+id/dialpad_fragment"
+        android:name="com.android.car.dialer.ui.dialpad.KeypadFragment"
+        android:layout_height="456dp"
+        android:layout_width="416dp"
+        android:layout_marginLeft="120dp"
+        android:layout_below="@id/list_view"/>
+
+    <RelativeLayout
+        android:layout_height="@dimen/dialer_keypad_button_size"
+        android:layout_width="0dp"
+        android:layout_below="@id/dialpad_fragment"
+        android:layout_marginTop="38dp"
+        android:layout_alignLeft="@id/dialpad_fragment"
+        android:layout_alignRight="@id/dialpad_fragment">
+
+        <ImageView
+            android:id="@+id/call_button"
+            android:layout_height="match_parent"
+            android:layout_width="match_parent"
+            android:adjustViewBounds="true"
+            android:scaleType="fitXY"
+            android:src="@drawable/icon_call_button"
+            android:layout_toLeftOf="@id/delete_button"/>
+
+        <ImageButton
+            android:id="@+id/delete_button"
+            android:layout_width="@dimen/dialer_keypad_button_size"
+            android:layout_height="match_parent"
+            style="@style/DialpadSecondaryButton"
+            android:src="@drawable/ic_backspace"
+            android:layout_marginLeft="64dp"
+            android:visibility="gone"
+            android:layout_alignParentRight="true"/>
+    </RelativeLayout>
+
+    <include
+        layout="@layout/dialpad_user_profile"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_marginTop="10dp"
+        android:layout_below="@id/title"
+        android:layout_centerHorizontal="true"/>
+
+    <include
+        layout="@layout/restricted_dialing_mode_label"
+        android:id="@+id/restricted_dialing_mode_label"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerHorizontal="true"
+        android:layout_marginTop="8dp"
+        android:layout_below="@id/title"
+        android:visibility="invisible"/>
+</RelativeLayout>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/dialpad_user_profile.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/dialpad_user_profile.xml
new file mode 100644
index 0000000..bb4e11f
--- /dev/null
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/dialpad_user_profile.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <TextView
+        android:id="@+id/display_name"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="@style/TextAppearance.DialpadDisplayName"
+        android:singleLine="true"
+        android:layout_centerHorizontal="true"
+        android:layout_alignParentTop="true"/>
+
+    <TextView
+        android:id="@+id/label"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:singleLine="true"
+        android:layout_marginTop="12dp"
+        android:layout_below="@id/display_name"
+        android:layout_centerHorizontal="true"/>
+
+    <ImageView
+        android:id="@+id/dialpad_contact_avatar"
+        android:layout_height="@dimen/dialpad_contact_avatar_size"
+        android:layout_width="@dimen/dialpad_contact_avatar_size"
+        android:layout_below="@id/label"
+        android:layout_alignParentBottom="true"
+        android:layout_centerHorizontal="true"/>
+
+    <TextView
+        android:id="@+id/dialpad_contact_initials"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:layout_width="10dp"
+        android:layout_height="10dp"/>
+</RelativeLayout>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/restricted_dialing_mode_label.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/restricted_dialing_mode_label.xml
new file mode 100644
index 0000000..6a23fd5
--- /dev/null
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/restricted_dialing_mode_label.xml
@@ -0,0 +1,27 @@
+<!--
+  ~ 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.
+  -->
+
+<TextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/restricted_dialing_mode_label"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_marginTop="8dp"
+    android:padding="8dp"
+    android:textAppearance="@style/TextAppearance.Body2"
+    android:text="@string/restricted_dialing_mode_label"
+    android:alpha="0.8"
+    android:background="@drawable/restricted_dialing_mode_label_background"/>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/type_down_list_item.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/type_down_list_item.xml
new file mode 100644
index 0000000..68cf567
--- /dev/null
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/layout/type_down_list_item.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/contact_result"
+    android:foreground="?android:attr/selectableItemBackground"
+    android:layout_width="match_parent"
+    android:layout_height="112dp">
+
+    <ImageView
+        android:id="@+id/contact_picture"
+        android:layout_width="72dp"
+        android:layout_height="72dp"
+        android:scaleType="centerCrop"
+        android:layout_centerVertical="true"
+        android:layout_alignParentLeft="true"/>
+
+    <TextView
+        android:id="@+id/contact_name"
+        android:layout_marginStart="24dp"
+        android:layout_marginTop="14dp"
+        android:layout_marginBottom="8dp"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:singleLine="true"
+        android:textAppearance="@style/TextAppearance.ContactResultTitle"
+        android:duplicateParentState="true"
+        android:layout_alignParentTop="true"
+        android:layout_toRightOf="@id/contact_picture"/>
+
+    <TextView
+        android:id="@+id/phone_number"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="14dp"
+        android:theme="@style/Theme.CarUi.WithToolbar"
+        android:singleLine="true"
+        android:layout_alignParentBottom="true"
+        android:layout_alignLeft="@id/contact_name"/>
+</RelativeLayout>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values-night/colors.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values-night/colors.xml
new file mode 100644
index 0000000..929bfe2
--- /dev/null
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values-night/colors.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <color name="keypad_background_color">#282A2D</color>
+</resources>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values/colors.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values/colors.xml
new file mode 100644
index 0000000..5452e77
--- /dev/null
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values/colors.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <color name="keypad_background_color">#E8EAED</color>
+</resources>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values/dimens.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values/dimens.xml
new file mode 100644
index 0000000..108cfb1
--- /dev/null
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources>
+    <dimen name="dialer_keypad_button_size">96dp</dimen>
+    <dimen name="dialpad_contact_avatar_size">64dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values/strings.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values/strings.xml
new file mode 100644
index 0000000..88b64b6
--- /dev/null
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="restricted_dialing_mode_label">Dialpad usage is restricted while driving</string>
+</resources>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values/styles.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values/styles.xml
index 678cf0c..daeefb8 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values/styles.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/values/styles.xml
@@ -20,18 +20,54 @@
         <item name="android:clickable">true</item>
         <item name="android:layout_width">wrap_content</item>
         <item name="android:layout_height">wrap_content</item>
-        <item name="android:layout_marginTop">16dp</item>
-        <item name="android:layout_marginRight">16dp</item>
-        <item name="android:layout_marginBottom">16dp</item>
-        <item name="android:layout_marginLeft">16dp</item>
+        <item name="android:layout_marginTop">12dp</item>
+        <item name="android:layout_marginRight">32dp</item>
+        <item name="android:layout_marginBottom">12dp</item>
+        <item name="android:layout_marginLeft">32dp</item>
         <item name="android:background">@drawable/keypad_default_background</item>
         <item name="android:focusable">true</item>
     </style>
 
-    <style name="DialpadPrimaryButton">
-        <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_height">150dp</item>
-        <item name="android:layout_marginBottom">180dp</item>
-        <item name="android:scaleType">center</item>
+    <style name="TextAppearance.DialNumber" parent="android:style/TextAppearance">
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textSize">32sp</item>
+    </style>
+
+    <style name="SubheaderText" parent="android:style/TextAppearance">
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textSize">24sp</item>
+        <item name="android:textFontWeight">500</item>
+        <item name="android:textStyle">normal</item>
+    </style>
+
+    <style name="AddFavoriteText">
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+
+    <!-- Call history -->
+    <style name="TextAppearance.CallLogTitleDefault" parent="TextAppearance.Body1">
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+    <!-- Customized text color for missed calls can be added here -->
+    <style name="TextAppearance.CallLogTitleMissedCall" parent="TextAppearance.Body1">
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+
+    <style name="DialpadSecondaryButton">
+        <item name="android:background">@drawable/dialer_ripple_background</item>
+        <item name="android:scaleType">centerInside</item>
+        <item name="android:tint">?android:attr/textColorPrimary</item>
+    </style>
+
+    <style name="TextAppearance.DialpadDisplayName" parent="TextAppearance.Body1"/>
+
+    <style name="TextAppearance.ContactResultTitle" parent="TextAppearance.Body1">
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+
+    <style name="TextAppearance.TypeDownListSpan" parent="TextAppearance.Body3">
+        <item name="android:textStyle">bold</item>
+        <item name="android:textSize">32sp</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
 </resources>
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/xml/overlays.xml b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/xml/overlays.xml
index a404157..b75bc81 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/xml/overlays.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitDialerRRO/res/xml/overlays.xml
@@ -15,9 +15,40 @@
   ~ limitations under the License.
   -->
 <overlay>
-    <item target="style/KeypadButtonStyle" value="@style/KeypadButtonStyle"/>
-    <item target="style/DialpadPrimaryButton" value="@style/DialpadPrimaryButton"/>
-
     <item target="drawable/ic_phone" value="@drawable/ic_phone"/>
     <item target="drawable/icon_call_button" value="@drawable/icon_call_button"/>
+    <item target="drawable/ic_backspace" value="@drawable/ic_backspace"/>
+    <item target="drawable/dialer_ripple_background" value="@drawable/dialer_ripple_background"/>
+    <item target="drawable/restricted_dialing_mode_label_background" value="@drawable/restricted_dialing_mode_label_background"/>
+    <item target="drawable/ic_arrow_right" value="@drawable/ic_arrow_right"/>
+
+    <item target="id/dialpad_fragment" value="@id/dialpad_fragment" />
+    <item target="id/call_button" value="@id/call_button" />
+    <item target="id/title" value="@id/title" />
+    <item target="id/delete_button" value="@id/delete_button" />
+    <item target="id/display_name" value="@id/display_name" />
+    <item target="id/label" value="@id/label" />
+    <item target="id/dialpad_contact_avatar" value="@id/dialpad_contact_avatar" />
+    <item target="id/dialpad_contact_initials" value="@id/dialpad_contact_initials" />
+    <item target="id/list_view" value="@id/list_view" />
+    <item target="id/restricted_dialing_mode_label" value="@id/restricted_dialing_mode_label" />
+    <item target="id/contact_picture" value="@id/contact_picture" />
+    <item target="id/contact_result" value="@id/contact_result" />
+    <item target="id/contact_name" value="@id/contact_name" />
+    <item target="id/phone_number" value="@id/phone_number" />
+
+    <item target="layout/dialpad_fragment_with_type_down" value="@layout/dialpad_fragment_with_type_down"/>
+    <item target="layout/dialpad_user_profile" value="@layout/dialpad_user_profile"/>
+    <item target="layout/type_down_list_item" value="@layout/type_down_list_item"/>
+    <item target="layout/restricted_dialing_mode_label" value="@layout/restricted_dialing_mode_label"/>
+
+    <item target="style/KeypadButtonStyle" value="@style/KeypadButtonStyle"/>
+    <item target="style/TextAppearance.DialNumber" value="@style/TextAppearance.DialNumber"/>
+    <item target="style/SubheaderText" value="@style/SubheaderText"/>
+    <item target="style/AddFavoriteText" value="@style/AddFavoriteText"/>
+    <item target="style/TextAppearance.CallLogTitleDefault" value="@style/TextAppearance.CallLogTitleDefault"/>
+    <item target="style/TextAppearance.DialpadDisplayName" value="@style/TextAppearance.DialpadDisplayName"/>
+    <item target="style/TextAppearance.ContactResultTitle" value="@style/TextAppearance.ContactResultTitle"/>
+    <item target="style/TextAppearance.TypeDownListSpan" value="@style/TextAppearance.TypeDownListSpan"/>
+
 </overlay>
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 afc5169..c912ab9 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
@@ -21,4 +21,8 @@
         <item>com.android.car.carlauncher.homescreen.audio.AudioCard</item>
     </string-array>
 
+    <string-array name="config_foregroundDAComponents" translatable="false">
+        <item>com.android.car.carlauncher/.AppGridActivity</item>
+        <item>com.android.car.notification/.CarNotificationCenterActivity</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 8b153fa..9cc1245 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
@@ -54,5 +54,5 @@
     <item target="integer/enter_exit_animation_foreground_display_area_duration_ms" value="@integer/enter_exit_animation_foreground_display_area_duration_ms"/>
 
     <item target="array/config_homeCardModuleClasses" value="@array/config_homeCardModuleClasses"/>
-
+    <item target="array/config_foregroundDAComponents" value="@array/config_foregroundDAComponents"/>
 </overlay>
\ No newline at end of file
diff --git a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/car_notification_actions_view.xml b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/car_notification_actions_view.xml
index c3e5db0..a3c2ce3 100644
--- a/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/car_notification_actions_view.xml
+++ b/car_product/car_ui_portrait/rro/CarUiPortraitNotificationRRO/res/layout/car_notification_actions_view.xml
@@ -22,7 +22,7 @@
         android:layout_height="wrap_content"
         android:orientation="horizontal">
 
-        <Button
+        <com.android.car.notification.template.CarNotificationActionButton
             android:id="@+id/action_1"
             style="@style/NotificationActionButton1"
             android:layout_width="0dp"
@@ -33,7 +33,7 @@
             android:paddingTop="@dimen/action_button_padding_top"
             android:visibility="gone"/>
 
-        <Button
+        <com.android.car.notification.template.CarNotificationActionButton
             android:id="@+id/action_2"
             style="@style/NotificationActionButton2"
             android:layout_weight="1"
@@ -45,7 +45,7 @@
             android:paddingTop="@dimen/action_button_padding_top"
             android:visibility="gone"/>
 
-        <Button
+        <com.android.car.notification.template.CarNotificationActionButton
             android:id="@+id/action_3"
             style="@style/NotificationActionButton3"
             android:layout_width="0dp"
diff --git a/car_product/car_ui_portrait/rro/android/res/values/colors.xml b/car_product/car_ui_portrait/rro/android/res/values/colors.xml
index 8fc21cc..16f1e2b 100644
--- a/car_product/car_ui_portrait/rro/android/res/values/colors.xml
+++ b/car_product/car_ui_portrait/rro/android/res/values/colors.xml
@@ -65,4 +65,5 @@
   <color name="error_color_device_default_dark">#ec928e</color> <!-- Material Red 300 -->
   <color name="error_color_device_default_light">#b3261e</color> <!-- Material Red 600 -->
 
+  <color name="list_divider_color">#2E3134</color>
 </resources>
diff --git a/car_product/car_ui_portrait/rro/android/res/values/themes_device_defaults.xml b/car_product/car_ui_portrait/rro/android/res/values/themes_device_defaults.xml
index e1671e4..dc0198b 100644
--- a/car_product/car_ui_portrait/rro/android/res/values/themes_device_defaults.xml
+++ b/car_product/car_ui_portrait/rro/android/res/values/themes_device_defaults.xml
@@ -45,6 +45,8 @@
         <item name="android:colorButtonNormal">@color/btn_device_default_dark</item>
         <item name="android:colorControlHighlight">@color/btn_device_default_dark</item>
         <item name="android:colorControlNormal">@color/btn_device_default_dark</item>
+
+        <item name="android:listDivider">@color/list_divider_color</item>
     </style>
 
     <style name="Theme.DeviceDefault.Dialog" parent="android:Theme.Material.Dialog">
diff --git a/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/drawable/tab_background.xml b/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/drawable/tab_background.xml
index 0162e5b..ffbeb18 100644
--- a/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/drawable/tab_background.xml
+++ b/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/drawable/tab_background.xml
@@ -17,7 +17,7 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_activated="true">
         <shape android:shape="rectangle">
-            <solid android:color="#2E3134"/>
+            <solid android:color="@color/tab_background_color"/>
         </shape>
     </item>
     <item android:state_activated="false">
diff --git a/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/layout/car_ui_base_layout_toolbar.xml b/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/layout/car_ui_base_layout_toolbar.xml
index fe4c97f..02a7320 100644
--- a/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/layout/car_ui_base_layout_toolbar.xml
+++ b/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/layout/car_ui_base_layout_toolbar.xml
@@ -75,7 +75,7 @@
                     android:layout_gravity="center"
                     android:scaleType="fitXY"
                     android:background="@drawable/car_ui_toolbar_menu_item_icon_ripple"
-                    android:tint="@color/car_ui_text_color_primary"/>
+                    android:tint="?android:attr/textColorPrimary"/>
 
                 <ImageView
                     android:id="@+id/car_ui_toolbar_logo"
@@ -162,7 +162,7 @@
             <View
                 android:layout_width="match_parent"
                 android:layout_height="2dp"
-                android:background="#F1F3F4"
+                android:background="?android:attr/listDivider"
                 app:layout_constraintBottom_toBottomOf="parent"
                 app:layout_constraintEnd_toEndOf="parent"
                 app:layout_constraintStart_toStartOf="parent" />
@@ -175,6 +175,7 @@
         android:layout_width="wrap_content"
         android:layout_height="0dp"
         android:tag="car_ui_left_inset"
+        android:orientation="horizontal"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintTop_toBottomOf="@id/top_part_of_toolbar_focus_area"
         app:layout_constraintBottom_toBottomOf="parent">
@@ -189,10 +190,11 @@
     <!-- Hairline to the right of the tabs -->
     <View
         android:layout_width="2dp"
-        android:layout_height="0dp"
-        android:background="#F1F3F4"
-        app:layout_constraintBottom_toBottomOf="@id/left_part_of_toolbar_focus_area"
-        app:layout_constraintTop_toTopOf="@id/left_part_of_toolbar_focus_area"
-        app:layout_constraintEnd_toEndOf="@id/left_part_of_toolbar_focus_area" />
+        android:layout_height="match_parent"
+        android:background="?android:attr/listDivider"
+        android:focusable="false"
+        app:layout_constraintStart_toEndOf="@id/left_part_of_toolbar_focus_area"
+        app:layout_constraintTop_toBottomOf="@id/top_part_of_toolbar_focus_area"
+        app:layout_constraintBottom_toBottomOf="parent"/>
 
 </androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values-night/colors.xml b/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values-night/colors.xml
new file mode 100644
index 0000000..9134d34
--- /dev/null
+++ b/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values-night/colors.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <color name="tab_background_color">#282A2D</color>
+</resources>
diff --git a/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values/colors.xml b/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values/colors.xml
new file mode 100644
index 0000000..fca1ab1
--- /dev/null
+++ b/car_product/car_ui_portrait/rro/car-ui-toolbar-customizations/res/values/colors.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <color name="tab_background_color">#E8EAED</color>
+</resources>
diff --git a/car_product/overlay/frameworks/base/core/res/res/values-da/strings.xml b/car_product/overlay/frameworks/base/core/res/res/values-da/strings.xml
index 1c751f9..6db644f 100644
--- a/car_product/overlay/frameworks/base/core/res/res/values-da/strings.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/values-da/strings.xml
@@ -18,6 +18,6 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="owner_name" msgid="3416113395996003764">"Chauffør"</string>
-    <string name="permlab_accessCoarseLocation" msgid="2494909511737161237">"få kun adgang til omtrentlig placering i forgrunden"</string>
+    <string name="permlab_accessCoarseLocation" msgid="2494909511737161237">"få kun adgang til omtrentlig lokation i forgrunden"</string>
     <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="2093486820466005919">"Aktivér mikrofon"</string>
 </resources>
diff --git a/car_product/overlay/frameworks/base/core/res/res/values/config.xml b/car_product/overlay/frameworks/base/core/res/res/values/config.xml
index a1b0cd2..73326f1 100644
--- a/car_product/overlay/frameworks/base/core/res/res/values/config.xml
+++ b/car_product/overlay/frameworks/base/core/res/res/values/config.xml
@@ -156,4 +156,10 @@
      This flag should be enabled only when the product does not have any UI to toggle airplane
      mode like automotive devices.-->
     <bool name="config_autoResetAirplaneMode">true</bool>
+
+    <!-- The component name of the activity for the companion-device-manager notification access
+         confirmation. -->
+    <string name="config_notificationAccessConfirmationActivity" translatable="false">
+        com.android.car.settings/com.android.car.settings.notifications.NotificationAccessConfirmationActivity
+    </string>
 </resources>
diff --git a/service/src/com/android/car/hal/CarPropertyUtils.java b/service/src/com/android/car/hal/CarPropertyUtils.java
index 9ac2745..146a4fb 100644
--- a/service/src/com/android/car/hal/CarPropertyUtils.java
+++ b/service/src/com/android/car/hal/CarPropertyUtils.java
@@ -260,6 +260,12 @@
         int areaType = getVehicleAreaType(p.prop & VehicleArea.MASK);
 
         Class<?> clazz = getJavaClass(p.prop & VehiclePropertyType.MASK);
+        float maxSampleRate = 0f;
+        float minSampleRate = 0f;
+        if (p.changeMode != CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_STATIC) {
+            maxSampleRate = p.maxSampleRate;
+            minSampleRate = p.minSampleRate;
+        }
         if (p.areaConfigs.isEmpty()) {
             return CarPropertyConfig
                     .newBuilder(clazz, propertyId, areaType, /* capacity */ 1)
@@ -268,8 +274,8 @@
                     .setChangeMode(p.changeMode)
                     .setConfigArray(p.configArray)
                     .setConfigString(p.configString)
-                    .setMaxSampleRate(p.maxSampleRate)
-                    .setMinSampleRate(p.minSampleRate)
+                    .setMaxSampleRate(maxSampleRate)
+                    .setMinSampleRate(minSampleRate)
                     .build();
         } else {
             CarPropertyConfig.Builder builder = CarPropertyConfig
@@ -278,8 +284,8 @@
                     .setChangeMode(p.changeMode)
                     .setConfigArray(p.configArray)
                     .setConfigString(p.configString)
-                    .setMaxSampleRate(p.maxSampleRate)
-                    .setMinSampleRate(p.minSampleRate);
+                    .setMaxSampleRate(maxSampleRate)
+                    .setMinSampleRate(minSampleRate);
 
             for (VehicleAreaConfig area : p.areaConfigs) {
                 if (classMatched(Integer.class, clazz)) {
diff --git a/service/src/com/android/car/telemetry/CarTelemetryService.java b/service/src/com/android/car/telemetry/CarTelemetryService.java
index 59b5a68..537ce45 100644
--- a/service/src/com/android/car/telemetry/CarTelemetryService.java
+++ b/service/src/com/android/car/telemetry/CarTelemetryService.java
@@ -15,26 +15,26 @@
  */
 package com.android.car.telemetry;
 
-import static android.car.telemetry.CarTelemetryManager.ERROR_NEWER_MANIFEST_EXISTS;
-import static android.car.telemetry.CarTelemetryManager.ERROR_NONE;
-import static android.car.telemetry.CarTelemetryManager.ERROR_PARSE_MANIFEST_FAILED;
-import static android.car.telemetry.CarTelemetryManager.ERROR_SAME_MANIFEST_EXISTS;
+import static android.car.telemetry.CarTelemetryManager.ERROR_METRICS_CONFIG_NONE;
+import static android.car.telemetry.CarTelemetryManager.ERROR_METRICS_CONFIG_PARSE_FAILED;
+import static android.car.telemetry.CarTelemetryManager.ERROR_METRICS_CONFIG_UNKNOWN;
 
 import android.annotation.NonNull;
 import android.app.StatsManager;
 import android.car.Car;
-import android.car.telemetry.CarTelemetryManager.AddManifestError;
 import android.car.telemetry.ICarTelemetryService;
 import android.car.telemetry.ICarTelemetryServiceListener;
-import android.car.telemetry.ManifestKey;
+import android.car.telemetry.MetricsConfigKey;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.RemoteException;
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
 
 import com.android.car.CarLocalServices;
+import com.android.car.CarLog;
 import com.android.car.CarPropertyService;
 import com.android.car.CarServiceBase;
 import com.android.car.CarServiceUtils;
@@ -46,14 +46,12 @@
 import com.android.car.telemetry.publisher.StatsManagerImpl;
 import com.android.car.telemetry.publisher.StatsManagerProxy;
 import com.android.car.telemetry.systemmonitor.SystemMonitor;
-import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 
 import com.google.protobuf.InvalidProtocolBufferException;
 
 import java.io.File;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 /**
  * CarTelemetryService manages OEM telemetry collection, processing and communication
@@ -61,11 +59,7 @@
  */
 public class CarTelemetryService extends ICarTelemetryService.Stub implements CarServiceBase {
 
-    // TODO(b/189340793): Rename Manifest to MetricsConfig
-
     private static final boolean DEBUG = false;
-    private static final int DEFAULT_VERSION = 0;
-    private static final String TAG = CarTelemetryService.class.getSimpleName();
     public static final String TELEMETRY_DIR = "telemetry";
 
     private final Context mContext;
@@ -74,11 +68,7 @@
     private final HandlerThread mTelemetryThread = CarServiceUtils.getHandlerThread(
             CarTelemetryService.class.getSimpleName());
     private final Handler mTelemetryHandler = new Handler(mTelemetryThread.getLooper());
-    @GuardedBy("mLock")
-    private final Map<String, Integer> mNameVersionMap = new HashMap<>();
-    private final Object mLock = new Object();
 
-    @GuardedBy("mLock")
     private ICarTelemetryServiceListener mListener;
     private DataBroker mDataBroker;
     private DataBrokerController mDataBrokerController;
@@ -140,9 +130,12 @@
         // TODO(b/184890506): verify that only a hardcoded app can set the listener
         mContext.enforceCallingOrSelfPermission(
                 Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "setListener");
-        synchronized (mLock) {
-            setListenerLocked(listener);
-        }
+        mTelemetryHandler.post(() -> {
+            if (DEBUG) {
+                Slog.d(CarLog.TAG_TELEMETRY, "Setting the listener for car telemetry service");
+            }
+            mListener = listener;
+        });
     }
 
     /**
@@ -151,50 +144,91 @@
     @Override
     public void clearListener() {
         mContext.enforceCallingOrSelfPermission(
-                Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "setListener");
-        synchronized (mLock) {
-            clearListenerLocked();
-        }
+                Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "clearListener");
+        mTelemetryHandler.post(() -> {
+            if (DEBUG) {
+                Slog.d(CarLog.TAG_TELEMETRY, "Clearing the listener for car telemetry service");
+            }
+            mListener = null;
+        });
     }
 
     /**
-     * Allows client to send telemetry manifests.
+     * Send a telemetry metrics config to the service.
      *
-     * @param key    the unique key to identify the manifest.
-     * @param config the serialized bytes of a Manifest object.
-     * @return {@link AddManifestError} the error code.
+     * @param key    the unique key to identify the MetricsConfig.
+     * @param config the serialized bytes of a MetricsConfig object.
      */
     @Override
-    public @AddManifestError int addManifest(@NonNull ManifestKey key, @NonNull byte[] config) {
+    public void addMetricsConfig(@NonNull MetricsConfigKey key, @NonNull byte[] config) {
         mContext.enforceCallingOrSelfPermission(
-                Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "setListener");
-        synchronized (mLock) {
-            return addManifestLocked(key, config);
-        }
+                Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "addMetricsConfig");
+        mTelemetryHandler.post(() -> {
+            Slog.d(CarLog.TAG_TELEMETRY, "Adding metrics config " + key.getName()
+                    + " to car telemetry service");
+            // TODO(b/199540952): If config is active, add it to DataBroker
+            TelemetryProto.MetricsConfig metricsConfig = null;
+            int status = ERROR_METRICS_CONFIG_UNKNOWN;
+            try {
+                metricsConfig = TelemetryProto.MetricsConfig.parseFrom(config);
+            } catch (InvalidProtocolBufferException e) {
+                Slog.e(CarLog.TAG_TELEMETRY, "Failed to parse MetricsConfig.", e);
+                status = ERROR_METRICS_CONFIG_PARSE_FAILED;
+            }
+            // if config can be parsed, add it to persistent storage
+            if (metricsConfig != null) {
+                status = mMetricsConfigStore.addMetricsConfig(metricsConfig);
+            }
+            // If no error (a config is successfully added), script results from an older version
+            // should be deleted
+            if (status == ERROR_METRICS_CONFIG_NONE) {
+                mResultStore.deleteResult(key.getName());
+            }
+            try {
+                mListener.onAddMetricsConfigStatus(key, status);
+            } catch (RemoteException e) {
+                Slog.d(CarLog.TAG_TELEMETRY, "error with ICarTelemetryServiceListener", e);
+            }
+        });
     }
 
     /**
-     * Removes a manifest based on the key.
+     * Removes a metrics config based on the key. This will also remove outputs produced by the
+     * MetricsConfig.
      */
     @Override
-    public boolean removeManifest(@NonNull ManifestKey key) {
+    public void removeMetricsConfig(@NonNull MetricsConfigKey key) {
         mContext.enforceCallingOrSelfPermission(
-                Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "setListener");
-        synchronized (mLock) {
-            return removeManifestLocked(key);
-        }
+                Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "removeMetricsConfig");
+        mTelemetryHandler.post(() -> {
+            Slog.d(CarLog.TAG_TELEMETRY, "Removing metrics config " + key.getName()
+                    + " from car telemetry service");
+            // TODO(b/198792767): Check both config name and config version for deletion
+            // TODO(b/199540952): Stop and remove config from data broker
+            mResultStore.deleteResult(key.getName()); // delete the config's script results
+            boolean success = mMetricsConfigStore.deleteMetricsConfig(key.getName());
+            try {
+                mListener.onRemoveMetricsConfigStatus(key, success);
+            } catch (RemoteException e) {
+                Slog.d(CarLog.TAG_TELEMETRY, "error with ICarTelemetryServiceListener", e);
+            }
+        });
     }
 
     /**
-     * Removes all manifests.
+     * Removes all MetricsConfigs. This will also remove all MetricsConfig outputs.
      */
     @Override
-    public void removeAllManifests() {
+    public void removeAllMetricsConfigs() {
         mContext.enforceCallingOrSelfPermission(
-                Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "setListener");
-        synchronized (mLock) {
-            removeAllManifestsLocked();
-        }
+                Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "removeAllMetricsConfig");
+        mTelemetryHandler.post(() -> {
+            // TODO(b/199540952): Stop and remove all configs from DataBroker
+            Slog.d(CarLog.TAG_TELEMETRY,
+                    "Removing all metrics config from car telemetry service");
+            mMetricsConfigStore.deleteAllMetricsConfigs();
+            mResultStore.deleteAllResults();
+        });
     }
 
     /**
@@ -202,99 +236,40 @@
      * {@link ICarTelemetryServiceListener}.
      */
     @Override
-    public void sendFinishedReports(@NonNull ManifestKey key) {
+    public void sendFinishedReports(@NonNull MetricsConfigKey key) {
         // TODO(b/184087869): Implement
         mContext.enforceCallingOrSelfPermission(
-                Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "setListener");
+                Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "sendFinishedReports");
         if (DEBUG) {
-            Slog.d(TAG, "Flushing reports for a manifest");
+            Slog.d(CarLog.TAG_TELEMETRY, "Flushing reports for a metrics config");
         }
     }
 
     /**
-     * Sends all script results associated using the {@link ICarTelemetryServiceListener}.
+     * Sends all script results or errors using the {@link ICarTelemetryServiceListener}.
      */
     @Override
     public void sendAllFinishedReports() {
         // TODO(b/184087869): Implement
         mContext.enforceCallingOrSelfPermission(
-                Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "setListener");
+                Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "sendAllFinishedReports");
         if (DEBUG) {
-            Slog.d(TAG, "Flushing all reports");
+            Slog.d(CarLog.TAG_TELEMETRY, "Flushing all reports");
         }
     }
 
-    /**
-     * Sends all errors using the {@link ICarTelemetryServiceListener}.
-     */
-    @Override
-    public void sendScriptExecutionErrors() {
-        // TODO(b/184087869): Implement
-        mContext.enforceCallingOrSelfPermission(
-                Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE, "setListener");
-        if (DEBUG) {
-            Slog.d(TAG, "Flushing script execution errors");
-        }
+    @VisibleForTesting
+    Handler getTelemetryHandler() {
+        return mTelemetryHandler;
     }
 
-    @GuardedBy("mLock")
-    private void setListenerLocked(@NonNull ICarTelemetryServiceListener listener) {
-        if (DEBUG) {
-            Slog.d(TAG, "Setting the listener for car telemetry service");
-        }
-        mListener = listener;
+    @VisibleForTesting
+    ResultStore getResultStore() {
+        return mResultStore;
     }
 
-    @GuardedBy("mLock")
-    private void clearListenerLocked() {
-        if (DEBUG) {
-            Slog.d(TAG, "Clearing listener");
-        }
-        mListener = null;
-    }
-
-    @GuardedBy("mLock")
-    private @AddManifestError int addManifestLocked(ManifestKey key, byte[] configProto) {
-        if (DEBUG) {
-            Slog.d(TAG, "Adding MetricsConfig to car telemetry service");
-        }
-        int currentVersion = mNameVersionMap.getOrDefault(key.getName(), DEFAULT_VERSION);
-        if (currentVersion > key.getVersion()) {
-            return ERROR_NEWER_MANIFEST_EXISTS;
-        } else if (currentVersion == key.getVersion()) {
-            return ERROR_SAME_MANIFEST_EXISTS;
-        }
-
-        TelemetryProto.MetricsConfig metricsConfig;
-        try {
-            metricsConfig = TelemetryProto.MetricsConfig.parseFrom(configProto);
-        } catch (InvalidProtocolBufferException e) {
-            Slog.e(TAG, "Failed to parse MetricsConfig.", e);
-            return ERROR_PARSE_MANIFEST_FAILED;
-        }
-        mNameVersionMap.put(key.getName(), key.getVersion());
-
-        // TODO(b/186047142): Store the MetricsConfig to disk
-        // TODO(b/186047142): Send metricsConfig to a script manager or a queue
-        return ERROR_NONE;
-    }
-
-    @GuardedBy("mLock")
-    private boolean removeManifestLocked(@NonNull ManifestKey key) {
-        Integer version = mNameVersionMap.remove(key.getName());
-        if (version == null) {
-            return false;
-        }
-        // TODO(b/186047142): Delete manifest from disk and remove it from queue
-        return true;
-    }
-
-    @GuardedBy("mLock")
-    private void removeAllManifestsLocked() {
-        if (DEBUG) {
-            Slog.d(TAG, "Removing all manifest from car telemetry service");
-        }
-        mNameVersionMap.clear();
-        // TODO(b/186047142): Delete all manifests from disk & queue
+    @VisibleForTesting
+    MetricsConfigStore getMetricsConfigStore() {
+        return mMetricsConfigStore;
     }
 }
diff --git a/service/src/com/android/car/telemetry/MetricsConfigStore.java b/service/src/com/android/car/telemetry/MetricsConfigStore.java
index eb7f7ba..7a5512a 100644
--- a/service/src/com/android/car/telemetry/MetricsConfigStore.java
+++ b/service/src/com/android/car/telemetry/MetricsConfigStore.java
@@ -16,6 +16,12 @@
 
 package com.android.car.telemetry;
 
+import static android.car.telemetry.CarTelemetryManager.ERROR_METRICS_CONFIG_ALREADY_EXISTS;
+import static android.car.telemetry.CarTelemetryManager.ERROR_METRICS_CONFIG_NONE;
+import static android.car.telemetry.CarTelemetryManager.ERROR_METRICS_CONFIG_UNKNOWN;
+import static android.car.telemetry.CarTelemetryManager.ERROR_METRICS_CONFIG_VERSION_TOO_OLD;
+
+import android.util.ArrayMap;
 import android.util.Slog;
 
 import com.android.car.CarLog;
@@ -26,6 +32,7 @@
 import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 /**
  * This class is responsible for storing, retrieving, and deleting {@link
@@ -37,28 +44,34 @@
     static final String METRICS_CONFIG_DIR = "metrics_configs";
 
     private final File mConfigDirectory;
+    private Map<String, TelemetryProto.MetricsConfig> mActiveConfigs;
+    private Map<String, Integer> mNameVersionMap;
 
     MetricsConfigStore(File rootDirectory) {
         mConfigDirectory = new File(rootDirectory, METRICS_CONFIG_DIR);
         mConfigDirectory.mkdirs();
+        mActiveConfigs = new ArrayMap<>();
+        mNameVersionMap = new ArrayMap<>();
+        // TODO(b/197336485): Add expiration date check for MetricsConfig
+        for (File file : mConfigDirectory.listFiles()) {
+            try {
+                byte[] serializedConfig = Files.readAllBytes(file.toPath());
+                TelemetryProto.MetricsConfig config =
+                        TelemetryProto.MetricsConfig.parseFrom(serializedConfig);
+                mActiveConfigs.put(config.getName(), config);
+                mNameVersionMap.put(config.getName(), config.getVersion());
+            } catch (IOException e) {
+                // TODO(b/197336655): record failure
+                file.delete();
+            }
+        }
     }
 
     /**
      * Returns all active {@link TelemetryProto.MetricsConfig} from disk.
      */
     List<TelemetryProto.MetricsConfig> getActiveMetricsConfigs() {
-        // TODO(b/197336485): Add expiration date check for MetricsConfig
-        List<TelemetryProto.MetricsConfig> activeConfigs = new ArrayList<>();
-        for (File file : mConfigDirectory.listFiles()) {
-            try {
-                byte[] serializedConfig = Files.readAllBytes(file.toPath());
-                activeConfigs.add(TelemetryProto.MetricsConfig.parseFrom(serializedConfig));
-            } catch (IOException e) {
-                // TODO(b/197336655): record failure
-                file.delete();
-            }
-        }
-        return activeConfigs;
+        return new ArrayList<>(mActiveConfigs.values());
     }
 
     /**
@@ -67,8 +80,17 @@
      * @param metricsConfig the config to be persisted to disk.
      * @return true if the MetricsConfig should start receiving data, false otherwise.
      */
-    boolean addMetricsConfig(TelemetryProto.MetricsConfig metricsConfig) {
-        // TODO(b/197336485): Check version and expiration date for MetricsConfig
+    int addMetricsConfig(TelemetryProto.MetricsConfig metricsConfig) {
+        // TODO(b/198823862): Validate config version
+        // TODO(b/197336485): Check expiration date for MetricsConfig
+        int currentVersion = mNameVersionMap.getOrDefault(metricsConfig.getName(), -1);
+        if (currentVersion > metricsConfig.getVersion()) {
+            return ERROR_METRICS_CONFIG_VERSION_TOO_OLD;
+        } else if (currentVersion == metricsConfig.getVersion()) {
+            return ERROR_METRICS_CONFIG_ALREADY_EXISTS;
+        }
+        mActiveConfigs.put(metricsConfig.getName(), metricsConfig);
+        mNameVersionMap.put(metricsConfig.getName(), metricsConfig.getVersion());
         try {
             Files.write(
                     new File(mConfigDirectory, metricsConfig.getName()).toPath(),
@@ -76,12 +98,21 @@
         } catch (IOException e) {
             // TODO(b/197336655): record failure
             Slog.w(CarLog.TAG_TELEMETRY, "Failed to write metrics config to disk", e);
+            return ERROR_METRICS_CONFIG_UNKNOWN;
         }
-        return true;
+        return ERROR_METRICS_CONFIG_NONE;
     }
 
     /** Deletes the MetricsConfig from disk. Returns the success status. */
     boolean deleteMetricsConfig(String metricsConfigName) {
+        mActiveConfigs.remove(metricsConfigName);
         return new File(mConfigDirectory, metricsConfigName).delete();
     }
+
+    void deleteAllMetricsConfigs() {
+        mActiveConfigs.clear();
+        for (File file : mConfigDirectory.listFiles()) {
+            file.delete();
+        }
+    }
 }
diff --git a/service/src/com/android/car/telemetry/ResultStore.java b/service/src/com/android/car/telemetry/ResultStore.java
index 976ee8e..ffa3d09 100644
--- a/service/src/com/android/car/telemetry/ResultStore.java
+++ b/service/src/com/android/car/telemetry/ResultStore.java
@@ -97,27 +97,23 @@
      *
      * @param metricsConfigName name of the MetricsConfig.
      * @param deleteResult      if true, the final result will be deleted from disk.
-     * @param callback          for receiving the metrics output. If result does not exist, it will
-     *                          receive a null value.
      */
-    public void getFinalResult(
-            String metricsConfigName, boolean deleteResult, FinalResultCallback callback) {
+    public PersistableBundle getFinalResult(String metricsConfigName, boolean deleteResult) {
         File file = new File(mFinalResultDirectory, metricsConfigName);
         // if no final result exists for this metrics config, return immediately
         if (!file.exists()) {
-            callback.onFinalResult(metricsConfigName, null);
-            return;
+            return null;
         }
+        PersistableBundle result = null;
         try (FileInputStream fis = new FileInputStream(file)) {
-            PersistableBundle bundle = PersistableBundle.readFromStream(fis);
-            callback.onFinalResult(metricsConfigName, bundle);
+            result = PersistableBundle.readFromStream(fis);
         } catch (IOException e) {
             Slog.w(CarLog.TAG_TELEMETRY, "Failed to get final result from disk.", e);
-            callback.onFinalResult(metricsConfigName, null);
         }
         if (deleteResult) {
             file.delete();
         }
+        return result;
     }
 
     /**
@@ -136,6 +132,27 @@
         deleteAllStaleData(mInterimResultDirectory, mFinalResultDirectory);
     }
 
+    /**
+     * Deletes script result associated with the given config name. If result does not exist, this
+     * method does not do anything.
+     */
+    public void deleteResult(String metricsConfigName) {
+        mInterimResultCache.remove(metricsConfigName);
+        deleteFileInDirectory(mInterimResultDirectory, metricsConfigName);
+        deleteFileInDirectory(mFinalResultDirectory, metricsConfigName);
+    }
+
+    /** Deletes all interim and final results stored in disk. */
+    public void deleteAllResults() {
+        mInterimResultCache.clear();
+        for (File interimResult : mInterimResultDirectory.listFiles()) {
+            interimResult.delete();
+        }
+        for (File finalResult : mFinalResultDirectory.listFiles()) {
+            finalResult.delete();
+        }
+    }
+
     /** Writes dirty interim results to disk. */
     private void writeInterimResultsToFile() {
         mInterimResultCache.forEach((metricsConfigName, interimResult) -> {
@@ -180,11 +197,6 @@
         file.delete();
     }
 
-    /** Callback for receiving final metrics output. */
-    interface FinalResultCallback {
-        void onFinalResult(String metricsConfigName, PersistableBundle result);
-    }
-
     /** Wrapper around a result and whether the result should be written to disk. */
     static final class InterimResult {
         private final PersistableBundle mBundle;
diff --git a/service/src/com/android/car/telemetry/publisher/StatsPublisher.java b/service/src/com/android/car/telemetry/publisher/StatsPublisher.java
index e551f1a..8d5f7ea 100644
--- a/service/src/com/android/car/telemetry/publisher/StatsPublisher.java
+++ b/service/src/com/android/car/telemetry/publisher/StatsPublisher.java
@@ -47,8 +47,8 @@
  * Publisher for {@link TelemetryProto.StatsPublisher}.
  *
  * <p>The publisher adds subscriber configurations in StatsD and they persist between reboots and
- * CarTelemetryService restarts. Please use {@link #removeAllDataSubscribers} or
- * {@link #removeDataSubscriber} to clean-up these configs from StatsD store.
+ * CarTelemetryService restarts. Please use {@link #removeAllDataSubscribers} to clean-up these
+ * configs from StatsD store.
  */
 public class StatsPublisher extends AbstractPublisher {
     // These IDs are used in StatsdConfig and ConfigMetricsReport.
@@ -124,22 +124,28 @@
         }
     }
 
+    private void processReport(long configKey, StatsLogProto.ConfigMetricsReportList report) {
+        // TODO(b/197269115): parse the report
+        Slog.i(CarLog.TAG_TELEMETRY, "Received reports: " + report.getReportsCount());
+        if (report.getReportsCount() > 0) {
+            PersistableBundle data = new PersistableBundle();
+            // TODO(b/197269115): parse the report
+            data.putInt("reportsCount", report.getReportsCount());
+            DataSubscriber subscriber = getSubscriberByConfigKey(configKey);
+            if (subscriber != null) {
+                subscriber.push(data);
+            }
+        }
+    }
+
     private void pullReportsPeriodically() {
         for (long configKey : getActiveConfigKeys()) {
             try {
-                StatsLogProto.ConfigMetricsReportList report =
-                        StatsLogProto.ConfigMetricsReportList.parseFrom(
-                                mStatsManager.getReports(configKey));
-                // TODO(b/197269115): parse the report
-                Slog.i(CarLog.TAG_TELEMETRY, "Received reports: " + report.getReportsCount());
-                if (report.getReportsCount() > 0) {
-                    PersistableBundle data = new PersistableBundle();
-                    // TODO(b/197269115): parse the report
-                    data.putInt("reportsCount", report.getReportsCount());
-                    mConfigKeyToSubscribers.get(configKey).push(data);
-                }
+                processReport(configKey, StatsLogProto.ConfigMetricsReportList.parseFrom(
+                        mStatsManager.getReports(configKey)));
             } catch (StatsUnavailableException e) {
-                // TODO(b/189143813): retry if stats is not available
+                // 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,
@@ -206,9 +212,12 @@
                     String sharedPrefVersion = buildSharedPrefConfigVersionKey(configKey);
                     editor.remove(key).remove(sharedPrefVersion);
                 } catch (StatsUnavailableException e) {
-                    Slog.w(CarLog.TAG_TELEMETRY, "Failed to remove config " + configKey, e);
-                    // TODO(b/189143813): if StatsManager is not ready, retry N times and hard fail
-                    //                    after by notifying DataBroker.
+                    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.
                 }
             });
             editor.apply();
@@ -230,6 +239,13 @@
         }
     }
 
+    /** 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 the key for SharedPreferences to store/retrieve configKey associated with the
      * subscriber.
@@ -254,21 +270,18 @@
      */
     @GuardedBy("mLock")
     private long addStatsConfigLocked(DataSubscriber subscriber) {
-        String sharedPrefConfigKey = buildSharedPrefConfigKey(subscriber);
         long configKey = buildConfigKey(subscriber);
         // Store MetricsConfig (of CarTelemetryService) version per handler_function.
         String sharedPrefVersion = buildSharedPrefConfigVersionKey(configKey);
-        StatsdConfig config = buildStatsdConfig(subscriber, configKey);
         if (mSharedPreferences.contains(sharedPrefVersion)) {
             int currentVersion = mSharedPreferences.getInt(sharedPrefVersion, 0);
-            if (currentVersion < subscriber.getMetricsConfig().getVersion()) {
-                // TODO(b/189143813): remove old version from StatsD
-                Slog.d(CarLog.TAG_TELEMETRY, "Removing old config from StatsD");
-            } else {
-                // Ignore if the MetricsConfig version is current or older.
+            if (currentVersion >= subscriber.getMetricsConfig().getVersion()) {
+                // It's trying to add current or older MetricsConfig version, just ignore it.
                 return configKey;
-            }
+            }  // if the subscriber's MetricsConfig version is newer, it will replace the old one.
         }
+        String sharedPrefConfigKey = buildSharedPrefConfigKey(subscriber);
+        StatsdConfig config = buildStatsdConfig(subscriber, configKey);
         try {
             // It doesn't throw exception if the StatsdConfig is invalid. But it shouldn't happen,
             // as we generate well-tested StatsdConfig in this service.
@@ -281,6 +294,9 @@
             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(
+                    new IllegalStateException("Failed to add config " + configKey, e));
         }
         return configKey;
     }
@@ -296,9 +312,12 @@
             mStatsManager.removeConfig(configKey);
             mSharedPreferences.edit().remove(sharedPrefVersion).remove(sharedPrefConfigKey).apply();
         } catch (StatsUnavailableException e) {
-            Slog.w(CarLog.TAG_TELEMETRY, "Failed to remove config " + configKey, e);
-            // TODO(b/189143813): if StatsManager is not ready, retry N times and hard fail after
-            //                    by notifying DataBroker.
+            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 we
+            // retry. So we will just ignore the failures. The next call of this method will
+            // try deleting StatsD configs again.
         }
         return configKey;
     }
diff --git a/tests/CarLibTests/src/android/car/CarTelemetryManagerTest.java b/tests/CarLibTests/src/android/car/CarTelemetryManagerTest.java
deleted file mode 100644
index 906662d..0000000
--- a/tests/CarLibTests/src/android/car/CarTelemetryManagerTest.java
+++ /dev/null
@@ -1,175 +0,0 @@
-/*
- * 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;
-
-import static android.car.telemetry.CarTelemetryManager.ERROR_NONE;
-import static android.car.telemetry.CarTelemetryManager.ERROR_SAME_MANIFEST_EXISTS;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.verify;
-
-import android.app.Application;
-import android.car.telemetry.CarTelemetryManager;
-import android.car.telemetry.ManifestKey;
-import android.car.testapi.CarTelemetryController;
-import android.car.testapi.FakeCar;
-
-import androidx.test.core.app.ApplicationProvider;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.annotation.internal.DoNotInstrument;
-
-import java.util.concurrent.Executor;
-
-@RunWith(RobolectricTestRunner.class)
-@DoNotInstrument
-public class CarTelemetryManagerTest {
-    @Rule
-    public MockitoRule rule = MockitoJUnit.rule();
-
-    private static final byte[] ERROR_BYTES = "ERROR".getBytes();
-    private static final byte[] MANIFEST_BYTES = "MANIFEST".getBytes();
-    private static final byte[] SCRIPT_RESULT_BYTES = "SCRIPT RESULT".getBytes();
-    private static final ManifestKey DEFAULT_MANIFEST_KEY =
-            new ManifestKey("NAME", 1);
-    private static final Executor DIRECT_EXECUTOR = Runnable::run;
-
-    private CarTelemetryController mCarTelemetryController;
-    private CarTelemetryManager mCarTelemetryManager;
-
-    @Mock
-    private CarTelemetryManager.CarTelemetryResultsListener mListener;
-
-
-
-    @Before
-    public void setUp() {
-        Application context = ApplicationProvider.getApplicationContext();
-        FakeCar fakeCar = FakeCar.createFakeCar(context);
-        Car carApi = fakeCar.getCar();
-
-        mCarTelemetryManager =
-                (CarTelemetryManager) carApi.getCarManager(Car.CAR_TELEMETRY_SERVICE);
-        mCarTelemetryController = fakeCar.getCarTelemetryController();
-    }
-
-    @Test
-    public void setListener_shouldSucceed() {
-        mCarTelemetryManager.setListener(DIRECT_EXECUTOR, mListener);
-
-        assertThat(mCarTelemetryController.isListenerSet()).isTrue();
-    }
-
-    @Test
-    public void clearListener_shouldSucceed() {
-        mCarTelemetryManager.setListener(DIRECT_EXECUTOR, mListener);
-        mCarTelemetryManager.clearListener();
-
-        assertThat(mCarTelemetryController.isListenerSet()).isFalse();
-    }
-
-    @Test
-    public void addManifest_whenNew_shouldSucceed() {
-        int result = mCarTelemetryManager.addManifest(DEFAULT_MANIFEST_KEY, MANIFEST_BYTES);
-
-        assertThat(result).isEqualTo(ERROR_NONE);
-        assertThat(mCarTelemetryController.getValidManifestsCount()).isEqualTo(1);
-    }
-
-    @Test
-    public void addManifest_whenDuplicate_shouldIgnore() {
-        int firstResult =
-                mCarTelemetryManager.addManifest(DEFAULT_MANIFEST_KEY, MANIFEST_BYTES);
-        int secondResult =
-                mCarTelemetryManager.addManifest(DEFAULT_MANIFEST_KEY, MANIFEST_BYTES);
-
-        assertThat(firstResult).isEqualTo(ERROR_NONE);
-        assertThat(secondResult).isEqualTo(ERROR_SAME_MANIFEST_EXISTS);
-        assertThat(mCarTelemetryController.getValidManifestsCount()).isEqualTo(1);
-    }
-
-    @Test
-    public void removeManifest_whenValid_shouldSucceed() {
-        mCarTelemetryManager.addManifest(DEFAULT_MANIFEST_KEY, MANIFEST_BYTES);
-
-        boolean result = mCarTelemetryManager.removeManifest(DEFAULT_MANIFEST_KEY);
-
-        assertThat(result).isTrue();
-        assertThat(mCarTelemetryController.getValidManifestsCount()).isEqualTo(0);
-    }
-
-    @Test
-    public void removeManifest_whenInvalid_shouldIgnore() {
-        mCarTelemetryManager.addManifest(DEFAULT_MANIFEST_KEY, MANIFEST_BYTES);
-
-        boolean result = mCarTelemetryManager.removeManifest(new ManifestKey("NAME", 100));
-
-        assertThat(result).isFalse();
-        assertThat(mCarTelemetryController.getValidManifestsCount()).isEqualTo(1);
-    }
-
-    @Test
-    public void removeAllManifests_shouldSucceed() {
-        mCarTelemetryManager.addManifest(DEFAULT_MANIFEST_KEY, MANIFEST_BYTES);
-        mCarTelemetryManager.addManifest(new ManifestKey("NAME", 100), MANIFEST_BYTES);
-
-        mCarTelemetryManager.removeAllManifests();
-
-        assertThat(mCarTelemetryController.getValidManifestsCount()).isEqualTo(0);
-    }
-
-    @Test
-    public void sendFinishedReports_shouldSucceed() {
-        mCarTelemetryManager.setListener(DIRECT_EXECUTOR, mListener);
-        mCarTelemetryController.addDataForKey(DEFAULT_MANIFEST_KEY, SCRIPT_RESULT_BYTES);
-
-        mCarTelemetryManager.sendFinishedReports(DEFAULT_MANIFEST_KEY);
-
-        verify(mListener).onResult(DEFAULT_MANIFEST_KEY, SCRIPT_RESULT_BYTES);
-    }
-
-    @Test
-    public void sendAllFinishedReports_shouldSucceed() {
-        mCarTelemetryManager.setListener(DIRECT_EXECUTOR, mListener);
-        mCarTelemetryController.addDataForKey(DEFAULT_MANIFEST_KEY, SCRIPT_RESULT_BYTES);
-        ManifestKey key2 = new ManifestKey("key name", 1);
-        mCarTelemetryController.addDataForKey(key2, SCRIPT_RESULT_BYTES);
-
-        mCarTelemetryManager.sendAllFinishedReports();
-
-        verify(mListener).onResult(DEFAULT_MANIFEST_KEY, SCRIPT_RESULT_BYTES);
-        verify(mListener).onResult(key2, SCRIPT_RESULT_BYTES);
-    }
-
-    @Test
-    public void sendScriptExecutionErrors_shouldSucceed() {
-        mCarTelemetryManager.setListener(DIRECT_EXECUTOR, mListener);
-        mCarTelemetryController.setErrorData(ERROR_BYTES);
-
-        mCarTelemetryManager.sendScriptExecutionErrors();
-
-        verify(mListener).onError(ERROR_BYTES);
-    }
-}
diff --git a/tests/CarSecurityPermissionTest/src/com/android/car/telemetry/CarTelemetryManagerPermissionTest.java b/tests/CarSecurityPermissionTest/src/com/android/car/telemetry/CarTelemetryManagerPermissionTest.java
index 509c931..84409cf 100644
--- a/tests/CarSecurityPermissionTest/src/com/android/car/telemetry/CarTelemetryManagerPermissionTest.java
+++ b/tests/CarSecurityPermissionTest/src/com/android/car/telemetry/CarTelemetryManagerPermissionTest.java
@@ -22,7 +22,7 @@
 
 import android.car.Car;
 import android.car.telemetry.CarTelemetryManager;
-import android.car.telemetry.ManifestKey;
+import android.car.telemetry.MetricsConfigKey;
 import android.content.Context;
 
 import androidx.annotation.NonNull;
@@ -45,8 +45,8 @@
 public class CarTelemetryManagerPermissionTest {
     private final Context mContext =
             InstrumentationRegistry.getInstrumentation().getTargetContext();
-    private final ManifestKey mManifestKey = new ManifestKey("name", 1);
-    private final byte[] mManifestBytes = "manifest".getBytes();
+    private final MetricsConfigKey mMetricsConfigKey = new MetricsConfigKey("name", 1);
+    private final byte[] mMetricsConfigBytes = "manifest".getBytes();
 
     private Car mCar;
     private CarTelemetryManager mCarTelemetryManager;
@@ -83,25 +83,26 @@
     }
 
     @Test
-    public void testAddManifest() throws Exception {
+    public void testAddMetricsConfig() throws Exception {
         Exception e = expectThrows(SecurityException.class,
-                () -> mCarTelemetryManager.addManifest(mManifestKey, mManifestBytes));
+                () -> mCarTelemetryManager.addMetricsConfig(mMetricsConfigKey,
+                        mMetricsConfigBytes));
 
         assertThat(e.getMessage()).contains(Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE);
     }
 
     @Test
-    public void testRemoveManifest() throws Exception {
+    public void testRemoveMetricsConfig() throws Exception {
         Exception e = expectThrows(SecurityException.class,
-                () -> mCarTelemetryManager.removeManifest(mManifestKey));
+                () -> mCarTelemetryManager.removeMetricsConfig(mMetricsConfigKey));
 
         assertThat(e.getMessage()).contains(Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE);
     }
 
     @Test
-    public void testRemoveAllManifests() throws Exception {
+    public void testRemoveAllMetricsConfigs() throws Exception {
         Exception e = expectThrows(SecurityException.class,
-                () -> mCarTelemetryManager.removeAllManifests());
+                () -> mCarTelemetryManager.removeAllMetricsConfigs());
 
         assertThat(e.getMessage()).contains(Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE);
     }
@@ -109,7 +110,7 @@
     @Test
     public void testSendFinishedReports() throws Exception {
         Exception e = expectThrows(SecurityException.class,
-                () -> mCarTelemetryManager.sendFinishedReports(mManifestKey));
+                () -> mCarTelemetryManager.sendFinishedReports(mMetricsConfigKey));
 
         assertThat(e.getMessage()).contains(Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE);
     }
@@ -122,23 +123,22 @@
         assertThat(e.getMessage()).contains(Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE);
     }
 
-    @Test
-    public void testSendScriptExecutionErrors() throws Exception {
-        Exception e = expectThrows(SecurityException.class,
-                () -> mCarTelemetryManager.sendScriptExecutionErrors());
-
-        assertThat(e.getMessage()).contains(Car.PERMISSION_USE_CAR_TELEMETRY_SERVICE);
-    }
-
     private class FakeCarTelemetryResultsListener implements
             CarTelemetryManager.CarTelemetryResultsListener {
         @Override
-        public void onResult(@NonNull ManifestKey key, @NonNull byte[] result) {
+        public void onResult(@NonNull MetricsConfigKey key, @NonNull byte[] result) {
         }
 
         @Override
-        public void onError(@NonNull byte[] error) {
+        public void onError(@NonNull MetricsConfigKey key, @NonNull byte[] error) {
+        }
+
+        @Override
+        public void onAddMetricsConfigStatus(@NonNull MetricsConfigKey key, int statusCode) {
+        }
+
+        @Override
+        public void onRemoveMetricsConfigStatus(@NonNull MetricsConfigKey key, boolean success) {
         }
     }
-
 }
diff --git a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
index af573f7..ea527fa 100644
--- a/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
+++ b/tests/EmbeddedKitchenSinkApp/AndroidManifest.xml
@@ -50,6 +50,8 @@
     <uses-permission android:name="android.car.permission.READ_CAR_STEERING"/>
     <uses-permission android:name="android.car.permission.STORAGE_MONITORING"/>
     <uses-permission android:name="android.car.permission.CAR_DYNAMICS_STATE"/>
+    <!-- use for CarServiceTest -->
+    <uses-permission android:name="android.car.permission.USE_CAR_TELEMETRY_SERVICE"/>
     <!-- Allow querying and writing to any property -->
     <uses-permission android:name="android.car.permission.CAR_ENERGY_PORTS" />
     <uses-permission android:name="android.car.permission.PERMISSION_CONTROL_ENERGY_PORTS" />
diff --git a/tests/carservice_test/src/com/android/car/CarTelemetryManagerTest.java b/tests/carservice_test/src/com/android/car/CarTelemetryManagerTest.java
new file mode 100644
index 0000000..88302de
--- /dev/null
+++ b/tests/carservice_test/src/com/android/car/CarTelemetryManagerTest.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2017 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;
+
+import static android.car.telemetry.CarTelemetryManager.ERROR_METRICS_CONFIG_ALREADY_EXISTS;
+import static android.car.telemetry.CarTelemetryManager.ERROR_METRICS_CONFIG_NONE;
+import static android.car.telemetry.CarTelemetryManager.ERROR_METRICS_CONFIG_PARSE_FAILED;
+import static android.car.telemetry.CarTelemetryManager.ERROR_METRICS_CONFIG_VERSION_TOO_OLD;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assume.assumeTrue;
+
+import android.annotation.NonNull;
+import android.car.Car;
+import android.car.telemetry.CarTelemetryManager;
+import android.car.telemetry.MetricsConfigKey;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.MediumTest;
+
+import com.android.car.telemetry.CarTelemetryService;
+import com.android.car.telemetry.TelemetryProto;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+
+/** Test the public entry points for the CarTelemetryManager. */
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class CarTelemetryManagerTest extends MockedCarTestBase {
+    private static final long TIMEOUT_MS = 5_000L;
+    private static final String TAG = CarTelemetryManagerTest.class.getSimpleName();
+    private static final byte[] INVALID_METRICS_CONFIG = "bad config".getBytes();
+    private static final Executor DIRECT_EXECUTOR = Runnable::run;
+    private static final MetricsConfigKey KEY_V1 = new MetricsConfigKey("my_metrics_config", 1);
+    private static final MetricsConfigKey KEY_V2 = new MetricsConfigKey("my_metrics_config", 2);
+    private static final TelemetryProto.MetricsConfig METRICS_CONFIG_V1 =
+            TelemetryProto.MetricsConfig.newBuilder()
+                    .setName("my_metrics_config").setVersion(1).setScript("no-op").build();
+    private static final TelemetryProto.MetricsConfig METRICS_CONFIG_V2 =
+            METRICS_CONFIG_V1.toBuilder().setVersion(2).build();
+
+    private final FakeCarTelemetryResultsListener mListener = new FakeCarTelemetryResultsListener();
+    private final HandlerThread mTelemetryThread =
+            CarServiceUtils.getHandlerThread(CarTelemetryService.class.getSimpleName());
+    private final Handler mHandler = new Handler(mTelemetryThread.getLooper());
+
+    private CarTelemetryManager mCarTelemetryManager;
+    private CountDownLatch mIdleHandlerLatch = new CountDownLatch(1);
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        assumeTrue(getCar().isFeatureEnabled(Car.CAR_TELEMETRY_SERVICE));
+
+        mTelemetryThread.getLooper().getQueue().addIdleHandler(() -> {
+            mIdleHandlerLatch.countDown();
+            return true;
+        });
+
+        Log.i(TAG, "attempting to get CAR_TELEMETRY_SERVICE");
+        mCarTelemetryManager = (CarTelemetryManager) getCar().getCarManager(
+                Car.CAR_TELEMETRY_SERVICE);
+        mCarTelemetryManager.setListener(DIRECT_EXECUTOR, mListener);
+    }
+
+    @Test
+    public void testSetClearListener() {
+        mCarTelemetryManager.clearListener();
+        mCarTelemetryManager.setListener(DIRECT_EXECUTOR, mListener);
+
+        // setListener multiple times should fail
+        assertThrows(IllegalStateException.class,
+                () -> mCarTelemetryManager.setListener(DIRECT_EXECUTOR, mListener));
+    }
+
+    @Test
+    public void testAddMetricsConfig() throws Exception {
+        // invalid config, should fail
+        mCarTelemetryManager.addMetricsConfig(KEY_V1, INVALID_METRICS_CONFIG);
+        waitForHandlerThreadToFinish();
+        assertThat(mListener.getAddConfigStatus(KEY_V1)).isEqualTo(
+                ERROR_METRICS_CONFIG_PARSE_FAILED);
+
+        // new valid config, should succeed
+        mCarTelemetryManager.addMetricsConfig(KEY_V1, METRICS_CONFIG_V1.toByteArray());
+        waitForHandlerThreadToFinish();
+        assertThat(mListener.getAddConfigStatus(KEY_V1)).isEqualTo(ERROR_METRICS_CONFIG_NONE);
+
+        // duplicate config, should fail
+        mCarTelemetryManager.addMetricsConfig(KEY_V1, METRICS_CONFIG_V1.toByteArray());
+        waitForHandlerThreadToFinish();
+        assertThat(mListener.getAddConfigStatus(KEY_V1)).isEqualTo(
+                ERROR_METRICS_CONFIG_ALREADY_EXISTS);
+
+        // newer version of the config should replace older version
+        mCarTelemetryManager.addMetricsConfig(KEY_V2, METRICS_CONFIG_V2.toByteArray());
+        waitForHandlerThreadToFinish();
+        assertThat(mListener.getAddConfigStatus(KEY_V2)).isEqualTo(ERROR_METRICS_CONFIG_NONE);
+
+        // older version of the config should not be accepted
+        mCarTelemetryManager.addMetricsConfig(KEY_V1, METRICS_CONFIG_V1.toByteArray());
+        waitForHandlerThreadToFinish();
+        assertThat(mListener.getAddConfigStatus(KEY_V1)).isEqualTo(
+                ERROR_METRICS_CONFIG_VERSION_TOO_OLD);
+    }
+
+    @Test
+    public void testRemoveMetricsConfig() throws Exception {
+        mCarTelemetryManager.removeMetricsConfig(KEY_V1);
+        waitForHandlerThreadToFinish();
+        assertThat(mListener.getRemoveConfigStatus(KEY_V1)).isFalse();
+
+        mCarTelemetryManager.addMetricsConfig(KEY_V1, METRICS_CONFIG_V1.toByteArray());
+        waitForHandlerThreadToFinish();
+        mCarTelemetryManager.removeMetricsConfig(KEY_V1);
+        waitForHandlerThreadToFinish();
+        assertThat(mListener.getRemoveConfigStatus(KEY_V1)).isTrue();
+    }
+
+    private void waitForHandlerThreadToFinish() throws Exception {
+        assertWithMessage("handler not idle in %sms", TIMEOUT_MS)
+                .that(mIdleHandlerLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
+        mIdleHandlerLatch = new CountDownLatch(1); // reset idle handler condition
+        mHandler.runWithScissors(() -> {
+        }, TIMEOUT_MS);
+    }
+
+
+    private static final class FakeCarTelemetryResultsListener
+            implements CarTelemetryManager.CarTelemetryResultsListener {
+
+        private Map<MetricsConfigKey, Boolean> mRemoveConfigStatusMap = new ArrayMap<>();
+        private Map<MetricsConfigKey, Integer> mAddConfigStatusMap = new ArrayMap<>();
+
+        @Override
+        public void onResult(@NonNull MetricsConfigKey key, @NonNull byte[] result) {
+        }
+
+        @Override
+        public void onError(@NonNull MetricsConfigKey key, @NonNull byte[] error) {
+        }
+
+        @Override
+        public void onAddMetricsConfigStatus(@NonNull MetricsConfigKey key, int statusCode) {
+            mAddConfigStatusMap.put(key, statusCode);
+        }
+
+        @Override
+        public void onRemoveMetricsConfigStatus(@NonNull MetricsConfigKey key, boolean success) {
+            mRemoveConfigStatusMap.put(key, success);
+        }
+
+        public int getAddConfigStatus(MetricsConfigKey key) {
+            return mAddConfigStatusMap.getOrDefault(key, -100);
+        }
+
+        public boolean getRemoveConfigStatus(MetricsConfigKey key) {
+            return mRemoveConfigStatusMap.getOrDefault(key, false);
+        }
+    }
+}
diff --git a/tests/carservice_unit_test/src/com/android/car/telemetry/CarTelemetryServiceTest.java b/tests/carservice_unit_test/src/com/android/car/telemetry/CarTelemetryServiceTest.java
index e0e8568..39389d2 100644
--- a/tests/carservice_unit_test/src/com/android/car/telemetry/CarTelemetryServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/telemetry/CarTelemetryServiceTest.java
@@ -17,12 +17,18 @@
 package com.android.car.telemetry;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.car.telemetry.CarTelemetryManager;
-import android.car.telemetry.ManifestKey;
+import android.car.telemetry.ICarTelemetryServiceListener;
+import android.car.telemetry.MetricsConfigKey;
 import android.content.Context;
+import android.os.Handler;
+import android.os.PersistableBundle;
 
 import androidx.test.filters.SmallTest;
 
@@ -41,17 +47,29 @@
 
 import java.io.File;
 import java.nio.file.Files;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 @RunWith(MockitoJUnitRunner.class)
 @SmallTest
 public class CarTelemetryServiceTest {
-    private final ManifestKey mManifestKeyV1 = new ManifestKey("Name", 1);
-    private final ManifestKey mManifestKeyV2 = new ManifestKey("Name", 2);
-    private final TelemetryProto.MetricsConfig mMetricsConfig =
-            TelemetryProto.MetricsConfig.newBuilder().setScript("no-op").build();
+    private static final long TIMEOUT_MS = 5_000L;
+    private static final String METRICS_CONFIG_NAME = "my_metrics_config";
+    private static final MetricsConfigKey KEY_V1 = new MetricsConfigKey(METRICS_CONFIG_NAME, 1);
+    private static final MetricsConfigKey KEY_V2 = new MetricsConfigKey(METRICS_CONFIG_NAME, 2);
+    private static final TelemetryProto.MetricsConfig METRICS_CONFIG_V1 =
+            TelemetryProto.MetricsConfig.newBuilder()
+                    .setName(METRICS_CONFIG_NAME).setVersion(1).setScript("no-op").build();
+    private static final TelemetryProto.MetricsConfig METRICS_CONFIG_V2 =
+            TelemetryProto.MetricsConfig.newBuilder()
+                    .setName(METRICS_CONFIG_NAME).setVersion(2).setScript("no-op").build();
 
+    private CountDownLatch mIdleHandlerLatch = new CountDownLatch(1);
     private CarTelemetryService mService;
     private File mTempSystemCarDir;
+    private Handler mTelemetryHandler;
+    private MetricsConfigStore mMetricsConfigStore;
+    private ResultStore mResultStore;
 
     @Rule
     public MockitoRule mMockitoRule = MockitoJUnit.rule();
@@ -60,6 +78,8 @@
     @Mock
     private Context mContext;
     @Mock
+    private ICarTelemetryServiceListener mMockListener;
+    @Mock
     private SystemInterface mMockSystemInterface;
 
     @Before
@@ -71,76 +91,125 @@
         when(mMockSystemInterface.getSystemCarDir()).thenReturn(mTempSystemCarDir);
 
         mService = new CarTelemetryService(mContext, mMockCarPropertyService);
+        mService.init();
+        mService.setListener(mMockListener);
+
+        mTelemetryHandler = mService.getTelemetryHandler();
+        mTelemetryHandler.getLooper().getQueue().addIdleHandler(() -> {
+            mIdleHandlerLatch.countDown();
+            return true;
+        });
+        waitForHandlerThreadToFinish();
+
+        mMetricsConfigStore = mService.getMetricsConfigStore();
+        mResultStore = mService.getResultStore();
     }
 
     @Test
-    public void testAddManifest_newManifest_shouldSucceed() {
-        int result = mService.addManifest(mManifestKeyV1, mMetricsConfig.toByteArray());
+    public void testAddMetricsConfig_newMetricsConfig_shouldSucceed() throws Exception {
+        mService.addMetricsConfig(KEY_V1, METRICS_CONFIG_V1.toByteArray());
 
-        assertThat(result).isEqualTo(CarTelemetryManager.ERROR_NONE);
+        waitForHandlerThreadToFinish();
+        verify(mMockListener).onAddMetricsConfigStatus(
+                eq(KEY_V1), eq(CarTelemetryManager.ERROR_METRICS_CONFIG_NONE));
     }
 
     @Test
-    public void testAddManifest_duplicateManifest_shouldFail() {
-        mService.addManifest(mManifestKeyV1, mMetricsConfig.toByteArray());
+    public void testAddMetricsConfig_duplicateMetricsConfig_shouldFail() throws Exception {
+        mService.addMetricsConfig(KEY_V1, METRICS_CONFIG_V1.toByteArray());
+        waitForHandlerThreadToFinish();
+        verify(mMockListener).onAddMetricsConfigStatus(
+                eq(KEY_V1), eq(CarTelemetryManager.ERROR_METRICS_CONFIG_NONE));
 
-        int result = mService.addManifest(mManifestKeyV1, mMetricsConfig.toByteArray());
+        mService.addMetricsConfig(KEY_V1, METRICS_CONFIG_V1.toByteArray());
 
-        assertThat(result).isEqualTo(CarTelemetryManager.ERROR_SAME_MANIFEST_EXISTS);
+        waitForHandlerThreadToFinish();
+        verify(mMockListener).onAddMetricsConfigStatus(
+                eq(KEY_V1), eq(CarTelemetryManager.ERROR_METRICS_CONFIG_ALREADY_EXISTS));
     }
 
     @Test
-    public void testAddManifest_invalidManifest_shouldFail() {
-        int result = mService.addManifest(mManifestKeyV1, "bad manifest".getBytes());
+    public void testAddMetricsConfig_invalidMetricsConfig_shouldFail() throws Exception {
+        mService.addMetricsConfig(KEY_V1, "bad config".getBytes());
 
-        assertThat(result).isEqualTo(CarTelemetryManager.ERROR_PARSE_MANIFEST_FAILED);
+        waitForHandlerThreadToFinish();
+        verify(mMockListener).onAddMetricsConfigStatus(
+                eq(KEY_V1), eq(CarTelemetryManager.ERROR_METRICS_CONFIG_PARSE_FAILED));
     }
 
     @Test
-    public void testAddManifest_olderManifest_shouldFail() {
-        mService.addManifest(mManifestKeyV2, mMetricsConfig.toByteArray());
+    public void testAddMetricsConfig_olderMetricsConfig_shouldFail() throws Exception {
+        mService.addMetricsConfig(KEY_V2, METRICS_CONFIG_V2.toByteArray());
+        waitForHandlerThreadToFinish();
+        verify(mMockListener).onAddMetricsConfigStatus(
+                eq(KEY_V2), eq(CarTelemetryManager.ERROR_METRICS_CONFIG_NONE));
 
-        int result = mService.addManifest(mManifestKeyV1, mMetricsConfig.toByteArray());
+        mService.addMetricsConfig(KEY_V1, METRICS_CONFIG_V1.toByteArray());
 
-        assertThat(result).isEqualTo(CarTelemetryManager.ERROR_NEWER_MANIFEST_EXISTS);
+        waitForHandlerThreadToFinish();
+        verify(mMockListener).onAddMetricsConfigStatus(
+                eq(KEY_V1), eq(CarTelemetryManager.ERROR_METRICS_CONFIG_VERSION_TOO_OLD));
     }
 
     @Test
-    public void testAddManifest_newerManifest_shouldReplace() {
-        mService.addManifest(mManifestKeyV1, mMetricsConfig.toByteArray());
+    public void testAddMetricsConfig_newerMetricsConfig_shouldReplaceAndDeleteOldResult()
+            throws Exception {
+        mService.addMetricsConfig(KEY_V1, METRICS_CONFIG_V1.toByteArray());
+        mResultStore.putInterimResult(KEY_V1.getName(), new PersistableBundle());
 
-        int result = mService.addManifest(mManifestKeyV2, mMetricsConfig.toByteArray());
+        mService.addMetricsConfig(KEY_V2, METRICS_CONFIG_V2.toByteArray());
 
-        assertThat(result).isEqualTo(CarTelemetryManager.ERROR_NONE);
+        waitForHandlerThreadToFinish();
+        verify(mMockListener).onAddMetricsConfigStatus(
+                eq(KEY_V2), eq(CarTelemetryManager.ERROR_METRICS_CONFIG_NONE));
+        assertThat(mMetricsConfigStore.getActiveMetricsConfigs())
+                .containsExactly(METRICS_CONFIG_V2);
+        assertThat(mResultStore.getInterimResult(KEY_V1.getName())).isNull();
     }
 
     @Test
-    public void testRemoveManifest_manifestExists_shouldSucceed() {
-        mService.addManifest(mManifestKeyV1, mMetricsConfig.toByteArray());
+    public void testRemoveMetricsConfig_configExists_shouldDeleteScriptResult() throws Exception {
+        mService.addMetricsConfig(KEY_V1, METRICS_CONFIG_V1.toByteArray());
+        mResultStore.putInterimResult(KEY_V1.getName(), new PersistableBundle());
 
-        boolean result = mService.removeManifest(mManifestKeyV1);
+        mService.removeMetricsConfig(KEY_V1);
 
-        assertThat(result).isTrue();
+        waitForHandlerThreadToFinish();
+        verify(mMockListener).onRemoveMetricsConfigStatus(eq(KEY_V1), eq(true));
+        assertThat(mMetricsConfigStore.getActiveMetricsConfigs()).isEmpty();
+        assertThat(mResultStore.getInterimResult(KEY_V1.getName())).isNull();
     }
 
     @Test
-    public void testRemoveManifest_manifestDoesNotExist_shouldFail() {
-        boolean result = mService.removeManifest(mManifestKeyV1);
+    public void testRemoveMetricsConfig_configDoesNotExist_shouldFail() throws Exception {
+        mService.removeMetricsConfig(KEY_V1);
 
-        assertThat(result).isFalse();
+        waitForHandlerThreadToFinish();
+        verify(mMockListener).onRemoveMetricsConfigStatus(eq(KEY_V1), eq(false));
     }
 
     @Test
-    public void testRemoveAllManifests_shouldSucceed() {
-        mService.addManifest(mManifestKeyV1, mMetricsConfig.toByteArray());
-        mService.addManifest(mManifestKeyV2, mMetricsConfig.toByteArray());
+    public void testRemoveAllMetricsConfigs_shouldRemoveConfigsAndResults() throws Exception {
+        MetricsConfigKey key = new MetricsConfigKey("test config", 2);
+        TelemetryProto.MetricsConfig config =
+                TelemetryProto.MetricsConfig.newBuilder().setName(key.getName()).build();
+        mService.addMetricsConfig(key, config.toByteArray());
+        mService.addMetricsConfig(KEY_V1, METRICS_CONFIG_V1.toByteArray());
+        mResultStore.putInterimResult(KEY_V1.getName(), new PersistableBundle());
+        mResultStore.putFinalResult(key.getName(), new PersistableBundle());
 
-        mService.removeAllManifests();
+        mService.removeAllMetricsConfigs();
 
-        // verify that the manifests are cleared by adding them again, should succeed
-        int result = mService.addManifest(mManifestKeyV1, mMetricsConfig.toByteArray());
-        assertThat(result).isEqualTo(CarTelemetryManager.ERROR_NONE);
-        result = mService.addManifest(mManifestKeyV2, mMetricsConfig.toByteArray());
-        assertThat(result).isEqualTo(CarTelemetryManager.ERROR_NONE);
+        waitForHandlerThreadToFinish();
+        assertThat(mMetricsConfigStore.getActiveMetricsConfigs()).isEmpty();
+        assertThat(mResultStore.getInterimResult(KEY_V1.getName())).isNull();
+        assertThat(mResultStore.getFinalResult(key.getName(), /* deleteResult = */ false)).isNull();
+    }
+
+    private void waitForHandlerThreadToFinish() throws Exception {
+        assertWithMessage("handler not idle in %sms", TIMEOUT_MS)
+                .that(mIdleHandlerLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)).isTrue();
+        mIdleHandlerLatch = new CountDownLatch(1); // reset idle handler condition
+        mTelemetryHandler.runWithScissors(() -> { }, TIMEOUT_MS);
     }
 }
diff --git a/tests/carservice_unit_test/src/com/android/car/telemetry/MetricsConfigStoreTest.java b/tests/carservice_unit_test/src/com/android/car/telemetry/MetricsConfigStoreTest.java
index 8c546a0..daf83d8 100644
--- a/tests/carservice_unit_test/src/com/android/car/telemetry/MetricsConfigStoreTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/telemetry/MetricsConfigStoreTest.java
@@ -18,6 +18,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.car.telemetry.CarTelemetryManager;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -53,6 +55,7 @@
     public void testRetrieveActiveMetricsConfigs_shouldSendConfigsToListener() throws Exception {
         writeConfigToDisk(METRICS_CONFIG_FOO);
         writeConfigToDisk(METRICS_CONFIG_BAR);
+        mMetricsConfigStore = new MetricsConfigStore(mTestRootDir); // reload data
 
         List<TelemetryProto.MetricsConfig> result = mMetricsConfigStore.getActiveMetricsConfigs();
 
@@ -61,14 +64,14 @@
 
     @Test
     public void testAddMetricsConfig_shouldWriteConfigToDisk() throws Exception {
-        boolean status = mMetricsConfigStore.addMetricsConfig(METRICS_CONFIG_FOO);
+        int status = mMetricsConfigStore.addMetricsConfig(METRICS_CONFIG_FOO);
 
-        assertThat(status).isTrue();
+        assertThat(status).isEqualTo(CarTelemetryManager.ERROR_METRICS_CONFIG_NONE);
         assertThat(readConfigFromFile(NAME_FOO)).isEqualTo(METRICS_CONFIG_FOO);
     }
 
     @Test
-    public void testDeleteMetricsConfig_whenNoConfig_shouldReturnFalse() throws Exception {
+    public void testDeleteMetricsConfig_whenNoConfig_shouldReturnFalse() {
         boolean status = mMetricsConfigStore.deleteMetricsConfig(NAME_BAR);
 
         assertThat(status).isFalse();
@@ -84,6 +87,16 @@
         assertThat(new File(mTestMetricsConfigDir, NAME_BAR).exists()).isFalse();
     }
 
+    @Test
+    public void testDeleteAllMetricsConfigs_shouldDeleteAll() throws Exception {
+        writeConfigToDisk(METRICS_CONFIG_FOO);
+        writeConfigToDisk(METRICS_CONFIG_BAR);
+
+        mMetricsConfigStore.deleteAllMetricsConfigs();
+
+        assertThat(mTestMetricsConfigDir.listFiles()).isEmpty();
+    }
+
     private void writeConfigToDisk(TelemetryProto.MetricsConfig config) throws Exception {
         File file = new File(mTestMetricsConfigDir, config.getName());
         Files.write(file.toPath(), config.toByteArray());
diff --git a/tests/carservice_unit_test/src/com/android/car/telemetry/ResultStoreTest.java b/tests/carservice_unit_test/src/com/android/car/telemetry/ResultStoreTest.java
index af0281a..ed59675 100644
--- a/tests/carservice_unit_test/src/com/android/car/telemetry/ResultStoreTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/telemetry/ResultStoreTest.java
@@ -19,17 +19,11 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.verify;
-
 import android.os.PersistableBundle;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
 
 import java.io.ByteArrayOutputStream;
@@ -49,12 +43,6 @@
     private File mTestFinalResultDir;
     private ResultStore mResultStore;
 
-    @Mock
-    private ResultStore.FinalResultCallback mMockFinalResultCallback;
-    @Captor
-    private ArgumentCaptor<PersistableBundle> mBundleCaptor;
-
-
     @Before
     public void setUp() throws Exception {
         TEST_INTERIM_BUNDLE.putString("test key", "interim value");
@@ -131,11 +119,9 @@
     public void testGetFinalResult_whenNoData_shouldReceiveNull() throws Exception {
         String metricsConfigName = "my_metrics_config";
 
-        mResultStore.getFinalResult(metricsConfigName, true, mMockFinalResultCallback);
+        PersistableBundle bundle = mResultStore.getFinalResult(metricsConfigName, true);
 
-        verify(mMockFinalResultCallback).onFinalResult(eq(metricsConfigName),
-                mBundleCaptor.capture());
-        assertThat(mBundleCaptor.getValue()).isNull();
+        assertThat(bundle).isNull();
     }
 
     @Test
@@ -144,11 +130,9 @@
         Files.write(new File(mTestFinalResultDir, metricsConfigName).toPath(),
                 "not a bundle".getBytes(StandardCharsets.UTF_8));
 
-        mResultStore.getFinalResult(metricsConfigName, true, mMockFinalResultCallback);
+        PersistableBundle bundle = mResultStore.getFinalResult(metricsConfigName, true);
 
-        verify(mMockFinalResultCallback).onFinalResult(eq(metricsConfigName),
-                mBundleCaptor.capture());
-        assertThat(mBundleCaptor.getValue()).isNull();
+        assertThat(bundle).isNull();
     }
 
     @Test
@@ -156,12 +140,10 @@
         String testFinalFileName = "my_metrics_config";
         writeBundleToFile(mTestFinalResultDir, testFinalFileName, TEST_FINAL_BUNDLE);
 
-        mResultStore.getFinalResult(testFinalFileName, true, mMockFinalResultCallback);
+        PersistableBundle bundle = mResultStore.getFinalResult(testFinalFileName, true);
 
-        verify(mMockFinalResultCallback).onFinalResult(eq(testFinalFileName),
-                mBundleCaptor.capture());
         // should compare value instead of reference
-        assertThat(mBundleCaptor.getValue().toString()).isEqualTo(TEST_FINAL_BUNDLE.toString());
+        assertThat(bundle.toString()).isEqualTo(TEST_FINAL_BUNDLE.toString());
         assertThat(new File(mTestFinalResultDir, testFinalFileName).exists()).isFalse();
     }
 
@@ -170,12 +152,10 @@
         String testFinalFileName = "my_metrics_config";
         writeBundleToFile(mTestFinalResultDir, testFinalFileName, TEST_FINAL_BUNDLE);
 
-        mResultStore.getFinalResult(testFinalFileName, false, mMockFinalResultCallback);
+        PersistableBundle bundle = mResultStore.getFinalResult(testFinalFileName, false);
 
-        verify(mMockFinalResultCallback).onFinalResult(eq(testFinalFileName),
-                mBundleCaptor.capture());
         // should compare value instead of reference
-        assertThat(mBundleCaptor.getValue().toString()).isEqualTo(TEST_FINAL_BUNDLE.toString());
+        assertThat(bundle.toString()).isEqualTo(TEST_FINAL_BUNDLE.toString());
         assertThat(new File(mTestFinalResultDir, testFinalFileName).exists()).isTrue();
     }
 
@@ -236,6 +216,26 @@
         assertThat(new File(mTestFinalResultDir, metricsConfigName).exists()).isTrue();
     }
 
+    @Test
+    public void testDeleteResult_whenInterimResult_shouldDelete() throws Exception {
+        String metricsConfigName = "my_metrics_config";
+        writeBundleToFile(mTestInterimResultDir, metricsConfigName, TEST_INTERIM_BUNDLE);
+
+        mResultStore.deleteResult(metricsConfigName);
+
+        assertThat(new File(mTestInterimResultDir, metricsConfigName).exists()).isFalse();
+    }
+
+    @Test
+    public void testDeleteResult_whenFinalResult_shouldDelete() throws Exception {
+        String metricsConfigName = "my_metrics_config";
+        writeBundleToFile(mTestFinalResultDir, metricsConfigName, TEST_FINAL_BUNDLE);
+
+        mResultStore.deleteResult(metricsConfigName);
+
+        assertThat(new File(mTestFinalResultDir, metricsConfigName).exists()).isFalse();
+    }
+
     private void writeBundleToFile(
             File dir, String fileName, PersistableBundle persistableBundle) throws Exception {
         writeBundleToFile(new File(dir, fileName), persistableBundle);
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 2bc4f07..a672bd5 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
@@ -23,11 +23,14 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.StatsManager;
 import android.os.Looper;
 import android.os.Message;
 import android.os.PersistableBundle;
@@ -93,6 +96,8 @@
     private final FakeSharedPreferences mFakeSharedPref = new FakeSharedPreferences();
     private final FakeHandlerWrapper mFakeHandlerWrapper =
             new FakeHandlerWrapper(Looper.getMainLooper(), FakeHandlerWrapper.Mode.QUEUEING);
+    private Throwable mPublisherFailure;
+
     @Mock private DataSubscriber mMockDataSubscriber;
     @Mock private StatsManagerProxy mStatsManager;
 
@@ -195,6 +200,16 @@
     }
 
     @Test
+    public void testAddDataSubscriber_whenFails_notifiesFailureConsumer() throws Exception {
+        doThrow(new StatsManager.StatsUnavailableException("fail"))
+                .when(mStatsManager).addConfig(anyLong(), any());
+
+        mPublisher.addDataSubscriber(mMockDataSubscriber);
+
+        assertThat(mPublisherFailure).hasMessageThat().contains("Failed to add config");
+    }
+
+    @Test
     public void testRemoveDataSubscriber_removesPeriodicStatsdReportPull() {
         mPublisher.addDataSubscriber(mMockDataSubscriber);
 
@@ -246,7 +261,9 @@
     // TODO(b/189143813): add test cases when connecting to Statsd fails
     // TODO(b/189143813): add test cases for handling config version upgrades
 
-    private void onPublisherFailure(AbstractPublisher publisher, Throwable error) { }
+    private void onPublisherFailure(AbstractPublisher publisher, Throwable error) {
+        mPublisherFailure = error;
+    }
 
     private static void assertThatMessageIsScheduledWithGivenDelay(Message msg, long delayMillis) {
         long expectedTimeMillis = SystemClock.uptimeMillis() + delayMillis;