Merge "softap: Update LOHS API usage"
diff --git a/car-lib/Android.bp b/car-lib/Android.bp
index 73cbc5d..adec1a5 100644
--- a/car-lib/Android.bp
+++ b/car-lib/Android.bp
@@ -311,6 +311,9 @@
         },
     },
     compile_dex: true,
+    dist: {
+        targets: ["dist_files"],
+    }
 }
 
 // Export the api/system-current.txt file.
diff --git a/car-lib/api/system-current.txt b/car-lib/api/system-current.txt
index 6310b72..4388c08 100644
--- a/car-lib/api/system-current.txt
+++ b/car-lib/api/system-current.txt
@@ -234,7 +234,8 @@
   public abstract class InstrumentClusterRenderingService extends android.app.Service {
     ctor public InstrumentClusterRenderingService();
     method @Deprecated @Nullable public android.graphics.Bitmap getBitmap(android.net.Uri);
-    method @Nullable public android.graphics.Bitmap getBitmap(android.net.Uri, int, int);
+    method @Nullable public android.graphics.Bitmap getBitmap(@NonNull android.net.Uri, int, int);
+    method @Nullable public android.graphics.Bitmap getBitmap(@NonNull android.net.Uri, int, int, float);
     method @MainThread @Nullable public abstract android.car.cluster.renderer.NavigationRenderer getNavigationRenderer();
     method @CallSuper public android.os.IBinder onBind(android.content.Intent);
     method @MainThread public void onKeyEvent(@NonNull android.view.KeyEvent);
@@ -816,6 +817,7 @@
     method @RequiresPermission(android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) public int getGroupMinVolume(int, int);
     method @RequiresPermission(android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) public int getGroupVolume(int);
     method @RequiresPermission(android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) public int getGroupVolume(int, int);
+    method @NonNull @RequiresPermission(android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) public java.util.List<android.media.AudioDeviceInfo> getInputDevicesForZoneId(int);
     method @Nullable @RequiresPermission(android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) public android.media.AudioDeviceInfo getOutputDeviceForUsage(int, int);
     method @NonNull @RequiresPermission(android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) public int[] getUsagesForVolumeGroupId(int);
     method @NonNull @RequiresPermission(android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) public int[] getUsagesForVolumeGroupId(int, int);
@@ -928,6 +930,14 @@
 
 }
 
+package android.car.settings {
+
+  public static final class CarSettings.Secure {
+    field public static final String KEY_AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL = "android.car.KEY_AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL";
+  }
+
+}
+
 package android.car.storagemonitoring {
 
   public final class CarStorageMonitoringManager {
@@ -1094,21 +1104,6 @@
 
 }
 
-package android.car.user {
-
-  public final class CarUserManager {
-    method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public int createDriver(@NonNull String, boolean);
-    method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public int createPassenger(@NonNull String, int);
-    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<java.lang.Integer> getAllDrivers();
-    method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<java.lang.Integer> getPassengers(int);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean startPassenger(int, int);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean stopPassenger(int);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean switchDriver(int);
-    field public static final int INVALID_USER_ID = -10000; // 0xffffd8f0
-  }
-
-}
-
 package android.car.vms {
 
   public final class VmsAssociatedLayer implements android.os.Parcelable {
diff --git a/car-lib/src/android/car/Car.java b/car-lib/src/android/car/Car.java
index f31581b..4a8a76b 100644
--- a/car-lib/src/android/car/Car.java
+++ b/car-lib/src/android/car/Car.java
@@ -158,6 +158,7 @@
     /**
      * @hide
      */
+    @OptionalFeature
     @SystemApi
     public static final String DIAGNOSTIC_SERVICE = "diagnostic";
 
diff --git a/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java b/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java
index 3683b19..582a872 100644
--- a/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java
+++ b/car-lib/src/android/car/cluster/renderer/InstrumentClusterRenderingService.java
@@ -96,6 +96,7 @@
 
     private static final String BITMAP_QUERY_WIDTH = "w";
     private static final String BITMAP_QUERY_HEIGHT = "h";
+    private static final String BITMAP_QUERY_OFFLANESALPHA = "offLanesAlpha";
 
     private final Handler mUiHandler = new Handler(Looper.getMainLooper());
 
@@ -584,6 +585,8 @@
      * This is a costly operation. Returned bitmaps should be cached and fetching should be done on
      * a secondary thread.
      *
+     * @param uri The URI of the bitmap
+     *
      * @throws IllegalArgumentException if {@code uri} does not have width and height query params.
      *
      * @deprecated Replaced by {@link #getBitmap(Uri, int, int)}.
@@ -639,8 +642,16 @@
     }
 
     /**
+     * See {@link #getBitmap(Uri, int, int, float)}
+     */
+    @Nullable
+    public Bitmap getBitmap(@NonNull Uri uri, int width, int height) {
+        return getBitmap(uri, width, height, 1f);
+    }
+
+    /**
      * Fetches a bitmap from the navigation context owner (application holding navigation focus)
-     * of the given width and height. The fetched bitmaps are cached.
+     * of the given width and height and off lane opacity. The fetched bitmaps are cached.
      * It returns null if:
      * <ul>
      * <li>there is no navigation context owner
@@ -649,13 +660,20 @@
      * </ul>
      * This is a costly operation. Returned bitmaps should be fetched on a secondary thread.
      *
-     * @throws IllegalArgumentException if {@code width} or {@code height} is not greater than 0.
+     * @param uri           The URI of the bitmap
+     * @param width         Requested width
+     * @param height        Requested height
+     * @param offLanesAlpha Opacity value of the off-lane images. Only used for lane guidance images
+     * @throws IllegalArgumentException if width, height <= 0, or 0 > offLanesAlpha > 1
      */
     @Nullable
-    public Bitmap getBitmap(Uri uri, int width, int height) {
+    public Bitmap getBitmap(@NonNull Uri uri, int width, int height, float offLanesAlpha) {
         if (width <= 0 || height <= 0) {
             throw new IllegalArgumentException("Width and height must be > 0");
         }
+        if (offLanesAlpha < 0 || offLanesAlpha > 1) {
+            throw new IllegalArgumentException("offLanesAlpha must be between [0, 1]");
+        }
 
         try {
             ContextOwner contextOwner = getNavigationContextOwner();
@@ -667,6 +685,7 @@
             uri = uri.buildUpon()
                     .appendQueryParameter(BITMAP_QUERY_WIDTH, String.valueOf(width))
                     .appendQueryParameter(BITMAP_QUERY_HEIGHT, String.valueOf(height))
+                    .appendQueryParameter(BITMAP_QUERY_OFFLANESALPHA, String.valueOf(offLanesAlpha))
                     .build();
 
             String host = uri.getHost();
diff --git a/car-lib/src/android/car/media/CarAudioManager.java b/car-lib/src/android/car/media/CarAudioManager.java
index 61cdd2f..16eaa07 100644
--- a/car-lib/src/android/car/media/CarAudioManager.java
+++ b/car-lib/src/android/car/media/CarAudioManager.java
@@ -24,8 +24,10 @@
 import android.car.CarLibLog;
 import android.car.CarManagerBase;
 import android.media.AudioAttributes;
+import android.media.AudioDeviceAddress;
 import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
+import android.media.AudioManager.AudioDeviceRole;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -34,7 +36,10 @@
 import android.view.DisplayAddress;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 /**
  * APIs for handling audio in a car.
@@ -452,7 +457,7 @@
             }
             return zoneIdList;
         } catch (RemoteException e) {
-            return handleRemoteExceptionFromCarService(e, new ArrayList<>());
+            return handleRemoteExceptionFromCarService(e, Collections.emptyList());
         }
     }
 
@@ -600,6 +605,24 @@
         }
     }
 
+    /**
+     * Gets the input devices for an audio zone
+     *
+     * @return list of input devices
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
+    public @NonNull List<AudioDeviceInfo> getInputDevicesForZoneId(int zoneId) {
+        try {
+            return convertInputDeviceAddressesToDeviceInfos(
+                    mService.getInputDevicesAddressesForZoneId(zoneId),
+                    AudioManager.GET_DEVICES_INPUTS);
+        } catch (RemoteException e) {
+            return handleRemoteExceptionFromCarService(e, new ArrayList<>());
+        }
+    }
+
     /** @hide */
     @Override
     public void onCarDisconnected() {
@@ -641,6 +664,25 @@
         mCarVolumeCallbacks.remove(callback);
     }
 
+    private List<AudioDeviceInfo> convertInputDeviceAddressesToDeviceInfos(
+            List<AudioDeviceAddress> addresses, @AudioDeviceRole int flag) {
+        int addressesSize = addresses.size();
+        Set<String> deviceAddressMap = new HashSet<>(addressesSize);
+        for (int i = 0; i < addressesSize; ++i) {
+            AudioDeviceAddress deviceAddress = addresses.get(i);
+            deviceAddressMap.add(deviceAddress.getAddress());
+        }
+        List<AudioDeviceInfo> deviceInfoList = new ArrayList<>(addresses.size());
+        AudioDeviceInfo[] inputDevices = mAudioManager.getDevices(flag);
+        for (int i = 0; i < inputDevices.length; ++i) {
+            AudioDeviceInfo info = inputDevices[i];
+            if (info.isSource() && deviceAddressMap.contains(info.getAddress())) {
+                deviceInfoList.add(info);
+            }
+        }
+        return deviceInfoList;
+    }
+
     /**
      * Callback interface to receive volume change events in a car.
      * Extend this class and register it with {@link #registerCarVolumeCallback(CarVolumeCallback)}
diff --git a/car-lib/src/android/car/media/ICarAudio.aidl b/car-lib/src/android/car/media/ICarAudio.aidl
index 22da23a..bc5c902 100644
--- a/car-lib/src/android/car/media/ICarAudio.aidl
+++ b/car-lib/src/android/car/media/ICarAudio.aidl
@@ -17,6 +17,7 @@
 package android.car.media;
 
 import android.car.media.CarAudioPatchHandle;
+import android.media.AudioDeviceAddress;
 /**
  * Binder interface for {@link android.car.media.CarAudioManager}.
  * Check {@link android.car.media.CarAudioManager} APIs for expected behavior of each call.
@@ -51,6 +52,7 @@
 
     String getOutputDeviceAddressForUsage(int zoneId, int usage);
 
+    List<AudioDeviceAddress> getInputDevicesAddressesForZoneId(int zoneId);
     /**
      * IBinder is ICarVolumeCallback but passed as IBinder due to aidl hidden.
      */
diff --git a/car-lib/src/android/car/settings/CarSettings.java b/car-lib/src/android/car/settings/CarSettings.java
index be89bdc..ca9c6ac 100644
--- a/car-lib/src/android/car/settings/CarSettings.java
+++ b/car-lib/src/android/car/settings/CarSettings.java
@@ -16,6 +16,8 @@
 
 package android.car.settings;
 
+import android.annotation.SystemApi;
+
 /**
  * System level car related settings.
  */
@@ -97,7 +99,29 @@
     /**
      * @hide
      */
+    @SystemApi
     public static final class Secure {
+        private Secure() {}
+
+        /**
+         * Key to indicate whether audio focus requests for
+         * {@link android.hardware.automotive.audiocontrol.V1_0.ContextNumber.NAVIGATION} should
+         * be rejected if focus is currently held by
+         * {@link android.hardware.automotive.audiocontrol.V1_0.ContextNumber.CALL}.
+         * <p>The value is a boolean (1 or 0) where:
+         * <ul>
+         * <li>1 indicates {@code NAVIGATION} should be rejected when a {@code CALL} is in progress.
+         * <li>0 indicates {@code NAVIGATION} and {@code CALL} should be allowed to hold focus
+         * concurrently.
+         * </ul>
+         *
+         * <p>Recommended {@code false} as default value.
+         *
+         * @hide
+         */
+        @SystemApi
+        public static final String KEY_AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL =
+                "android.car.KEY_AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL";
 
         /**
          * Key for a list of devices to automatically connect on Bluetooth A2DP Sink profile
diff --git a/car-lib/src/android/car/user/CarUserManager.java b/car-lib/src/android/car/user/CarUserManager.java
index 6fa2592..94a8b33 100644
--- a/car-lib/src/android/car/user/CarUserManager.java
+++ b/car-lib/src/android/car/user/CarUserManager.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
 import android.annotation.UserIdInt;
 import android.car.Car;
 import android.car.CarManagerBase;
@@ -40,7 +39,6 @@
  *
  * @hide
  */
-@SystemApi
 public final class CarUserManager extends CarManagerBase {
 
     /* User id representing invalid user */
@@ -49,7 +47,6 @@
     private static final String TAG = CarUserManager.class.getSimpleName();
     private final ICarUserService mService;
 
-    /** @hide */
     @VisibleForTesting
     public CarUserManager(Car car, @NonNull IBinder service) {
         super(car);
@@ -63,10 +60,7 @@
      * @param admin Whether the created driver will be an admin.
      * @return user id of the created driver, or {@code INVALID_USER_ID} if the driver could
      *         not be created.
-     *
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     @Nullable
     public int createDriver(@NonNull String name, boolean admin) {
@@ -85,10 +79,7 @@
      * @param driverId User id of the driver under whom a passenger is created.
      * @return user id of the created passenger, or {@code INVALID_USER_ID} if the passenger
      *         could not be created.
-     *
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     @Nullable
     public int createPassenger(@NonNull String name, @UserIdInt int driverId) {
@@ -105,10 +96,7 @@
      *
      * @param driverId User id of the driver to switch to.
      * @return {@code true} if user switching succeeds, or {@code false} if it fails.
-     *
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     public boolean switchDriver(@UserIdInt int driverId) {
         try {
@@ -122,10 +110,7 @@
      * Returns all drivers who can occupy the driving zone. Guest users are included in the list.
      *
      * @return the list of user ids who can be a driver on the device.
-     *
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     @NonNull
     public List<Integer> getAllDrivers() {
@@ -141,10 +126,7 @@
      *
      * @param driverId User id of a driver.
      * @return the list of user ids who are passengers under the given driver.
-     *
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     @NonNull
     public List<Integer> getPassengers(@UserIdInt int driverId) {
@@ -162,10 +144,7 @@
      * @param zoneId Zone id to which the passenger is assigned.
      * @return {@code true} if the user is successfully started or the user is already running.
      *         Otherwise, {@code false}.
-     *
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     public boolean startPassenger(@UserIdInt int passengerId, int zoneId) {
         try {
@@ -180,10 +159,7 @@
      *
      * @param passengerId User id of the passenger to be stopped.
      * @return {@code true} if successfully stopped, or {@code false} if failed.
-     *
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     public boolean stopPassenger(@UserIdInt int passengerId) {
         try {
@@ -193,7 +169,6 @@
         }
     }
 
-    /** @hide */
     @Override
     public void onCarDisconnected() {
         // nothing to do
diff --git a/car_product/build/car.mk b/car_product/build/car.mk
index 4814f9a..4a76b5c 100644
--- a/car_product/build/car.mk
+++ b/car_product/build/car.mk
@@ -101,6 +101,7 @@
     libcar-framework-service-jni \
 
 # System Server components
+# Order is important: if X depends on Y, then Y should precede X on the list.
 PRODUCT_SYSTEM_SERVER_JARS += car-frameworks-service
 
 # Boot animation
@@ -184,8 +185,6 @@
     zh_CN zh_HK zh_TW \
     zu_ZA
 
-# should add to BOOT_JARS only once
-ifeq (,$(INCLUDED_ANDROID_CAR_TO_PRODUCT_BOOT_JARS))
 PRODUCT_BOOT_JARS += \
     android.car
 
@@ -198,9 +197,6 @@
 PRODUCT_HIDDENAPI_STUBS_TEST := \
     android.car-test-stubs-dex
 
-INCLUDED_ANDROID_CAR_TO_PRODUCT_BOOT_JARS := yes
-endif
-
 # Disable Prime Shader Cache in SurfaceFlinger to make it available faster
 PRODUCT_PROPERTY_OVERRIDES += \
     service.sf.prime_shader_cache=0
diff --git a/computepipe/aidl/android/automotive/computepipe/registry/IClientInfo.aidl b/computepipe/aidl/android/automotive/computepipe/registry/IClientInfo.aidl
index d01ef0a..1dd2010 100644
--- a/computepipe/aidl/android/automotive/computepipe/registry/IClientInfo.aidl
+++ b/computepipe/aidl/android/automotive/computepipe/registry/IClientInfo.aidl
@@ -18,7 +18,7 @@
 @VintfStability
 interface IClientInfo {
     /**
-     * Retrieve the ID assigned to the client
+     * Retrieve the name of the client.
      */
-    int getClientId();
+     @utf8InCpp String getClientName();
 }
diff --git a/computepipe/example/FaceTracker.h b/computepipe/example/FaceTracker.h
index ea6ba8b..60fd473 100644
--- a/computepipe/example/FaceTracker.h
+++ b/computepipe/example/FaceTracker.h
@@ -57,9 +57,9 @@
 
 class ClientInfo : public BnClientInfo {
   public:
-    ndk::ScopedAStatus getClientId(int32_t* _aidl_return) {
+    ndk::ScopedAStatus getClientName(std::string* _aidl_return) override {
         if (_aidl_return) {
-            *_aidl_return = 0;
+            *_aidl_return = "FaceTrackerClient";
             return ndk::ScopedAStatus::ok();
         }
         return ndk::ScopedAStatus::fromExceptionCode(EX_TRANSACTION_FAILED);
diff --git a/computepipe/router/1.0/PipeClient.cpp b/computepipe/router/1.0/PipeClient.cpp
index 817782a..b7e299d 100644
--- a/computepipe/router/1.0/PipeClient.cpp
+++ b/computepipe/router/1.0/PipeClient.cpp
@@ -33,14 +33,13 @@
       mClientInfo(info) {
 }
 
-uint32_t PipeClient::getClientId() {
+std::string PipeClient::getClientName() {
     if (mClientInfo == nullptr) {
         return 0;
     }
-    int id = 0;
-    auto status = mClientInfo->getClientId(&id);
-    uint32_t res = (status.isOk() && id > 0) ? id : 0;
-    return res;
+    std::string name;
+    auto status = mClientInfo->getClientName(&name);
+    return (status.isOk()) ? name : "";
 }
 
 bool PipeClient::startClientMonitor() {
diff --git a/computepipe/router/1.0/include/PipeClient.h b/computepipe/router/1.0/include/PipeClient.h
index 03fc8f3..32730f4 100644
--- a/computepipe/router/1.0/include/PipeClient.h
+++ b/computepipe/router/1.0/include/PipeClient.h
@@ -44,7 +44,7 @@
     explicit PipeClient(
         const std::shared_ptr<aidl::android::automotive::computepipe::registry::IClientInfo>& info);
     bool startClientMonitor() override;
-    uint32_t getClientId() override;
+    std::string getClientName() override;
     bool isAlive() override;
     ~PipeClient();
 
diff --git a/computepipe/router/include/ClientHandle.h b/computepipe/router/include/ClientHandle.h
index 7d026d2..4e2796a 100644
--- a/computepipe/router/include/ClientHandle.h
+++ b/computepipe/router/include/ClientHandle.h
@@ -18,6 +18,7 @@
 #define ANDROID_AUTOMOTIVE_COMPUTEPIPE_ROUTER_CLIENT_HANDLE
 
 #include <cstdint>
+#include <string>
 
 namespace android {
 namespace automotive {
@@ -34,7 +35,7 @@
 class ClientHandle {
   public:
     /* Get client identifier as defined by the graph */
-    virtual uint32_t getClientId() = 0;
+    virtual std::string getClientName() = 0;
     /* Is client alive */
     virtual bool isAlive() = 0;
     /* start client monitor */
diff --git a/computepipe/runner/utils/DebuggerInterface.cc b/computepipe/runner/utils/DebuggerInterface.cc
deleted file mode 100644
index 66556da..0000000
--- a/computepipe/runner/utils/DebuggerInterface.cc
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright (C) 2019 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.
diff --git a/computepipe/runner/utils/DebuggerInterface.h b/computepipe/runner/utils/DebuggerInterface.h
deleted file mode 100644
index d262ec3..0000000
--- a/computepipe/runner/utils/DebuggerInterface.h
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright (C) 2019 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.
-
-#ifndef COMPUTEPIPE_RUNNER_UTILS_DEBUGGERINTERFACE_H_
-#define COMPUTEPIPE_RUNNER_UTILS_DEBUGGERINTERFACE_H_
-
-#include <string>
-
-#include "DebuggerInterfaceCallbacks.h"
-
-namespace android {
-namespace automotive {
-namespace computepipe {
-namespace runner_utils {
-
-// DebuggerInterface registers IPipeDebugger interface with computepipe router.
-// DebuggerInterface handles binder IPC calls and invokes appropriate
-// callbacks.
-class DebuggerInterface {
- public:
-    explicit DebuggerInterface(
-        const std::string& graph_name,
-        const DebuggerInterfaceCallbacks& debuggerInterfaceCallbacks);
-};
-
-}  // namespace runner_utils
-}  // namespace computepipe
-}  // namespace automotive
-}  // namespace android
-
-#endif  // COMPUTEPIPE_RUNNER_UTILS_DEBUGGERINTERFACE_H_
diff --git a/computepipe/runner/utils/DebuggerInterfaceCallbacks.h b/computepipe/runner/utils/DebuggerInterfaceCallbacks.h
deleted file mode 100644
index 2671e48..0000000
--- a/computepipe/runner/utils/DebuggerInterfaceCallbacks.h
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (C) 2019 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.
-
-#ifndef COMPUTEPIPE_RUNNER_UTILS_DEBUGGERINTERFACECALLBACKS_H_
-#define COMPUTEPIPE_RUNNER_UTILS_DEBUGGERINTERFACECALLBACKS_H_
-
-#include <functional>
-#include <string>
-
-namespace android {
-namespace automotive {
-namespace computepipe {
-namespace runner_utils {
-
-struct DebuggerInterfaceCallbacks {
-    explicit DebuggerInterfaceCallbacks(
-        std::function<Status()> startGraphProfile,
-        std::function<Status()> stopGraphProfile,
-        std::function<std::string()> GetDebugData) :
-            mStartGraphProfile(startGraphProfile),
-            mStopGraphProfile(stopGraphProfile),
-            mGetDebugData(GetDebugData) {}
-    const std::function<Status()> mStartGraphProfile;
-    const std::function<Status()> mStopGraphProfile;
-    const std::function<std::string()> mGetDebugData;
-};
-
-}  // namespace runner_utils
-}  // namespace computepipe
-}  // namespace automotive
-}  // namespace android
-
-#endif  // COMPUTEPIPE_RUNNER_UTILS_DEBUGGERINTERFACECALLBACKS_H_
diff --git a/computepipe/runner/utils/RunnerInterfaceCallbacks.h b/computepipe/runner/utils/RunnerInterfaceCallbacks.h
deleted file mode 100644
index f2f8ece..0000000
--- a/computepipe/runner/utils/RunnerInterfaceCallbacks.h
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright (C) 2019 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.
-
-#ifndef COMPUTEPIPE_RUNNER_UTILS_RUNNERINTERFACECALLBACKS_H_
-#define COMPUTEPIPE_RUNNER_UTILS_RUNNERINTERFACECALLBACKS_H_
-
-#include <functional>
-#include <memory>
-#include <string>
-
-#include "ConfigurationCommand.pb.h"
-#include "ControlCommand.pb.h"
-#include "MemHandle.h"
-#include "types/Status.h"
-
-namespace android {
-namespace automotive {
-namespace computepipe {
-namespace runner_utils {
-
-struct RunnerInterfaceCallbacks {
-    explicit RunnerInterfaceCallbacks(
-        std::function<Status(const proto::ControlCommand&)> processControlCommand,
-        std::function<Status(const proto::ConfigurationCommand&)> processConfigurationCommand,
-        std::function<Status(const std::shared_ptr<MemHandle>&)> releasePacket)
-        : mProcessControlCommand(processControlCommand),
-          mProcessConfigurationCommand(processConfigurationCommand),
-          mReleasePacket(releasePacket) {
-    }
-
-    const std::function<Status(const proto::ControlCommand&)> mProcessControlCommand;
-    const std::function<Status(const proto::ConfigurationCommand&)>
-        mProcessConfigurationCommand;
-    const std::function<Status(const std::shared_ptr<MemHandle>&)> mReleasePacket;
-};
-
-}  // namespace runner_utils
-}  // namespace computepipe
-}  // namespace automotive
-}  // namespace android
-
-#endif  // COMPUTEPIPE_RUNNER_UTILS_RUNNERINTERFACECALLBACKS_H_
diff --git a/computepipe/tests/PipeQueryTest.cpp b/computepipe/tests/PipeQueryTest.cpp
index acdee1d..3a03391 100644
--- a/computepipe/tests/PipeQueryTest.cpp
+++ b/computepipe/tests/PipeQueryTest.cpp
@@ -41,8 +41,8 @@
  */
 class FakeClientInfo : public BnClientInfo {
   public:
-    ndk::ScopedAStatus getClientId(int32_t* id) override {
-        *id = 1;
+    ndk::ScopedAStatus getClientName(std::string* name) override {
+        *name = "FakeClient";
         return ndk::ScopedAStatus::ok();
     }
 };
diff --git a/computepipe/tests/runner/client_interface/ClientInterfaceTest.cc b/computepipe/tests/runner/client_interface/ClientInterfaceTest.cc
index fd35da5..619c23e 100644
--- a/computepipe/tests/runner/client_interface/ClientInterfaceTest.cc
+++ b/computepipe/tests/runner/client_interface/ClientInterfaceTest.cc
@@ -12,19 +12,18 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <vector>
-
 #include <aidl/android/automotive/computepipe/registry/BnClientInfo.h>
 #include <aidl/android/automotive/computepipe/registry/IPipeQuery.h>
 #include <aidl/android/automotive/computepipe/registry/IPipeRegistration.h>
 #include <aidl/android/automotive/computepipe/runner/BnPipeStateCallback.h>
 #include <aidl/android/automotive/computepipe/runner/BnPipeStream.h>
 #include <aidl/android/automotive/computepipe/runner/PipeState.h>
-
 #include <android/binder_manager.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
+
 #include <utility>
+#include <vector>
 
 #include "ConfigurationCommand.pb.h"
 #include "ControlCommand.pb.h"
@@ -53,16 +52,16 @@
 using ::aidl::android::automotive::computepipe::runner::IPipeStateCallback;
 using ::aidl::android::automotive::computepipe::runner::PacketDescriptor;
 using ::aidl::android::automotive::computepipe::runner::PipeState;
-using ::android::automotive::computepipe::tests::MockMemHandle;
 using ::android::automotive::computepipe::runner::tests::MockRunnerEvent;
+using ::android::automotive::computepipe::tests::MockMemHandle;
 using ::ndk::ScopedAStatus;
 using ::ndk::SharedRefBase;
-using ::testing::AtLeast;
+using ::testing::_;
 using ::testing::AnyNumber;
+using ::testing::AtLeast;
 using ::testing::DoAll;
 using ::testing::Return;
 using ::testing::SaveArg;
-using ::testing::_;
 
 const char kRegistryInterfaceName[] = "router";
 int testIx = 0;
@@ -89,9 +88,9 @@
 
 class ClientInfo : public BnClientInfo {
   public:
-    ScopedAStatus getClientId(int32_t* _aidl_return) {
+    ScopedAStatus getClientName(std::string* _aidl_return) {
         if (_aidl_return) {
-            *_aidl_return = 0;
+            *_aidl_return = "ClientInfo";
             return ScopedAStatus::ok();
         }
         return ScopedAStatus::fromExceptionCode(EX_TRANSACTION_FAILED);
@@ -200,8 +199,7 @@
     status = mPipeRunner->setPipeTermination(3);
     EXPECT_EQ(status.getExceptionCode(), EX_TRANSACTION_FAILED);
     EXPECT_EQ(command.has_set_termination_option(), true);
-    EXPECT_EQ(command.set_termination_option().termination_option_id(),
-              3);
+    EXPECT_EQ(command.set_termination_option().termination_option_id(), 3);
 
     // Test that set output callback returns error status.
     std::shared_ptr<StreamCallback> streamCb = ndk::SharedRefBase::make<StreamCallback>();
@@ -209,8 +207,7 @@
     EXPECT_EQ(status.getExceptionCode(), EX_TRANSACTION_FAILED);
     EXPECT_EQ(command.has_set_output_stream(), true);
     EXPECT_EQ(command.set_output_stream().stream_id(), 0);
-    EXPECT_EQ(command.set_output_stream().max_inflight_packets_count(),
-              10);
+    EXPECT_EQ(command.set_output_stream().max_inflight_packets_count(), 10);
 
     // Release runner here. This should remove registry entry from router registry.
     mAidlClient.reset();
@@ -309,7 +306,7 @@
     EXPECT_TRUE(mPipeRunner->init(stateCallback).isOk());
 
     // Test that config complete status is conveyed to client.
-    std::map<int,int> m;
+    std::map<int, int> m;
     ClientConfig config(0, 0, 0, m);
     config.setPhaseState(TRANSITION_COMPLETE);
     EXPECT_EQ(mAidlClient->handleConfigPhase(config), Status::SUCCESS);
@@ -346,7 +343,7 @@
     EXPECT_TRUE(mPipeRunner->init(stateCallback).isOk());
 
     // Test that error while applying config is conveyed to client.
-    std::map<int,int> m;
+    std::map<int, int> m;
     ClientConfig config(0, 0, 0, m);
     config.setPhaseState(ABORTED);
     EXPECT_EQ(mAidlClient->handleConfigPhase(config), Status::SUCCESS);
@@ -380,23 +377,21 @@
     EXPECT_TRUE(mPipeRunner->setPipeOutputConfig(0, 10, streamCb).isOk());
     EXPECT_EQ(command.has_set_output_stream(), true);
     EXPECT_EQ(command.set_output_stream().stream_id(), 0);
-    EXPECT_EQ(command.set_output_stream().max_inflight_packets_count(),
-              10);
+    EXPECT_EQ(command.set_output_stream().max_inflight_packets_count(), 10);
 
     // Send a packet to client and verify the packet.
     std::shared_ptr<MockMemHandle> packet = std::make_unique<MockMemHandle>();
     uint64_t timestamp = 100;
     const std::string testData = "Test String.";
-    EXPECT_CALL(*packet, getType()).Times(AtLeast(1))
+    EXPECT_CALL(*packet, getType())
+        .Times(AtLeast(1))
         .WillRepeatedly(Return(proto::PacketType::SEMANTIC_DATA));
-    EXPECT_CALL(*packet, getTimeStamp()).Times(AtLeast(1))
-        .WillRepeatedly(Return(timestamp));
-    EXPECT_CALL(*packet, getSize()).Times(AtLeast(1))
-        .WillRepeatedly(Return(testData.size()));
-    EXPECT_CALL(*packet, getData()).Times(AtLeast(1))
-        .WillRepeatedly(Return(testData.c_str()));
-    EXPECT_EQ(mAidlClient->dispatchPacketToClient(
-        0, static_cast<std::shared_ptr<MemHandle>>(packet)), Status::SUCCESS);
+    EXPECT_CALL(*packet, getTimeStamp()).Times(AtLeast(1)).WillRepeatedly(Return(timestamp));
+    EXPECT_CALL(*packet, getSize()).Times(AtLeast(1)).WillRepeatedly(Return(testData.size()));
+    EXPECT_CALL(*packet, getData()).Times(AtLeast(1)).WillRepeatedly(Return(testData.c_str()));
+    EXPECT_EQ(
+        mAidlClient->dispatchPacketToClient(0, static_cast<std::shared_ptr<MemHandle>>(packet)),
+        Status::SUCCESS);
     EXPECT_EQ(streamCb->data, packet->getData());
     EXPECT_EQ(streamCb->timestamp, packet->getTimeStamp());
 }
diff --git a/evs/support_library/StreamHandler.cpp b/evs/support_library/StreamHandler.cpp
index 320f98b..5b962cc 100644
--- a/evs/support_library/StreamHandler.cpp
+++ b/evs/support_library/StreamHandler.cpp
@@ -275,7 +275,6 @@
         output.usage = input.usage;
         output.stride = input.stride;
         output.pixelSize = input.pixelSize;
-        output.bufferId = input.bufferId;
 
         // free the allocated output frame handle if it is not null
         if (output.memHandle.getNativeHandle() != nullptr) {
@@ -287,6 +286,7 @@
             return false;
         }
     }
+    output.bufferId = input.bufferId;
 
     // Create a GraphicBuffer from the existing handle
     sp<GraphicBuffer> inputBuffer = new GraphicBuffer(
diff --git a/service/res/values/config.xml b/service/res/values/config.xml
index c0ef110..c3a9262 100644
--- a/service/res/values/config.xml
+++ b/service/res/values/config.xml
@@ -305,7 +305,9 @@
     -->
     <string-array translatable="false" name="config_allowed_optional_car_features">
         <item>com.android.car.user.CarUserNoticeService</item>
+        <item>diagnostic</item>
         <item>storage_monitoring</item>
+        <item>vehicle_map_subscriber_service</item>
     </string-array>
 
     <!-- Configuration to enable passenger support.
diff --git a/service/src/com/android/car/CarFeatureController.java b/service/src/com/android/car/CarFeatureController.java
index fdaa1c5..1e8a5c5 100644
--- a/service/src/com/android/car/CarFeatureController.java
+++ b/service/src/com/android/car/CarFeatureController.java
@@ -61,35 +61,33 @@
             Car.AUDIO_SERVICE,
             Car.BLUETOOTH_SERVICE,
             Car.CAR_BUGREPORT_SERVICE,
+            Car.CAR_CONFIGURATION_SERVICE,
             Car.CAR_DRIVING_STATE_SERVICE,
             Car.CAR_MEDIA_SERVICE,
             Car.CAR_NAVIGATION_SERVICE,
             Car.CAR_OCCUPANT_ZONE_SERVICE,
+            Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE,
             Car.CAR_USER_SERVICE,
+            Car.CAR_UX_RESTRICTION_SERVICE,
             Car.INFO_SERVICE,
             Car.PACKAGE_SERVICE,
             Car.POWER_SERVICE,
             Car.PROJECTION_SERVICE,
             Car.PROPERTY_SERVICE,
             Car.TEST_SERVICE,
-            // Deprecated, but still should be supported
-            Car.SENSOR_SERVICE,
+            // All items below here are deprecated, but still should be supported
             Car.CAR_INSTRUMENT_CLUSTER_SERVICE,
             Car.CABIN_SERVICE,
             Car.HVAC_SERVICE,
-            Car.VENDOR_EXTENSION_SERVICE,
-            // Candidate for Optional, but stay mandatory for now until final decision is made.
-            Car.CAR_CONFIGURATION_SERVICE,
-            Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE,
-            Car.DIAGNOSTIC_SERVICE,
-            Car.CAR_UX_RESTRICTION_SERVICE,
-            // Marked as optional, but requires additional work
-            Car.VMS_SUBSCRIBER_SERVICE
+            Car.SENSOR_SERVICE,
+            Car.VENDOR_EXTENSION_SERVICE
     ));
 
     private static final HashSet<String> OPTIONAL_FEATURES = new HashSet<>(Arrays.asList(
+            CarFeatures.FEATURE_CAR_USER_NOTICE_SERVICE,
+            Car.DIAGNOSTIC_SERVICE,
             Car.STORAGE_MONITORING_SERVICE,
-            CarFeatures.FEATURE_CAR_USER_NOTICE_SERVICE
+            Car.VMS_SUBSCRIBER_SERVICE
     ));
 
     private static final String FEATURE_CONFIG_FILE_NAME = "car_feature_config.txt";
diff --git a/service/src/com/android/car/CarUxRestrictionsManagerService.java b/service/src/com/android/car/CarUxRestrictionsManagerService.java
index edd74ba..b2c7615 100644
--- a/service/src/com/android/car/CarUxRestrictionsManagerService.java
+++ b/service/src/com/android/car/CarUxRestrictionsManagerService.java
@@ -635,7 +635,7 @@
         writer.println("UX Restriction display info:");
         for (int i = mActivityViewDisplayInfoMap.size() - 1; i >= 0; --i) {
             DisplayInfo info = mActivityViewDisplayInfoMap.valueAt(i);
-            writer.printf("Display%d: physicalDisplayId=%d, owner=%s",
+            writer.printf("Display%d: physicalDisplayId=%d, owner=%s\n",
                     mActivityViewDisplayInfoMap.keyAt(i), info.mPhysicalDisplayId, info.mOwner);
         }
     }
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index 89f14e1..bcdc640 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -204,16 +204,28 @@
         mSystemStateControllerService = new SystemStateControllerService(
                 serviceContext, mCarAudioService, this);
         mCarStatsService = new CarStatsService(serviceContext);
-        mVmsBrokerService = new VmsBrokerService();
-        mVmsClientManager = new VmsClientManager(
-                // CarStatsService needs to be passed to the constructor due to HAL init order
-                serviceContext, mCarStatsService, mCarUserService, mVmsBrokerService,
-                mHal.getVmsHal());
-        mVmsSubscriberService = new VmsSubscriberService(
-                serviceContext, mVmsBrokerService, mVmsClientManager, mHal.getVmsHal());
-        mVmsPublisherService = new VmsPublisherService(
-                serviceContext, mCarStatsService, mVmsBrokerService, mVmsClientManager);
-        mCarDiagnosticService = new CarDiagnosticService(serviceContext, mHal.getDiagnosticHal());
+        if (mFeatureController.isFeatureEnabled(Car.VMS_SUBSCRIBER_SERVICE)) {
+            mVmsBrokerService = new VmsBrokerService();
+            mVmsClientManager = new VmsClientManager(
+                    // CarStatsService needs to be passed to the constructor due to HAL init order
+                    serviceContext, mCarStatsService, mCarUserService, mVmsBrokerService,
+                    mHal.getVmsHal());
+            mVmsSubscriberService = new VmsSubscriberService(
+                    serviceContext, mVmsBrokerService, mVmsClientManager, mHal.getVmsHal());
+            mVmsPublisherService = new VmsPublisherService(
+                    serviceContext, mCarStatsService, mVmsBrokerService, mVmsClientManager);
+        } else {
+            mVmsBrokerService = null;
+            mVmsClientManager = null;
+            mVmsSubscriberService = null;
+            mVmsPublisherService = null;
+        }
+        if (mFeatureController.isFeatureEnabled(Car.DIAGNOSTIC_SERVICE)) {
+            mCarDiagnosticService = new CarDiagnosticService(serviceContext,
+                    mHal.getDiagnosticHal());
+        } else {
+            mCarDiagnosticService = null;
+        }
         if (mFeatureController.isFeatureEnabled(Car.STORAGE_MONITORING_SERVICE)) {
             mCarStorageMonitoringService = new CarStorageMonitoringService(serviceContext,
                     systemInterface);
@@ -265,12 +277,12 @@
         allServices.add(mPerUserCarServiceHelper);
         allServices.add(mCarBluetoothService);
         allServices.add(mCarProjectionService);
-        allServices.add(mCarDiagnosticService);
+        addServiceIfNonNull(allServices, mCarDiagnosticService);
         addServiceIfNonNull(allServices, mCarStorageMonitoringService);
         allServices.add(mCarConfigurationService);
-        allServices.add(mVmsClientManager);
-        allServices.add(mVmsSubscriberService);
-        allServices.add(mVmsPublisherService);
+        addServiceIfNonNull(allServices, mVmsClientManager);
+        addServiceIfNonNull(allServices, mVmsSubscriberService);
+        addServiceIfNonNull(allServices, mVmsPublisherService);
         allServices.add(mCarTrustedDeviceService);
         allServices.add(mCarMediaService);
         allServices.add(mCarLocationService);
diff --git a/service/src/com/android/car/audio/CarAudioService.java b/service/src/com/android/car/audio/CarAudioService.java
index 5466d80..8c47105 100644
--- a/service/src/com/android/car/audio/CarAudioService.java
+++ b/service/src/com/android/car/audio/CarAudioService.java
@@ -36,6 +36,7 @@
 import android.hardware.automotive.audiocontrol.V1_0.IAudioControl;
 import android.media.AudioAttributes;
 import android.media.AudioAttributes.AttributeSystemUsage;
+import android.media.AudioDeviceAddress;
 import android.media.AudioDeviceInfo;
 import android.media.AudioDevicePort;
 import android.media.AudioFocusInfo;
@@ -412,10 +413,16 @@
                 .collect(Collectors.toList());
     }
 
+    private AudioDeviceInfo[] getAllInputDevices() {
+        return mAudioManager.getDevices(
+                AudioManager.GET_DEVICES_INPUTS);
+    }
+
     private CarAudioZone[] loadCarAudioConfiguration(List<CarAudioDeviceInfo> carAudioDeviceInfos) {
+        AudioDeviceInfo[] inputDevices = getAllInputDevices();
         try (InputStream inputStream = new FileInputStream(mCarAudioConfigurationPath)) {
             CarAudioZonesHelper zonesHelper = new CarAudioZonesHelper(mContext, inputStream,
-                    carAudioDeviceInfos);
+                    carAudioDeviceInfos, inputDevices);
             return zonesHelper.loadAudioZones();
         } catch (IOException | XmlPullParserException e) {
             throw new RuntimeException("Failed to parse audio zone configuration", e);
@@ -1024,6 +1031,21 @@
     }
 
     /**
+     * Gets the input devices for zone zoneId
+     */
+    public @NonNull List<AudioDeviceAddress> getInputDevicesAddressesForZoneId(int zoneId) {
+        enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS);
+        Preconditions.checkArgumentInRange(zoneId, 0, mCarAudioZones.length - 1,
+                "zoneId out of range: " + zoneId);
+        for (CarAudioZone zone : mCarAudioZones) {
+            if (zone.getId() == zoneId) {
+                return zone.getInputAudioDeviceAddresses();
+            }
+        }
+        throw new IllegalArgumentException("zoneId does not exist" + zoneId);
+    }
+
+    /**
      * Gets volume group by a given legacy stream type
      * @param streamType Legacy stream type such as {@link AudioManager#STREAM_MUSIC}
      * @return volume group id mapped from stream type
diff --git a/service/src/com/android/car/audio/CarAudioZone.java b/service/src/com/android/car/audio/CarAudioZone.java
index f512d48..63b0131 100644
--- a/service/src/com/android/car/audio/CarAudioZone.java
+++ b/service/src/com/android/car/audio/CarAudioZone.java
@@ -16,6 +16,7 @@
 package com.android.car.audio;
 
 import android.car.media.CarAudioManager;
+import android.media.AudioDeviceAddress;
 import android.media.AudioDeviceInfo;
 import android.util.Log;
 import android.view.DisplayAddress;
@@ -45,12 +46,14 @@
     private final String mName;
     private final List<CarVolumeGroup> mVolumeGroups;
     private final List<DisplayAddress.Physical> mPhysicalDisplayAddresses;
+    private List<AudioDeviceAddress> mInputAudioDeviceAddress;
 
     CarAudioZone(int id, String name) {
         mId = id;
         mName = name;
         mVolumeGroups = new ArrayList<>();
         mPhysicalDisplayAddresses = new ArrayList<>();
+        mInputAudioDeviceAddress = new ArrayList<>();
     }
 
     int getId() {
@@ -169,15 +172,23 @@
     }
 
     void dump(String indent, PrintWriter writer) {
+        String internalIndent = indent + "\t";
         writer.printf("%sCarAudioZone(%s:%d) isPrimary? %b\n", indent, mName, mId, isPrimaryZone());
         for (DisplayAddress.Physical physical: mPhysicalDisplayAddresses) {
             long port = (long) physical.getPort();
-            writer.printf("%sDisplayAddress.Physical(%d)\n", indent + "\t", port);
+            writer.printf("%sDisplayAddress.Physical(%d)\n", internalIndent, port);
         }
         writer.println();
 
         for (CarVolumeGroup group : mVolumeGroups) {
-            group.dump(indent + "\t", writer);
+            group.dump(internalIndent, writer);
+        }
+
+        writer.printf("%sInput Audio Device Addresses\n", internalIndent);
+        String devicesIndent = internalIndent + "\t";
+        for (AudioDeviceAddress audioDeviceAddress : mInputAudioDeviceAddress) {
+            writer.printf("%sDevice Address(%s)\n", devicesIndent,
+                    audioDeviceAddress.getAddress());
         }
         writer.println();
     }
@@ -206,4 +217,12 @@
             group.loadVolumesForUser(userId);
         }
     }
+
+    void addInputAudioDeviceAddress(AudioDeviceAddress address) {
+        mInputAudioDeviceAddress.add(address);
+    }
+
+    List<AudioDeviceAddress> getInputAudioDeviceAddresses() {
+        return mInputAudioDeviceAddress;
+    }
 }
diff --git a/service/src/com/android/car/audio/CarAudioZonesHelper.java b/service/src/com/android/car/audio/CarAudioZonesHelper.java
index 640dd0a..dd1eadb 100644
--- a/service/src/com/android/car/audio/CarAudioZonesHelper.java
+++ b/service/src/com/android/car/audio/CarAudioZonesHelper.java
@@ -19,7 +19,10 @@
 import android.car.media.CarAudioManager;
 import android.content.Context;
 import android.hardware.automotive.audiocontrol.V1_0.ContextNumber;
+import android.media.AudioDeviceAddress;
+import android.media.AudioDeviceInfo;
 import android.text.TextUtils;
+import android.util.SparseIntArray;
 import android.util.Xml;
 import android.view.DisplayAddress;
 
@@ -36,6 +39,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 import java.util.stream.Collectors;
 
@@ -43,6 +47,7 @@
  * A helper class loads all audio zones from the configuration XML file.
  */
 /* package */ class CarAudioZonesHelper {
+    private static final int INVALID_ZONE_ID = -1;
 
     private static final String NAMESPACE = null;
     private static final String TAG_ROOT = "carAudioConfiguration";
@@ -60,12 +65,20 @@
     private static final String ATTR_DEVICE_ADDRESS = "address";
     private static final String ATTR_CONTEXT_NAME = "context";
     private static final String ATTR_PHYSICAL_PORT = "port";
-    private static final int SUPPORTED_VERSION = 1;
+    private static final String ATTR_ZONE_ID = "audioZoneId";
+    private static final String ATTR_OCCUPANT_ZONE_ID = "occupantZoneId";
+    private static final String TAG_INPUT_DEVICES = "inputDevices";
+    private static final String TAG_INPUT_DEVICE = "inputDevice";
+    private static final int INVALID_VERSION = -1;
+    private static final int SUPPORTED_VERSION_1 = 1;
+    private static final int SUPPORTED_VERSION_2 = 2;
+    private static final SparseIntArray SUPPORTED_VERSIONS;
+
 
     private static final Map<String, Integer> CONTEXT_NAME_MAP;
 
     static {
-        CONTEXT_NAME_MAP = new HashMap<>();
+        CONTEXT_NAME_MAP = new HashMap<>(8);
         CONTEXT_NAME_MAP.put("music", ContextNumber.MUSIC);
         CONTEXT_NAME_MAP.put("navigation", ContextNumber.NAVIGATION);
         CONTEXT_NAME_MAP.put("voice_command", ContextNumber.VOICE_COMMAND);
@@ -74,27 +87,52 @@
         CONTEXT_NAME_MAP.put("alarm", ContextNumber.ALARM);
         CONTEXT_NAME_MAP.put("notification", ContextNumber.NOTIFICATION);
         CONTEXT_NAME_MAP.put("system_sound", ContextNumber.SYSTEM_SOUND);
+
+        SUPPORTED_VERSIONS = new SparseIntArray(2);
+        SUPPORTED_VERSIONS.put(SUPPORTED_VERSION_1, SUPPORTED_VERSION_1);
+        SUPPORTED_VERSIONS.put(SUPPORTED_VERSION_2, SUPPORTED_VERSION_2);
     }
 
     private final Context mContext;
     private final Map<String, CarAudioDeviceInfo> mAddressToCarAudioDeviceInfo;
+    private final Map<String, AudioDeviceInfo> mAddressToInputAudioDeviceInfo;
     private final InputStream mInputStream;
     private final Set<Long> mPortIds;
+    private final SparseIntArray mZoneIdToOccupantZoneIdMapping;
+    private final Set<Integer> mAudioZoneIds;
+    private final Set<String> mInputAudioDeviceAddresses;
 
     private boolean mHasPrimaryZone;
     private int mNextSecondaryZoneId;
+    private int mCurrentVersion;
 
+    /**
+     *  <p><b>Note: <b/> CarAudioZonesHelper is expected to be used
+     *  from a single thread. This should be the same thread that
+     *  originally called new CarAudioZonesHelper.<p/>
+     *
+     */
     CarAudioZonesHelper(Context context, @NonNull InputStream inputStream,
-            @NonNull List<CarAudioDeviceInfo> carAudioDeviceInfos) {
+            @NonNull List<CarAudioDeviceInfo> carAudioDeviceInfos,
+            @NonNull AudioDeviceInfo[] inputDeviceInfo) {
         mContext = context;
         mInputStream = inputStream;
         mAddressToCarAudioDeviceInfo = CarAudioZonesHelper.generateAddressToInfoMap(
                 carAudioDeviceInfos);
-
+        mAddressToInputAudioDeviceInfo =
+                CarAudioZonesHelper.generateAddressToInputAudioDeviceInfoMap(inputDeviceInfo);
         mNextSecondaryZoneId = CarAudioManager.PRIMARY_AUDIO_ZONE + 1;
         mPortIds = new HashSet<>();
+        mZoneIdToOccupantZoneIdMapping = new SparseIntArray();
+        mAudioZoneIds = new HashSet<>();
+        mInputAudioDeviceAddresses = new HashSet<>();
     }
 
+    SparseIntArray getCarAudioZoneIdToOccupantZoneIdMapping() {
+        return mZoneIdToOccupantZoneIdMapping;
+    }
+
+    // TODO: refactor this method to return List<CarAudioZone>
     CarAudioZone[] loadAudioZones() throws IOException, XmlPullParserException {
         List<CarAudioZone> carAudioZones = new ArrayList<>();
         parseCarAudioZones(carAudioZones, mInputStream);
@@ -108,6 +146,19 @@
                 .collect(Collectors.toMap(CarAudioDeviceInfo::getAddress, info -> info));
     }
 
+    private static Map<String, AudioDeviceInfo> generateAddressToInputAudioDeviceInfoMap(
+            @NonNull AudioDeviceInfo[] inputAudioDeviceInfos) {
+        HashMap<String, AudioDeviceInfo> deviceAddressToInputDeviceMap =
+                new HashMap<>(inputAudioDeviceInfos.length);
+        for (int i = 0; i < inputAudioDeviceInfos.length; ++i) {
+            AudioDeviceInfo device = inputAudioDeviceInfos[i];
+            if (device.isSource()) {
+                deviceAddressToInputDeviceMap.put(device.getAddress(), device);
+            }
+        }
+        return deviceAddressToInputDeviceMap;
+    }
+
     private void parseCarAudioZones(List<CarAudioZone> carAudioZones, InputStream stream)
             throws XmlPullParserException, IOException {
         final XmlPullParser parser = Xml.newPullParser();
@@ -121,11 +172,14 @@
         // Version check
         final int versionNumber = Integer.parseInt(
                 parser.getAttributeValue(NAMESPACE, ATTR_VERSION));
-        if (versionNumber != SUPPORTED_VERSION) {
-            throw new RuntimeException("Support version:"
-                    + SUPPORTED_VERSION + " only, got version:" + versionNumber);
+
+        if (SUPPORTED_VERSIONS.get(versionNumber, INVALID_VERSION) == INVALID_VERSION) {
+            throw new IllegalArgumentException("Latest Supported version:"
+                    + SUPPORTED_VERSION_2 + " , got version:" + versionNumber);
         }
 
+        mCurrentVersion = versionNumber;
+
         // Get all zones configured under <zones> tag
         while (parser.next() != XmlPullParser.END_TAG) {
             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
@@ -160,10 +214,9 @@
             mHasPrimaryZone = true;
         }
         final String zoneName = parser.getAttributeValue(NAMESPACE, ATTR_ZONE_NAME);
-
-        CarAudioZone zone = new CarAudioZone(
-                isPrimary ? CarAudioManager.PRIMARY_AUDIO_ZONE : getNextSecondaryZoneId(),
-                zoneName);
+        final int audioZoneId = getZoneId(isPrimary, parser);
+        parseOccupantZoneId(audioZoneId, parser);
+        final CarAudioZone zone = new CarAudioZone(audioZoneId, zoneName);
         while (parser.next() != XmlPullParser.END_TAG) {
             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
             // Expect one <volumeGroups> in one audio zone
@@ -171,6 +224,8 @@
                 parseVolumeGroups(parser, zone);
             } else if (TAG_DISPLAYS.equals(parser.getName())) {
                 parseDisplays(parser, zone);
+            } else if (TAG_INPUT_DEVICES.equals(parser.getName())) {
+                parseInputAudioDevices(parser, zone);
             } else {
                 skip(parser);
             }
@@ -178,6 +233,100 @@
         return zone;
     }
 
+    private int getZoneId(boolean isPrimary, XmlPullParser parser) {
+        String audioZoneIdString = parser.getAttributeValue(NAMESPACE, ATTR_ZONE_ID);
+        if (mCurrentVersion == SUPPORTED_VERSION_1) {
+            Preconditions.checkArgument(audioZoneIdString == null,
+                    "Invalid audio attribute %s"
+                            + ", Please update car audio configurations file "
+                            + "to version to 2 to use it.", ATTR_ZONE_ID);
+            return isPrimary ? CarAudioManager.PRIMARY_AUDIO_ZONE
+                    : getNextSecondaryZoneId();
+        }
+        // Primary zone does not need to define it
+        if (isPrimary && audioZoneIdString == null) {
+            return CarAudioManager.PRIMARY_AUDIO_ZONE;
+        }
+        Objects.requireNonNull(audioZoneIdString, () ->
+                "Requires " + ATTR_ZONE_ID + " for all audio zones.");
+        int zoneId = parsePositiveIntAttribute(ATTR_ZONE_ID, audioZoneIdString);
+        //Verify that primary zone id is PRIMARY_AUDIO_ZONE
+        if (isPrimary) {
+            Preconditions.checkArgument(zoneId == CarAudioManager.PRIMARY_AUDIO_ZONE,
+                    "Primary zone %s must be %d or it can be left empty.",
+                    ATTR_ZONE_ID, CarAudioManager.PRIMARY_AUDIO_ZONE);
+        } else {
+            Preconditions.checkArgument(zoneId != CarAudioManager.PRIMARY_AUDIO_ZONE,
+                    "%s can only be %d for primary zone.",
+                    ATTR_ZONE_ID, CarAudioManager.PRIMARY_AUDIO_ZONE);
+        }
+        validateAudioZoneIdIsUnique(zoneId);
+        return zoneId;
+    }
+
+    private void parseOccupantZoneId(int audioZoneId, XmlPullParser parser) {
+        String occupantZoneIdString = parser.getAttributeValue(NAMESPACE, ATTR_OCCUPANT_ZONE_ID);
+        if (mCurrentVersion == SUPPORTED_VERSION_1) {
+            Preconditions.checkArgument(occupantZoneIdString == null,
+                    "Invalid audio attribute %s"
+                            + ", Please update car audio configurations file "
+                            + "to version to 2 to use it.", ATTR_OCCUPANT_ZONE_ID);
+            return;
+        }
+        //Occupant id not required for all zones
+        if (occupantZoneIdString == null) {
+            return;
+        }
+        int occupantZoneId = parsePositiveIntAttribute(ATTR_OCCUPANT_ZONE_ID, occupantZoneIdString);
+        validateOccupantZoneIdIsUnique(occupantZoneId);
+        mZoneIdToOccupantZoneIdMapping.put(audioZoneId, occupantZoneId);
+    }
+
+    private int parsePositiveIntAttribute(String attribute, String integerString) {
+        try {
+            return Integer.parseUnsignedInt(integerString);
+        } catch (NumberFormatException | IndexOutOfBoundsException e) {
+            throw new IllegalArgumentException(attribute + " must be a positive integer, but was \""
+                    + integerString + "\" instead.", e);
+        }
+    }
+    private void parseInputAudioDevices(XmlPullParser parser, CarAudioZone zone)
+            throws IOException, XmlPullParserException {
+        if (mCurrentVersion == SUPPORTED_VERSION_1) {
+            throw new IllegalStateException(
+                    TAG_INPUT_DEVICES + " are not supported in car_audio_configuration.xml version "
+                    + SUPPORTED_VERSION_1);
+        }
+        while (parser.next() != XmlPullParser.END_TAG) {
+            if (parser.getEventType() != XmlPullParser.START_TAG) continue;
+            if (TAG_INPUT_DEVICE.equals(parser.getName())) {
+                String audioDeviceAddress =
+                        parser.getAttributeValue(NAMESPACE, ATTR_DEVICE_ADDRESS);
+                validateInputAudioDeviceAddress(audioDeviceAddress);
+                AudioDeviceInfo info = mAddressToInputAudioDeviceInfo.get(audioDeviceAddress);
+                Preconditions.checkArgument(info != null,
+                        "%s %s of %s does not exist, add input device to"
+                                + " audio_policy_configuration.xml.",
+                        ATTR_DEVICE_ADDRESS, audioDeviceAddress, TAG_INPUT_DEVICE);
+                zone.addInputAudioDeviceAddress(new AudioDeviceAddress(info));
+            }
+            skip(parser);
+        }
+    }
+
+    private void validateInputAudioDeviceAddress(String audioDeviceAddress) {
+        Objects.requireNonNull(audioDeviceAddress, () ->
+                TAG_INPUT_DEVICE + " " + ATTR_DEVICE_ADDRESS + " attribute must be present.");
+        Preconditions.checkArgument(!audioDeviceAddress.isEmpty(),
+                "%s %s attribute can not be empty.",
+                TAG_INPUT_DEVICE, ATTR_DEVICE_ADDRESS);
+        if (mInputAudioDeviceAddresses.contains(audioDeviceAddress)) {
+            throw new IllegalArgumentException(TAG_INPUT_DEVICE + " " + audioDeviceAddress
+                    + " repeats, " + TAG_INPUT_DEVICES + " can not repeat.");
+        }
+        mInputAudioDeviceAddresses.add(audioDeviceAddress);
+    }
+
     private void parseDisplays(XmlPullParser parser, CarAudioZone zone)
             throws IOException, XmlPullParserException {
         while (parser.next() != XmlPullParser.END_TAG) {
@@ -208,6 +357,21 @@
         mPortIds.add(portId);
     }
 
+    private void validateOccupantZoneIdIsUnique(int occupantZoneId) {
+        if (mZoneIdToOccupantZoneIdMapping.indexOfValue(occupantZoneId) > -1) {
+            throw new IllegalArgumentException(ATTR_OCCUPANT_ZONE_ID + " " + occupantZoneId
+                    + " is already associated with a zone");
+        }
+    }
+
+    private void validateAudioZoneIdIsUnique(int audioZoneId) {
+        if (mAudioZoneIds.contains(audioZoneId)) {
+            throw new IllegalArgumentException(ATTR_ZONE_ID + " " + audioZoneId
+                    + " is already associated with a zone");
+        }
+        mAudioZoneIds.add(audioZoneId);
+    }
+
     private void parseVolumeGroups(XmlPullParser parser, CarAudioZone zone)
             throws XmlPullParserException, IOException {
         int groupId = 0;
diff --git a/service/src/com/android/car/hal/DiagnosticHalService.java b/service/src/com/android/car/hal/DiagnosticHalService.java
index 506b922..f2027ab 100644
--- a/service/src/com/android/car/hal/DiagnosticHalService.java
+++ b/service/src/com/android/car/hal/DiagnosticHalService.java
@@ -49,7 +49,7 @@
  */
 public class DiagnosticHalService extends  HalServiceBase{
     static final int OBD2_SELECTIVE_FRAME_CLEAR = 1;
-    static final boolean DEBUG = true;
+    static final boolean DEBUG = false;
     private final Object mLock = new Object();
     private final VehicleHal mVehicleHal;
 
diff --git a/service/src/com/android/car/user/CarUserNoticeService.java b/service/src/com/android/car/user/CarUserNoticeService.java
index 70c5eb4..ba617c3 100644
--- a/service/src/com/android/car/user/CarUserNoticeService.java
+++ b/service/src/com/android/car/user/CarUserNoticeService.java
@@ -48,6 +48,8 @@
 import android.view.IWindowManager;
 import android.view.WindowManagerGlobal;
 
+import androidx.annotation.VisibleForTesting;
+
 import com.android.car.CarLocalServices;
 import com.android.car.CarServiceBase;
 import com.android.car.R;
@@ -78,7 +80,7 @@
     @Nullable
     private final Intent mServiceIntent;
 
-    private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+    private final Handler mMainHandler;
 
     private final Object mLock = new Object();
 
@@ -203,6 +205,12 @@
     };
 
     public CarUserNoticeService(Context context) {
+        this(context, new Handler(Looper.getMainLooper()));
+    }
+
+    @VisibleForTesting
+    CarUserNoticeService(Context context, Handler handler) {
+        mMainHandler = handler;
         Resources res = context.getResources();
         String componentName = res.getString(R.string.config_userNoticeUiService);
         if (componentName.isEmpty()) {
diff --git a/tests/CarDeveloperOptions/AndroidManifest.xml b/tests/CarDeveloperOptions/AndroidManifest.xml
index 7347c6a..436d7f0 100644
--- a/tests/CarDeveloperOptions/AndroidManifest.xml
+++ b/tests/CarDeveloperOptions/AndroidManifest.xml
@@ -85,7 +85,7 @@
     <uses-permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY" />
     <uses-permission android:name="android.permission.CONFIGURE_DISPLAY_COLOR_MODE" />
     <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
-    <uses-permission android:name="android.permission.SET_TIME" />
+    <uses-permission android:name="android.permission.SUGGEST_MANUAL_TIME_AND_ZONE" />
     <uses-permission android:name="android.permission.ACCESS_NOTIFICATIONS" />
     <uses-permission android:name="android.permission.REBOOT" />
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/IccLockSettings.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/IccLockSettings.java
index 02bd469..0eff1c0 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/IccLockSettings.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/IccLockSettings.java
@@ -517,7 +517,7 @@
         params.format = PixelFormat.TRANSLUCENT;
         params.windowAnimations = com.android.internal.R.style.Animation_Toast;
         params.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
-        params.setFitWindowInsetsTypes(params.getFitWindowInsetsTypes() & ~Type.statusBars());
+        params.setFitInsetsTypes(params.getFitInsetsTypes() & ~Type.statusBars());
         params.setTitle(errorMessage);
         params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/datetime/DatePreferenceController.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/datetime/DatePreferenceController.java
index 2434f1b..99625d7 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/datetime/DatePreferenceController.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/datetime/DatePreferenceController.java
@@ -17,8 +17,9 @@
 package com.android.car.developeroptions.datetime;
 
 import android.app.Activity;
-import android.app.AlarmManager;
 import android.app.DatePickerDialog;
+import android.app.timedetector.ManualTimeSuggestion;
+import android.app.timedetector.TimeDetector;
 import android.content.Context;
 import android.text.TextUtils;
 import android.text.format.DateFormat;
@@ -119,7 +120,10 @@
         long when = Math.max(c.getTimeInMillis(), DatePreferenceHost.MIN_DATE);
 
         if (when / 1000 < Integer.MAX_VALUE) {
-            ((AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE)).setTime(when);
+            TimeDetector timeDetector = mContext.getSystemService(TimeDetector.class);
+            ManualTimeSuggestion manualTimeSuggestion =
+                    TimeDetector.createManualTimeSuggestion(when, "Settings: Set date");
+            timeDetector.suggestManualTime(manualTimeSuggestion);
         }
     }
 }
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/datetime/TimePreferenceController.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/datetime/TimePreferenceController.java
index aeabc6e..ef0e7a9 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/datetime/TimePreferenceController.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/datetime/TimePreferenceController.java
@@ -17,8 +17,9 @@
 package com.android.car.developeroptions.datetime;
 
 import android.app.Activity;
-import android.app.AlarmManager;
 import android.app.TimePickerDialog;
+import android.app.timedetector.ManualTimeSuggestion;
+import android.app.timedetector.TimeDetector;
 import android.content.Context;
 import android.text.TextUtils;
 import android.text.format.DateFormat;
@@ -117,7 +118,10 @@
         long when = Math.max(c.getTimeInMillis(), TimePreferenceHost.MIN_DATE);
 
         if (when / 1000 < Integer.MAX_VALUE) {
-            ((AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE)).setTime(when);
+            TimeDetector timeDetector = mContext.getSystemService(TimeDetector.class);
+            ManualTimeSuggestion manualTimeSuggestion =
+                    TimeDetector.createManualTimeSuggestion(when, "Settings: Set time");
+            timeDetector.suggestManualTime(manualTimeSuggestion);
         }
     }
 }
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/datetime/timezone/TimeZoneSettings.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/datetime/timezone/TimeZoneSettings.java
index 4f43cd3..481e6ba 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/datetime/timezone/TimeZoneSettings.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/datetime/timezone/TimeZoneSettings.java
@@ -17,8 +17,9 @@
 package com.android.car.developeroptions.datetime.timezone;
 
 import android.app.Activity;
-import android.app.AlarmManager;
 import android.app.settings.SettingsEnums;
+import android.app.timezonedetector.ManualTimeZoneSuggestion;
+import android.app.timezonedetector.TimeZoneDetector;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
@@ -270,7 +271,10 @@
             editor.putString(PREF_KEY_REGION, regionId);
         }
         editor.apply();
-        getActivity().getSystemService(AlarmManager.class).setTimeZone(tzId);
+        TimeZoneDetector timeZoneDetector = getActivity().getSystemService(TimeZoneDetector.class);
+        ManualTimeZoneSuggestion suggestion = TimeZoneDetector.createManualTimeZoneSuggestion(
+                tzId, "Settings: Set time zone");
+        timeZoneDetector.suggestManualTimeZone(suggestion);
     }
 
     @Override
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/network/telephony/CellInfoUtil.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/network/telephony/CellInfoUtil.java
index 0be8ff6..5650d10 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/network/telephony/CellInfoUtil.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/network/telephony/CellInfoUtil.java
@@ -33,6 +33,7 @@
 
 import com.android.internal.telephony.OperatorInfo;
 
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -149,7 +150,8 @@
                 mcc,
                 mnc,
                 operatorInfo.getOperatorAlphaLong(),
-                operatorInfo.getOperatorAlphaShort());
+                operatorInfo.getOperatorAlphaShort(),
+                Collections.emptyList());
 
         CellInfoGsm ci = new CellInfoGsm();
         ci.setCellIdentity(cig);
diff --git a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/sim/SimPreferenceDialog.java b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/sim/SimPreferenceDialog.java
index 39d3f2e..376f349 100644
--- a/tests/CarDeveloperOptions/src/com/android/car/developeroptions/sim/SimPreferenceDialog.java
+++ b/tests/CarDeveloperOptions/src/com/android/car/developeroptions/sim/SimPreferenceDialog.java
@@ -65,7 +65,7 @@
         mSlotId = extras.getInt(SimSettings.EXTRA_SLOT_ID, -1);
         mSubscriptionManager = SubscriptionManager.from(mContext);
         mSubInfoRecord = mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(mSlotId);
-        mTintArr = mContext.getResources().getIntArray(com.android.internal.R.array.sim_colors);
+        mTintArr = mContext.getResources().getIntArray(android.R.array.simColors);
         mColorStrings = mContext.getResources().getStringArray(R.array.color_picker);
         mTintSelectorPos = 0;
 
diff --git a/tests/DefaultStorageMonitoringCompanionApp/src/com/google/android/car/defaultstoragemonitoringcompanionapp/MainActivity.java b/tests/DefaultStorageMonitoringCompanionApp/src/com/google/android/car/defaultstoragemonitoringcompanionapp/MainActivity.java
index 159260a..02adca2 100644
--- a/tests/DefaultStorageMonitoringCompanionApp/src/com/google/android/car/defaultstoragemonitoringcompanionapp/MainActivity.java
+++ b/tests/DefaultStorageMonitoringCompanionApp/src/com/google/android/car/defaultstoragemonitoringcompanionapp/MainActivity.java
@@ -111,7 +111,11 @@
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
             Log.d(TAG, "Connected to " + name.flattenToString());
-
+            if (!mCar.isFeatureEnabled(Car.STORAGE_MONITORING_SERVICE)) {
+                Log.e(TAG, "Car.STORAGE_MONITORING_SERVICE feature not supported, will finish");
+                finish();
+                return;
+            }
             CarStorageMonitoringManager storageMonitoringManager =
                     (CarStorageMonitoringManager) mCar.getCarManager(
                             Car.STORAGE_MONITORING_SERVICE);
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/activity_view_test_fragment.xml b/tests/EmbeddedKitchenSinkApp/res/layout/activity_view_test_fragment.xml
index bd74423..568bad5 100644
--- a/tests/EmbeddedKitchenSinkApp/res/layout/activity_view_test_fragment.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/activity_view_test_fragment.xml
@@ -19,7 +19,7 @@
     <RelativeLayout android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:padding="20dp">
-        <ActivityView android:id="@+id/av_activityview"
+        <android.car.app.CarActivityView android:id="@+id/av_activityview"
                       android:layout_width="match_parent"
                       android:layout_height="match_parent"/>
     </RelativeLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/res/layout/package_info_test.xml b/tests/EmbeddedKitchenSinkApp/res/layout/package_info_test.xml
new file mode 100644
index 0000000..23a5765
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/res/layout/package_info_test.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+<LinearLayout
+    android:layout_width="fill_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    xmlns:android="http://schemas.android.com/apk/res/android">
+    <LinearLayout
+        android:id="@+id/header"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="@dimen/header_margin"
+        android:orientation="horizontal">
+        <Spinner
+            android:id = "@+id/spinner"
+            android:layout_width = "wrap_content"
+            android:layout_height = "wrap_content"
+            android:layout_weight = "1"
+            android:paddingLeft="@dimen/header_padding"
+            android:prompt = "@string/select_user"
+            android:textSize = "@dimen/spinner_text_size"
+            />
+        <CheckBox
+            android:id = "@+id/checkbox_activities"
+            android:layout_width = "wrap_content"
+            android:layout_height = "wrap_content"
+            android:layout_weight = "1"
+            android:paddingLeft="@dimen/header_padding"
+            android:text = "@string/checkbox_activities"
+            android:textSize = "@dimen/checkbox_text_size"
+            />
+        <CheckBox
+            android:id = "@+id/checkbox_services"
+            android:layout_width = "wrap_content"
+            android:layout_height = "wrap_content"
+            android:layout_weight = "1"
+            android:text = "@string/checkbox_services"
+            android:textSize = "@dimen/checkbox_text_size"
+            />
+        <CheckBox
+            android:id = "@+id/checkbox_permissions"
+            android:layout_width = "wrap_content"
+            android:layout_height = "wrap_content"
+            android:layout_weight = "1"
+            android:text = "@string/checkbox_permissions"
+            android:textSize = "@dimen/checkbox_text_size"
+            />
+        <CheckBox
+            android:id = "@+id/checkbox_shareduid"
+            android:layout_width = "wrap_content"
+            android:layout_height = "wrap_content"
+            android:layout_weight = "1"
+            android:text = "@string/checkbox_shareduid"
+            android:textSize = "@dimen/checkbox_text_size"
+            />
+        <Button
+            android:id = "@+id/button_show"
+            android:layout_width = "wrap_content"
+            android:layout_height = "wrap_content"
+            android:layout_weight = "1"
+            android:text = "@string/button_show"
+            android:textSize = "@dimen/checkbox_text_size"
+            />
+    </LinearLayout>
+    <ScrollView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:id="@+id/packages"/>
+     </ScrollView>
+</LinearLayout>
diff --git a/tests/EmbeddedKitchenSinkApp/res/values/dimens.xml b/tests/EmbeddedKitchenSinkApp/res/values/dimens.xml
index 437d69d..a649f9c 100644
--- a/tests/EmbeddedKitchenSinkApp/res/values/dimens.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/values/dimens.xml
@@ -45,6 +45,12 @@
     <dimen name="car_card_header_height">96dp</dimen>
     <dimen name="car_card_action_bar_height">96dp</dimen>
 
+    <!-- Package info -->
+    <dimen name="spinner_text_size">16sp</dimen>
+    <dimen name="checkbox_text_size">16sp</dimen>
+    <dimen name="header_margin">8dp</dimen>
+    <dimen name="header_padding">8dp</dimen>
+
     <!-- Paddings -->
     <dimen name="car_padding_1">4dp</dimen>
     <dimen name="car_padding_2">10dp</dimen>
diff --git a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
index dce047f..da2d341 100644
--- a/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
+++ b/tests/EmbeddedKitchenSinkApp/res/values/strings.xml
@@ -178,6 +178,14 @@
     <string name="sw_center" translatable="false">Center</string>
     <string name="sw_back" translatable="false">Back</string>
 
+    <!-- package info test -->
+    <string name="select_user" translatable="false">Select user</string>
+    <string name="checkbox_activities" translatable="false">contains only activities</string>
+    <string name="checkbox_services" translatable="false">services are not exported</string>
+    <string name="checkbox_permissions" translatable="false"> does not require selected permissions</string>
+    <string name="checkbox_shareduid" translatable="false">sharedUserId is not system</string>
+    <string name="button_show" translatable="false">Show</string>
+
     <!-- power test -->
     <string name="power_request_shutdown" translatable="false">Request Shutdown</string>
     <string name="power_shutdown" translatable="false">Shutdown</string>
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
index cb10104..d751a43 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/KitchenSinkActivity.java
@@ -57,6 +57,7 @@
 import com.google.android.car.kitchensink.hvac.HvacTestFragment;
 import com.google.android.car.kitchensink.notification.NotificationFragment;
 import com.google.android.car.kitchensink.orientation.OrientationTestFragment;
+import com.google.android.car.kitchensink.packageinfo.PackageInfoFragment;
 import com.google.android.car.kitchensink.power.PowerTestFragment;
 import com.google.android.car.kitchensink.projection.ProjectionFragment;
 import com.google.android.car.kitchensink.property.PropertyTestFragment;
@@ -189,7 +190,8 @@
             new FragmentMenuEntry("volume test", VolumeTestFragment.class),
             new FragmentMenuEntry("vehicle ctrl", VehicleCtrlFragment.class),
             new FragmentMenuEntry("vehicle hal", VehicleHalFragment.class),
-            new FragmentMenuEntry("web links", WebLinksTestFragment.class)
+            new FragmentMenuEntry("web links", WebLinksTestFragment.class),
+            new FragmentMenuEntry("package info", PackageInfoFragment.class)
     );
 
     private Car mCarApi;
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/activityview/ActivityViewTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/activityview/ActivityViewTestFragment.java
index 1564061..40c470d 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/activityview/ActivityViewTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/activityview/ActivityViewTestFragment.java
@@ -16,7 +16,7 @@
 
 package com.google.android.car.kitchensink.activityview;
 
-import android.app.ActivityView;
+import android.car.app.CarActivityView;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.os.Bundle;
@@ -36,7 +36,7 @@
  * This fragment exercises the capabilities of virtual display.
  */
 public class ActivityViewTestFragment extends Fragment {
-    private ActivityView mActivityView;
+    private CarActivityView mActivityView;
     private ViewGroup mActivityViewParent;
 
     @Override
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/diagnostic/DiagnosticTestFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/diagnostic/DiagnosticTestFragment.java
index 177d36c..99164df 100644
--- a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/diagnostic/DiagnosticTestFragment.java
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/diagnostic/DiagnosticTestFragment.java
@@ -92,8 +92,15 @@
     }
 
     private void resumeDiagnosticManager() {
+        Car car = mActivity.getCar();
+        if (!car.isFeatureEnabled(Car.DIAGNOSTIC_SERVICE)) {
+            String notSupported = Car.DIAGNOSTIC_SERVICE + " not supported";
+            mLiveDiagnosticInfo.setText(notSupported);
+            mFreezeDiagnosticInfo.setText(notSupported);
+            return;
+        }
         mDiagnosticManager =
-                (CarDiagnosticManager) mActivity.getCar().getCarManager(Car.DIAGNOSTIC_SERVICE);
+                (CarDiagnosticManager) car.getCarManager(Car.DIAGNOSTIC_SERVICE);
         if (mLiveListener != null) {
             mDiagnosticManager.registerListener(mLiveListener,
                     CarDiagnosticManager.FRAME_TYPE_LIVE,
diff --git a/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/packageinfo/PackageInfoFragment.java b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/packageinfo/PackageInfoFragment.java
new file mode 100644
index 0000000..a43c510
--- /dev/null
+++ b/tests/EmbeddedKitchenSinkApp/src/com/google/android/car/kitchensink/packageinfo/PackageInfoFragment.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2020 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.google.android.car.kitchensink.packageinfo;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
+import android.os.Bundle;
+import android.os.UserManager;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import androidx.fragment.app.Fragment;
+
+import com.google.android.car.kitchensink.R;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Test fragment to check packages installed for each user
+ * <p>
+ * Options to apply conditions to filter out packages, if package
+ * <ul>
+ * <li>only have activities.
+ * <li>have service but not exported or for single user.
+ * <li>does not require any key permission
+ * (INTERACT_ACROSS_USERS, INTERACT_ACROSS_USERS_FULL, WRITE_DEVICE_CONFIG).
+ * <li>does not have sharedUserId or shardUserId is not system uid.
+ * </ul>
+ */
+public final class PackageInfoFragment extends Fragment{
+    private static final String TAG = "PackageInfoTest";
+    private static final boolean DEBUG = true;
+    private static final int PACKAGE_FLAGS = PackageManager.GET_META_DATA
+            | PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES
+            | PackageManager.GET_PERMISSIONS | PackageManager.GET_SIGNATURES;
+    private static final List<String> IMPORTANT_PERMISSIONS = Arrays.asList(
+            "android.permission.INTERACT_ACROSS_USERS",
+            "android.permission.INTERACT_ACROSS_USERS_FULL",
+            "android.permission.WRITE_DEVICE_CONFIG");
+    private static final String SYSTEM_UID = "android.uid.system";
+
+    private final List<PackageInfo> mPackagesToDisableForSystemUser = new ArrayList<>();
+
+    private UserManager mUserManager;
+    private PackageManager mPackageManager;
+    private UserInfo mUserToShow;
+    private int mShowFlags;
+    private TextView mPackageView;
+    private boolean mFilterActivities;
+    private boolean mFilterServices;
+    private boolean mFilterPermissions;
+    private boolean mFilterSharedUid;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
+        mPackageManager = getActivity().getPackageManager();
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance) {
+        View view = inflater.inflate(R.layout.package_info_test, container, false);
+        setListener(view);
+
+        return view;
+    }
+
+    private void refreshPackages() {
+        List<PackageInfo> packages = new ArrayList<PackageInfo>();
+        try {
+            packages = mPackageManager.getInstalledPackagesAsUser(PackageManager.GET_META_DATA
+                    | PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES
+                    | PackageManager.GET_PERMISSIONS | PackageManager.GET_SIGNATURES,
+                    mUserToShow.id);
+            if (DEBUG) {
+                Log.d(TAG, "total packages found: " + packages.size());
+            }
+        } catch (Exception e) {
+            if (DEBUG) {
+                Log.e(TAG, "failed to get packages for given user: " + mUserToShow);
+            }
+            mPackageView.setText("Cannot retrieve packages for this user..");
+            return;
+        }
+
+        mPackagesToDisableForSystemUser.clear();
+
+        for (PackageInfo packageInfo : packages) {
+            Log.d(TAG, "checking package: " + packageInfo);
+            boolean toBlacklist = true;
+            // check share user id, show package does not have sharedUserId or not system uid
+            if (mFilterSharedUid) {
+                if (DEBUG) {
+                    Log.d(TAG, "sharedUid flagged: " + (packageInfo.sharedUserId == null
+                                || !packageInfo.sharedUserId.equals(SYSTEM_UID)));
+                }
+
+                toBlacklist &= (packageInfo.sharedUserId == null
+                        || !packageInfo.sharedUserId.equals(SYSTEM_UID));
+            }
+
+            // check permissions, show package does not require selected permissions
+            if (mFilterPermissions && packageInfo.requestedPermissions != null) {
+                if (DEBUG) {
+                    for (String info : Arrays.asList(packageInfo.requestedPermissions)) {
+                        Log.d(TAG, info + " flagged: " + (!IMPORTANT_PERMISSIONS.contains(info)));
+                    }
+                }
+
+                toBlacklist &= !(Arrays.asList(packageInfo.requestedPermissions).stream().anyMatch(
+                        info -> IMPORTANT_PERMISSIONS.contains(info)));
+            }
+            // check services, w/o service or service not exported and w/o single user flag
+            if (mFilterServices && packageInfo.services != null) {
+                if (DEBUG) {
+                    for (ServiceInfo info : Arrays.asList(packageInfo.services)) {
+                        Log.d(TAG, info + " flagged: " + (!info.exported
+                                && (info.flags & ServiceInfo.FLAG_SINGLE_USER) == 0));
+                    }
+                }
+
+                toBlacklist &= Arrays.asList(packageInfo.services).stream().anyMatch(info ->
+                    !info.exported && (info.flags & ServiceInfo.FLAG_SINGLE_USER) == 0);
+            }
+            // check activities
+            if (mFilterActivities) {
+                if (DEBUG) {
+                    Log.d(TAG, packageInfo + " contain activities only, flagged: " + (
+                            packageInfo.activities != null
+                            && packageInfo.services == null
+                            && packageInfo.providers == null));
+                }
+                toBlacklist &= (packageInfo.activities != null
+                    && packageInfo.services == null && packageInfo.providers == null);
+            }
+
+            if (toBlacklist) {
+                mPackagesToDisableForSystemUser.add(packageInfo);
+            }
+        }
+    }
+
+    private void showPackagesOnView(TextView tv) {
+        refreshPackages();
+
+        tv.setText(mPackagesToDisableForSystemUser.size() + " Packages found ...\n");
+
+        for (PackageInfo info : mPackagesToDisableForSystemUser) {
+            tv.append(info.packageName.toString() + "\n");
+        }
+    }
+
+    private void setListener(View v) {
+        List<UserInfo> users = mUserManager.getUsers();
+        mUserToShow = users.get(0);
+        Spinner spinner = v.findViewById(R.id.spinner);
+        ArrayAdapter<UserInfo> userArrayAdapter = new ArrayAdapter<UserInfo>(
+                    getContext(), android.R.layout.simple_spinner_item, users);
+        userArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+        spinner.setAdapter(userArrayAdapter);
+        spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+            @Override
+            public void onItemSelected(AdapterView<?> parent, View currentView,
+                    int position, long id) {
+                mUserToShow = (UserInfo) parent.getItemAtPosition(position);
+            }
+            @Override
+            public void onNothingSelected(AdapterView<?> parent) {
+            }
+        });
+
+        CheckBox activities = v.findViewById(R.id.checkbox_activities);
+        CheckBox services = v.findViewById(R.id.checkbox_services);
+        CheckBox permissions = v.findViewById(R.id.checkbox_permissions);
+        CheckBox shareduid = v.findViewById(R.id.checkbox_shareduid);
+        Button showButton = v.findViewById(R.id.button_show);
+        TextView packageView = v.findViewById(R.id.packages);
+
+        activities.setOnClickListener(view -> mFilterActivities = ((CheckBox) view).isChecked());
+        services.setOnClickListener(view -> mFilterServices = ((CheckBox) view).isChecked());
+        permissions.setOnClickListener(view -> mFilterPermissions = ((CheckBox) view).isChecked());
+        shareduid.setOnClickListener(view -> mFilterSharedUid = ((CheckBox) view).isChecked());
+        showButton.setOnClickListener(view -> showPackagesOnView(packageView));
+    }
+}
diff --git a/tests/SecurityPermissionTest/src/com/android/car/CarPublicTest.java b/tests/SecurityPermissionTest/src/com/android/car/CarPublicTest.java
index 2284694..af1867b 100644
--- a/tests/SecurityPermissionTest/src/com/android/car/CarPublicTest.java
+++ b/tests/SecurityPermissionTest/src/com/android/car/CarPublicTest.java
@@ -55,11 +55,18 @@
         assertThrows(SecurityException.class, () -> mCar.getCarManager(Car.CAR_NAVIGATION_SERVICE));
         assertThrows(SecurityException.class, () -> mCar.getCarManager(
                 Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE));
-        assertThrows(SecurityException.class, () -> mCar.getCarManager(Car.DIAGNOSTIC_SERVICE));
+        if (mCar.isFeatureEnabled(Car.DIAGNOSTIC_SERVICE)) {
+            assertThrows(SecurityException.class, () -> mCar.getCarManager(Car.DIAGNOSTIC_SERVICE));
+        }
         assertThrows(SecurityException.class, () -> mCar.getCarManager(Car.POWER_SERVICE));
-        assertThrows(SecurityException.class, () -> mCar.getCarManager(Car.VMS_SUBSCRIBER_SERVICE));
+        if (mCar.isFeatureEnabled(Car.VMS_SUBSCRIBER_SERVICE)) {
+            assertThrows(SecurityException.class, () -> mCar.getCarManager(
+                    Car.VMS_SUBSCRIBER_SERVICE));
+        }
         assertThrows(SecurityException.class, () -> mCar.getCarManager(Car.TEST_SERVICE));
-        assertThrows(SecurityException.class, () -> mCar.getCarManager(
-                Car.STORAGE_MONITORING_SERVICE));
+        if (mCar.isFeatureEnabled(Car.STORAGE_MONITORING_SERVICE)) {
+            assertThrows(SecurityException.class, () -> mCar.getCarManager(
+                    Car.STORAGE_MONITORING_SERVICE));
+        }
     }
 }
diff --git a/tests/VmsSubscriberClientSample/src/com/google/android/car/vms/subscriber/VmsSubscriberClientSampleActivity.java b/tests/VmsSubscriberClientSample/src/com/google/android/car/vms/subscriber/VmsSubscriberClientSampleActivity.java
index 3730104..3faa93f 100644
--- a/tests/VmsSubscriberClientSample/src/com/google/android/car/vms/subscriber/VmsSubscriberClientSampleActivity.java
+++ b/tests/VmsSubscriberClientSample/src/com/google/android/car/vms/subscriber/VmsSubscriberClientSampleActivity.java
@@ -80,6 +80,10 @@
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
             Log.d(TAG, "Connected to Car Service");
+            if (!mCarApi.isFeatureEnabled(Car.VMS_SUBSCRIBER_SERVICE)) {
+                Log.e(TAG, "VMS not supported");
+                finish();
+            }
             mVmsSubscriberManager = getVmsSubscriberManager();
             configureSubscriptions(mVmsSubscriberManager);
         }
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarDiagnosticManagerTest.java b/tests/android_car_api_test/src/android/car/apitest/CarDiagnosticManagerTest.java
index 301ad87..12030bb 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarDiagnosticManagerTest.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarDiagnosticManagerTest.java
@@ -16,6 +16,8 @@
 
 package android.car.apitest;
 
+import static org.junit.Assume.assumeTrue;
+
 import android.car.Car;
 import android.car.diagnostic.CarDiagnosticEvent;
 import android.car.diagnostic.CarDiagnosticManager;
@@ -78,6 +80,7 @@
             InstrumentationRegistry.getInstrumentation().getContext(), mConnectionListener);
         mCar.connect();
         waitForConnection(DEFAULT_WAIT_TIMEOUT_MS);
+        assumeTrue(mCar.isFeatureEnabled(Car.DIAGNOSTIC_SERVICE));
         mCarDiagnosticManager = (CarDiagnosticManager) mCar.getCarManager(Car.DIAGNOSTIC_SERVICE);
         assertNotNull(mCarDiagnosticManager);
     }
diff --git a/tests/android_car_api_test/src/android/car/apitest/CarFeatureTest.java b/tests/android_car_api_test/src/android/car/apitest/CarFeatureTest.java
index 73c300b..46619bb 100644
--- a/tests/android_car_api_test/src/android/car/apitest/CarFeatureTest.java
+++ b/tests/android_car_api_test/src/android/car/apitest/CarFeatureTest.java
@@ -39,35 +39,33 @@
             Car.AUDIO_SERVICE,
             Car.BLUETOOTH_SERVICE,
             Car.CAR_BUGREPORT_SERVICE,
+            Car.CAR_CONFIGURATION_SERVICE,
             Car.CAR_DRIVING_STATE_SERVICE,
             Car.CAR_MEDIA_SERVICE,
             Car.CAR_NAVIGATION_SERVICE,
             Car.CAR_OCCUPANT_ZONE_SERVICE,
+            Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE,
             Car.CAR_USER_SERVICE,
+            Car.CAR_UX_RESTRICTION_SERVICE,
             Car.INFO_SERVICE,
             Car.PACKAGE_SERVICE,
             Car.POWER_SERVICE,
             Car.PROJECTION_SERVICE,
             Car.PROPERTY_SERVICE,
             Car.TEST_SERVICE,
-            // Deprecated, but still should be supported
-            Car.SENSOR_SERVICE,
+            // All items below here are deprecated, but still should be supported
             Car.CAR_INSTRUMENT_CLUSTER_SERVICE,
             Car.CABIN_SERVICE,
             Car.HVAC_SERVICE,
-            Car.VENDOR_EXTENSION_SERVICE,
-            // Candidate for Optional, but stay mandatory for now until final decision is made.
-            Car.CAR_CONFIGURATION_SERVICE,
-            Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE,
-            Car.DIAGNOSTIC_SERVICE,
-            Car.CAR_UX_RESTRICTION_SERVICE,
-            // Marked as optional, but requires additional work
-            Car.VMS_SUBSCRIBER_SERVICE
+            Car.SENSOR_SERVICE,
+            Car.VENDOR_EXTENSION_SERVICE
     );
 
     private static final List<String> OPTIONAL_FEATURES = Arrays.asList(
+            CarFeatures.FEATURE_CAR_USER_NOTICE_SERVICE,
+            Car.DIAGNOSTIC_SERVICE,
             Car.STORAGE_MONITORING_SERVICE,
-            CarFeatures.FEATURE_CAR_USER_NOTICE_SERVICE
+            Car.VMS_SUBSCRIBER_SERVICE
     );
 
     private static final String NON_EXISTING_FEATURE = "ThisFeatureDoesNotExist";
diff --git a/tests/carservice_test/res/raw/car_audio_configuration.xml b/tests/carservice_test/res/raw/car_audio_configuration.xml
index 4ae5217..2d7c609 100644
--- a/tests/carservice_test/res/raw/car_audio_configuration.xml
+++ b/tests/carservice_test/res/raw/car_audio_configuration.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
-<carAudioConfiguration version="1">
+<carAudioConfiguration version="2">
     <zones>
-        <zone name="primary zone" isPrimary="true">
+        <zone name="primary zone" isPrimary="true" audioZoneId="0" occupantZoneId="1">
             <volumeGroups>
                 <group>
                     <device address="bus0_media_out">
@@ -22,7 +22,7 @@
                 <display port="2"/>
             </displays>
         </zone>
-        <zone name="rear seat zone">
+        <zone name="rear seat zone" audioZoneId="2">
             <volumeGroups>
                 <group>
                     <device address="bus100_rear_seat">
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_V1.xml b/tests/carservice_test/res/raw/car_audio_configuration_V1.xml
new file mode 100644
index 0000000..4aaaa6c
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_V1.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<carAudioConfiguration version="1">
+    <zones>
+        <zone name="primary zone" isPrimary="true">
+            <volumeGroups>
+                <group>
+                    <device address="bus0_media_out">
+                        <context context="music"/>
+                    </device>
+                    <device address="bus3_call_ring_out">
+                        <context context="call_ring"/>
+                    </device>
+                </group>
+                <group>
+                    <device address="bus1_navigation_out">
+                        <context context="navigation"/>
+                    </device>
+                </group>
+            </volumeGroups>
+            <displays>
+                <display port="1"/>
+                <display port="2"/>
+            </displays>
+        </zone>
+        <zone name="rear seat zone">
+            <volumeGroups>
+                <group>
+                    <device address="bus100_rear_seat">
+                        <context context="music"/>
+                        <context context="navigation"/>
+                        <context context="voice_command"/>
+                        <context context="call_ring"/>
+                        <context context="call"/>
+                        <context context="alarm"/>
+                        <context context="notification"/>
+                        <context context="system_sound"/>
+                    </device>
+                </group>
+            </volumeGroups>
+        </zone>
+    </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_V1_with_audio_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_V1_with_audio_zone_id.xml
new file mode 100644
index 0000000..5793ab7
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_V1_with_audio_zone_id.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<carAudioConfiguration version="1">
+    <zones>
+        <zone name="primary zone" isPrimary="true" audioZoneId="0">
+            <volumeGroups>
+                <group>
+                    <device address="bus0_media_out">
+                        <context context="music"/>
+                    </device>
+                    <device address="bus3_call_ring_out">
+                        <context context="call_ring"/>
+                    </device>
+                </group>
+                <group>
+                    <device address="bus1_navigation_out">
+                        <context context="navigation"/>
+                    </device>
+                </group>
+            </volumeGroups>
+        </zone>
+        <zone name="rear seat zone">
+            <volumeGroups>
+                <group>
+                    <device address="bus100_rear_seat">
+                        <context context="music"/>
+                        <context context="navigation"/>
+                        <context context="voice_command"/>
+                        <context context="call_ring"/>
+                        <context context="call"/>
+                        <context context="alarm"/>
+                        <context context="notification"/>
+                        <context context="system_sound"/>
+                    </device>
+                </group>
+            </volumeGroups>
+        </zone>
+    </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_V1_with_occupant_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_V1_with_occupant_zone_id.xml
new file mode 100644
index 0000000..564a13c
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_V1_with_occupant_zone_id.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<carAudioConfiguration version="1">
+    <zones>
+        <zone name="primary zone" isPrimary="true" occupantZoneId="1">
+            <volumeGroups>
+                <group>
+                    <device address="bus0_media_out">
+                        <context context="music"/>
+                    </device>
+                    <device address="bus3_call_ring_out">
+                        <context context="call_ring"/>
+                    </device>
+                </group>
+                <group>
+                    <device address="bus1_navigation_out">
+                        <context context="navigation"/>
+                    </device>
+                </group>
+            </volumeGroups>
+        </zone>
+        <zone name="rear seat zone">
+            <volumeGroups>
+                <group>
+                    <device address="bus100_rear_seat">
+                        <context context="music"/>
+                        <context context="navigation"/>
+                        <context context="voice_command"/>
+                        <context context="call_ring"/>
+                        <context context="call"/>
+                        <context context="alarm"/>
+                        <context context="notification"/>
+                        <context context="system_sound"/>
+                    </device>
+                </group>
+            </volumeGroups>
+        </zone>
+    </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_duplicate_audio_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_duplicate_audio_zone_id.xml
new file mode 100644
index 0000000..2743c41
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_duplicate_audio_zone_id.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<carAudioConfiguration version="2">
+    <zones>
+        <zone name="primary zone" isPrimary="true" audioZoneId="0" occupantZoneId="1">
+            <volumeGroups>
+                <group>
+                    <device address="bus0_media_out">
+                        <context context="music"/>
+                    </device>
+                    <device address="bus3_call_ring_out">
+                        <context context="call_ring"/>
+                    </device>
+                </group>
+                <group>
+                    <device address="bus1_navigation_out">
+                        <context context="navigation"/>
+                    </device>
+                </group>
+            </volumeGroups>
+            <displays>
+                <display port="1"/>
+                <display port="2"/>
+            </displays>
+        </zone>
+        <zone name="rear seat zone" audioZoneId="1" occupantZoneId="2">
+            <volumeGroups>
+                <group>
+                    <device address="bus100_rear_seat">
+                        <context context="music"/>
+                        <context context="navigation"/>
+                        <context context="voice_command"/>
+                        <context context="call_ring"/>
+                        <context context="call"/>
+                        <context context="alarm"/>
+                        <context context="notification"/>
+                        <context context="system_sound"/>
+                    </device>
+                </group>
+            </volumeGroups>
+        </zone>
+        <zone name="rear seat zone 2" audioZoneId="1" occupantZoneId="3">
+            <volumeGroups>
+                <group>
+                    <device address="bus200_rear_seat">
+                        <context context="music"/>
+                        <context context="navigation"/>
+                        <context context="voice_command"/>
+                        <context context="call_ring"/>
+                        <context context="call"/>
+                        <context context="alarm"/>
+                        <context context="notification"/>
+                        <context context="system_sound"/>
+                    </device>
+                </group>
+            </volumeGroups>
+        </zone>
+    </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_duplicate_occupant_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_duplicate_occupant_zone_id.xml
new file mode 100644
index 0000000..613f6cd
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_duplicate_occupant_zone_id.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<carAudioConfiguration version="2">
+    <zones>
+        <zone name="primary zone" isPrimary="true" audioZoneId="0" occupantZoneId="1">
+            <volumeGroups>
+                <group>
+                    <device address="bus0_media_out">
+                        <context context="music"/>
+                    </device>
+                    <device address="bus3_call_ring_out">
+                        <context context="call_ring"/>
+                    </device>
+                </group>
+                <group>
+                    <device address="bus1_navigation_out">
+                        <context context="navigation"/>
+                    </device>
+                </group>
+            </volumeGroups>
+            <displays>
+                <display port="1"/>
+                <display port="2"/>
+            </displays>
+        </zone>
+        <zone name="rear seat zone" audioZoneId="2" occupantZoneId="1">
+            <volumeGroups>
+                <group>
+                    <device address="bus100_rear_seat">
+                        <context context="music"/>
+                        <context context="navigation"/>
+                        <context context="voice_command"/>
+                        <context context="call_ring"/>
+                        <context context="call"/>
+                        <context context="alarm"/>
+                        <context context="notification"/>
+                        <context context="system_sound"/>
+                    </device>
+                </group>
+            </volumeGroups>
+        </zone>
+    </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_empty_input_device.xml b/tests/carservice_test/res/raw/car_audio_configuration_empty_input_device.xml
new file mode 100644
index 0000000..d2ff43d
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_empty_input_device.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<carAudioConfiguration version="2">
+    <zones>
+        <zone name="primary zone" isPrimary="true" audioZoneId="0">
+            <volumeGroups>
+                <group>
+                    <device address="bus0_media_out">
+                        <context context="music"/>
+                    </device>
+                    <device address="bus3_call_ring_out">
+                        <context context="call_ring"/>
+                    </device>
+                </group>
+                <group>
+                    <device address="bus1_navigation_out">
+                        <context context="navigation"/>
+                    </device>
+                </group>
+            </volumeGroups>
+            <inputDevices>
+                <inputDevice address=""/>
+                <inputDevice address="Built-In Mic"/>
+            </inputDevices>
+        </zone>
+        <zone name="rear seat zone" audioZoneId="1">
+            <volumeGroups>
+                <group>
+                    <device address="bus100_rear_seat">
+                        <context context="music"/>
+                        <context context="navigation"/>
+                        <context context="voice_command"/>
+                        <context context="call_ring"/>
+                        <context context="call"/>
+                        <context context="alarm"/>
+                        <context context="notification"/>
+                        <context context="system_sound"/>
+                    </device>
+                </group>
+            </volumeGroups>
+            <inputDevices>
+                <inputDevice address="bus_1000_input"/>
+                <inputDevice address="Built-In Back Mic"/>
+            </inputDevices>
+        </zone>
+    </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_empty_occupant_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_empty_occupant_zone_id.xml
new file mode 100644
index 0000000..c4ef494
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_empty_occupant_zone_id.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<carAudioConfiguration version="2">
+    <zones>
+        <zone name="primary zone" isPrimary="true" audioZoneId="0" occupantZoneId="">
+            <volumeGroups>
+                <group>
+                    <device address="bus0_media_out">
+                        <context context="music"/>
+                    </device>
+                    <device address="bus3_call_ring_out">
+                        <context context="call_ring"/>
+                    </device>
+                </group>
+                <group>
+                    <device address="bus1_navigation_out">
+                        <context context="navigation"/>
+                    </device>
+                </group>
+            </volumeGroups>
+            <displays>
+                <display port="1"/>
+                <display port="2"/>
+            </displays>
+        </zone>
+        <zone name="rear seat zone" audioZoneId="2">
+            <volumeGroups>
+                <group>
+                    <device address="bus100_rear_seat">
+                        <context context="music"/>
+                        <context context="navigation"/>
+                        <context context="voice_command"/>
+                        <context context="call_ring"/>
+                        <context context="call"/>
+                        <context context="alarm"/>
+                        <context context="notification"/>
+                        <context context="system_sound"/>
+                    </device>
+                </group>
+            </volumeGroups>
+        </zone>
+    </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_missing_address.xml b/tests/carservice_test/res/raw/car_audio_configuration_missing_address.xml
new file mode 100644
index 0000000..7af8d68
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_missing_address.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<carAudioConfiguration version="2">
+    <zones>
+        <zone name="primary zone" isPrimary="true" audioZoneId="0">
+            <volumeGroups>
+                <group>
+                    <device address="bus0_media_out">
+                        <context context="music"/>
+                    </device>
+                    <device address="bus3_call_ring_out">
+                        <context context="call_ring"/>
+                    </device>
+                </group>
+                <group>
+                    <device address="bus1_navigation_out">
+                        <context context="navigation"/>
+                    </device>
+                </group>
+            </volumeGroups>
+            <inputDevices>
+                <inputDevice address="fm_tuner"/>
+                <inputDevice/>
+            </inputDevices>
+        </zone>
+        <zone name="rear seat zone" audioZoneId="1">
+            <volumeGroups>
+                <group>
+                    <device address="bus100_rear_seat">
+                        <context context="music"/>
+                        <context context="navigation"/>
+                        <context context="voice_command"/>
+                        <context context="call_ring"/>
+                        <context context="call"/>
+                        <context context="alarm"/>
+                        <context context="notification"/>
+                        <context context="system_sound"/>
+                    </device>
+                </group>
+            </volumeGroups>
+            <inputDevices>
+                <inputDevice address="bus_1000_input"/>
+                <inputDevice address="Built-In Back Mic"/>
+            </inputDevices>
+        </zone>
+    </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_negative_audio_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_negative_audio_zone_id.xml
new file mode 100644
index 0000000..3c202f3
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_negative_audio_zone_id.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<carAudioConfiguration version="2">
+    <zones>
+        <zone name="primary zone" isPrimary="true" audioZoneId="-1" occupantZoneId="1">
+            <volumeGroups>
+                <group>
+                    <device address="bus0_media_out">
+                        <context context="music"/>
+                    </device>
+                    <device address="bus3_call_ring_out">
+                        <context context="call_ring"/>
+                    </device>
+                </group>
+                <group>
+                    <device address="bus1_navigation_out">
+                        <context context="navigation"/>
+                    </device>
+                </group>
+            </volumeGroups>
+            <displays>
+                <display port="1"/>
+                <display port="2"/>
+            </displays>
+        </zone>
+        <zone name="rear seat zone" audioZoneId="2">
+            <volumeGroups>
+                <group>
+                    <device address="bus100_rear_seat">
+                        <context context="music"/>
+                        <context context="navigation"/>
+                        <context context="voice_command"/>
+                        <context context="call_ring"/>
+                        <context context="call"/>
+                        <context context="alarm"/>
+                        <context context="notification"/>
+                        <context context="system_sound"/>
+                    </device>
+                </group>
+            </volumeGroups>
+        </zone>
+    </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_negative_occupant_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_negative_occupant_zone_id.xml
new file mode 100644
index 0000000..ac240d6
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_negative_occupant_zone_id.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<carAudioConfiguration version="2">
+    <zones>
+        <zone name="primary zone" isPrimary="true" audioZoneId="0" occupantZoneId="-1">
+            <volumeGroups>
+                <group>
+                    <device address="bus0_media_out">
+                        <context context="music"/>
+                    </device>
+                    <device address="bus3_call_ring_out">
+                        <context context="call_ring"/>
+                    </device>
+                </group>
+                <group>
+                    <device address="bus1_navigation_out">
+                        <context context="navigation"/>
+                    </device>
+                </group>
+            </volumeGroups>
+            <displays>
+                <display port="1"/>
+                <display port="2"/>
+            </displays>
+        </zone>
+        <zone name="rear seat zone" audioZoneId="2">
+            <volumeGroups>
+                <group>
+                    <device address="bus100_rear_seat">
+                        <context context="music"/>
+                        <context context="navigation"/>
+                        <context context="voice_command"/>
+                        <context context="call_ring"/>
+                        <context context="call"/>
+                        <context context="alarm"/>
+                        <context context="notification"/>
+                        <context context="system_sound"/>
+                    </device>
+                </group>
+            </volumeGroups>
+        </zone>
+    </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_no_audio_zone_id_for_primary_zone.xml b/tests/carservice_test/res/raw/car_audio_configuration_no_audio_zone_id_for_primary_zone.xml
new file mode 100644
index 0000000..720e875
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_no_audio_zone_id_for_primary_zone.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<carAudioConfiguration version="2">
+    <zones>
+        <zone name="primary zone" isPrimary="true">
+            <volumeGroups>
+                <group>
+                    <device address="bus0_media_out">
+                        <context context="music"/>
+                    </device>
+                    <device address="bus3_call_ring_out">
+                        <context context="call_ring"/>
+                    </device>
+                </group>
+                <group>
+                    <device address="bus1_navigation_out">
+                        <context context="navigation"/>
+                    </device>
+                </group>
+            </volumeGroups>
+        </zone>
+        <zone name="rear seat zone" audioZoneId="2"
+              occupantZoneId="1">
+            <volumeGroups>
+                <group>
+                    <device address="bus100_rear_seat">
+                        <context context="music"/>
+                        <context context="navigation"/>
+                        <context context="voice_command"/>
+                        <context context="call_ring"/>
+                        <context context="call"/>
+                        <context context="alarm"/>
+                        <context context="notification"/>
+                        <context context="system_sound"/>
+                    </device>
+                </group>
+            </volumeGroups>
+        </zone>
+    </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_non_existent_input_device.xml b/tests/carservice_test/res/raw/car_audio_configuration_non_existent_input_device.xml
new file mode 100644
index 0000000..2f317d0
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_non_existent_input_device.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<carAudioConfiguration version="2">
+    <zones>
+        <zone name="primary zone" isPrimary="true">
+            <volumeGroups>
+                <group>
+                    <device address="bus0_media_out">
+                        <context context="music"/>
+                    </device>
+                    <device address="bus3_call_ring_out">
+                        <context context="call_ring"/>
+                    </device>
+                </group>
+                <group>
+                    <device address="bus1_navigation_out">
+                        <context context="navigation"/>
+                    </device>
+                </group>
+            </volumeGroups>
+            <inputDevices>
+                <inputDevice address="fm_tuner"/>
+                <inputDevice address="Built-In Mic"/>
+            </inputDevices>
+        </zone>
+        <zone name="rear seat zone" audioZoneId="1">
+            <volumeGroups>
+                <group>
+                    <device address="bus100_rear_seat">
+                        <context context="music"/>
+                        <context context="navigation"/>
+                        <context context="voice_command"/>
+                        <context context="call_ring"/>
+                        <context context="call"/>
+                        <context context="alarm"/>
+                        <context context="notification"/>
+                        <context context="system_sound"/>
+                    </device>
+                </group>
+            </volumeGroups>
+            <inputDevices>
+                <inputDevice address="MISSING_AUDIO_INPUT"/>
+                <inputDevice address="Built-In Back Mic"/>
+            </inputDevices>
+        </zone>
+    </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_non_numerical_audio_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_non_numerical_audio_zone_id.xml
new file mode 100644
index 0000000..2721b50
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_non_numerical_audio_zone_id.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<carAudioConfiguration version="2">
+    <zones>
+        <zone name="primary zone" isPrimary="true" audioZoneId="primary" occupantZoneId="1">
+            <volumeGroups>
+                <group>
+                    <device address="bus0_media_out">
+                        <context context="music"/>
+                    </device>
+                    <device address="bus3_call_ring_out">
+                        <context context="call_ring"/>
+                    </device>
+                </group>
+                <group>
+                    <device address="bus1_navigation_out">
+                        <context context="navigation"/>
+                    </device>
+                </group>
+            </volumeGroups>
+            <displays>
+                <display port="1"/>
+                <display port="2"/>
+            </displays>
+        </zone>
+        <zone name="rear seat zone" audioZoneId="2">
+            <volumeGroups>
+                <group>
+                    <device address="bus100_rear_seat">
+                        <context context="music"/>
+                        <context context="navigation"/>
+                        <context context="voice_command"/>
+                        <context context="call_ring"/>
+                        <context context="call"/>
+                        <context context="alarm"/>
+                        <context context="notification"/>
+                        <context context="system_sound"/>
+                    </device>
+                </group>
+            </volumeGroups>
+        </zone>
+    </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_non_numerical_occupant_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_non_numerical_occupant_zone_id.xml
new file mode 100644
index 0000000..85dd768
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_non_numerical_occupant_zone_id.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<carAudioConfiguration version="2">
+    <zones>
+        <zone name="primary zone" isPrimary="true" audioZoneId="0" occupantZoneId="one">
+            <volumeGroups>
+                <group>
+                    <device address="bus0_media_out">
+                        <context context="music"/>
+                    </device>
+                    <device address="bus3_call_ring_out">
+                        <context context="call_ring"/>
+                    </device>
+                </group>
+                <group>
+                    <device address="bus1_navigation_out">
+                        <context context="navigation"/>
+                    </device>
+                </group>
+            </volumeGroups>
+            <displays>
+                <display port="1"/>
+                <display port="2"/>
+            </displays>
+        </zone>
+        <zone name="rear seat zone" audioZoneId="2">
+            <volumeGroups>
+                <group>
+                    <device address="bus100_rear_seat">
+                        <context context="music"/>
+                        <context context="navigation"/>
+                        <context context="voice_command"/>
+                        <context context="call_ring"/>
+                        <context context="call"/>
+                        <context context="alarm"/>
+                        <context context="notification"/>
+                        <context context="system_sound"/>
+                    </device>
+                </group>
+            </volumeGroups>
+        </zone>
+    </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_non_primary_zone_with_primary_audio_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_non_primary_zone_with_primary_audio_zone_id.xml
new file mode 100644
index 0000000..d6889f6
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_non_primary_zone_with_primary_audio_zone_id.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<carAudioConfiguration version="2">
+    <zones>
+        <zone name="primary zone" isPrimary="true" occupantZoneId="1">
+            <volumeGroups>
+                <group>
+                    <device address="bus0_media_out">
+                        <context context="music"/>
+                    </device>
+                    <device address="bus3_call_ring_out">
+                        <context context="call_ring"/>
+                    </device>
+                </group>
+                <group>
+                    <device address="bus1_navigation_out">
+                        <context context="navigation"/>
+                    </device>
+                </group>
+            </volumeGroups>
+            <displays>
+                <display port="1"/>
+                <display port="2"/>
+            </displays>
+        </zone>
+        <zone name="rear seat zone" audioZoneId="0">
+            <volumeGroups>
+                <group>
+                    <device address="bus100_rear_seat">
+                        <context context="music"/>
+                        <context context="navigation"/>
+                        <context context="voice_command"/>
+                        <context context="call_ring"/>
+                        <context context="call"/>
+                        <context context="alarm"/>
+                        <context context="notification"/>
+                        <context context="system_sound"/>
+                    </device>
+                </group>
+            </volumeGroups>
+        </zone>
+    </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_primary_zone_with_non_zero_audio_zone_id.xml b/tests/carservice_test/res/raw/car_audio_configuration_primary_zone_with_non_zero_audio_zone_id.xml
new file mode 100644
index 0000000..ee0b268
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_primary_zone_with_non_zero_audio_zone_id.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<carAudioConfiguration version="2">
+    <zones>
+        <zone name="primary zone" isPrimary="true" audioZoneId="1" occupantZoneId="1">
+            <volumeGroups>
+                <group>
+                    <device address="bus0_media_out">
+                        <context context="music"/>
+                    </device>
+                    <device address="bus3_call_ring_out">
+                        <context context="call_ring"/>
+                    </device>
+                </group>
+                <group>
+                    <device address="bus1_navigation_out">
+                        <context context="navigation"/>
+                    </device>
+                </group>
+            </volumeGroups>
+            <displays>
+                <display port="1"/>
+                <display port="2"/>
+            </displays>
+        </zone>
+        <zone name="rear seat zone" audioZoneId="2">
+            <volumeGroups>
+                <group>
+                    <device address="bus100_rear_seat">
+                        <context context="music"/>
+                        <context context="navigation"/>
+                        <context context="voice_command"/>
+                        <context context="call_ring"/>
+                        <context context="call"/>
+                        <context context="alarm"/>
+                        <context context="notification"/>
+                        <context context="system_sound"/>
+                    </device>
+                </group>
+            </volumeGroups>
+        </zone>
+    </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_repeat_input_device.xml b/tests/carservice_test/res/raw/car_audio_configuration_repeat_input_device.xml
new file mode 100644
index 0000000..1934fe7
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_repeat_input_device.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<carAudioConfiguration version="2">
+    <zones>
+        <zone name="primary zone" isPrimary="true" audioZoneId="0">
+            <volumeGroups>
+                <group>
+                    <device address="bus0_media_out">
+                        <context context="music"/>
+                    </device>
+                    <device address="bus3_call_ring_out">
+                        <context context="call_ring"/>
+                    </device>
+                </group>
+                <group>
+                    <device address="bus1_navigation_out">
+                        <context context="navigation"/>
+                    </device>
+                </group>
+            </volumeGroups>
+            <inputDevices>
+                <inputDevice address="fm_tuner"/>
+                <inputDevice address="Built-In Mic"/>
+            </inputDevices>
+        </zone>
+        <zone name="rear seat zone" audioZoneId="1">
+            <volumeGroups>
+                <group>
+                    <device address="bus100_rear_seat">
+                        <context context="music"/>
+                        <context context="navigation"/>
+                        <context context="voice_command"/>
+                        <context context="call_ring"/>
+                        <context context="call"/>
+                        <context context="alarm"/>
+                        <context context="notification"/>
+                        <context context="system_sound"/>
+                    </device>
+                </group>
+            </volumeGroups>
+            <inputDevices>
+                <inputDevice address="fm_tuner"/>
+                <inputDevice address="Built-In Back Mic"/>
+            </inputDevices>
+        </zone>
+    </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/res/raw/car_audio_configuration_with_input_devices.xml b/tests/carservice_test/res/raw/car_audio_configuration_with_input_devices.xml
new file mode 100644
index 0000000..3b6a013
--- /dev/null
+++ b/tests/carservice_test/res/raw/car_audio_configuration_with_input_devices.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+
+<carAudioConfiguration version="2">
+    <zones>
+        <zone name="primary zone" isPrimary="true" audioZoneId="0">
+            <volumeGroups>
+                <group>
+                    <device address="bus0_media_out">
+                        <context context="music"/>
+                    </device>
+                    <device address="bus3_call_ring_out">
+                        <context context="call_ring"/>
+                    </device>
+                </group>
+                <group>
+                    <device address="bus1_navigation_out">
+                        <context context="navigation"/>
+                    </device>
+                </group>
+            </volumeGroups>
+            <inputDevices>
+                <inputDevice address="fm_tuner"/>
+                <inputDevice address="Built-In Mic"/>
+            </inputDevices>
+        </zone>
+        <zone name="rear seat zone" audioZoneId="1">
+            <volumeGroups>
+                <group>
+                    <device address="bus100_rear_seat">
+                        <context context="music"/>
+                        <context context="navigation"/>
+                        <context context="voice_command"/>
+                        <context context="call_ring"/>
+                        <context context="call"/>
+                        <context context="alarm"/>
+                        <context context="notification"/>
+                        <context context="system_sound"/>
+                    </device>
+                </group>
+            </volumeGroups>
+            <inputDevices>
+                <inputDevice address="bus_1000_input"/>
+                <inputDevice address="Built-In Back Mic"/>
+            </inputDevices>
+        </zone>
+    </zones>
+</carAudioConfiguration>
diff --git a/tests/carservice_test/src/com/android/car/CarDiagnosticManagerTest.java b/tests/carservice_test/src/com/android/car/CarDiagnosticManagerTest.java
index fbb7954..fb7eee6 100644
--- a/tests/carservice_test/src/com/android/car/CarDiagnosticManagerTest.java
+++ b/tests/carservice_test/src/com/android/car/CarDiagnosticManagerTest.java
@@ -204,6 +204,13 @@
     }
 
     @Override
+    protected synchronized void configureResourceOverrides(MockResources resources) {
+        super.configureResourceOverrides(resources);
+        resources.overrideResource(com.android.car.R.array.config_allowed_optional_car_features,
+                new String[]{Car.DIAGNOSTIC_SERVICE});
+    }
+
+    @Override
     protected synchronized void configureMockedHal() {
         java.util.Collection<Integer> numVendorSensors = Arrays.asList(0, 0);
         java.util.Collection<Integer> selectiveClear = Collections.singletonList(1);
diff --git a/tests/carservice_test/src/com/android/car/CarStorageMonitoringTest.java b/tests/carservice_test/src/com/android/car/CarStorageMonitoringTest.java
index 8ee1f85..1c7c9e9 100644
--- a/tests/carservice_test/src/com/android/car/CarStorageMonitoringTest.java
+++ b/tests/carservice_test/src/com/android/car/CarStorageMonitoringTest.java
@@ -371,6 +371,8 @@
     @Override
     protected synchronized void configureResourceOverrides(MockResources resources) {
         super.configureResourceOverrides(resources);
+        resources.overrideResource(com.android.car.R.array.config_allowed_optional_car_features,
+                new String[] {Car.STORAGE_MONITORING_SERVICE});
         final ResourceOverrides overrides = PER_TEST_RESOURCES.getOrDefault(getName(), null);
         if (overrides != null) {
             overrides.overrideResources(resources);
diff --git a/tests/carservice_test/src/com/android/car/MockedVmsTestBase.java b/tests/carservice_test/src/com/android/car/MockedVmsTestBase.java
index f31e467..17ec017 100644
--- a/tests/carservice_test/src/com/android/car/MockedVmsTestBase.java
+++ b/tests/carservice_test/src/com/android/car/MockedVmsTestBase.java
@@ -63,6 +63,8 @@
     @Override
     protected synchronized void configureResourceOverrides(MockResources resources) {
         super.configureResourceOverrides(resources);
+        resources.overrideResource(com.android.car.R.array.config_allowed_optional_car_features,
+                new String[] {Car.VMS_SUBSCRIBER_SERVICE});
         // Override publisher client endpoint configurations
         // Both lists must be set, but only one will be used (see setUp)
         resources.overrideResource(com.android.car.R.array.vmsPublisherSystemClients,
diff --git a/tests/carservice_test/src/com/android/car/VmsPublisherClientServiceTest.java b/tests/carservice_test/src/com/android/car/VmsPublisherClientServiceTest.java
index 46907f0..2b1129d 100644
--- a/tests/carservice_test/src/com/android/car/VmsPublisherClientServiceTest.java
+++ b/tests/carservice_test/src/com/android/car/VmsPublisherClientServiceTest.java
@@ -27,7 +27,6 @@
 import android.hardware.automotive.vehicle.V2_0.VmsMessageWithLayerIntegerValuesIndex;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.FlakyTest;
 import androidx.test.filters.MediumTest;
 
 import org.junit.Test;
@@ -47,8 +46,7 @@
     public static final byte[] PAYLOAD = new byte[]{1, 1, 2, 3, 5, 8, 13};
 
     @Test
-    @FlakyTest
-    public void testPublish() throws Exception {
+    public void testPublish() {
         MockHalClient client = getMockHalClient();
         client.sendMessage(
                 VmsMessageType.SUBSCRIBE,
@@ -56,6 +54,7 @@
                 MOCK_PUBLISHER_LAYER_SUBTYPE,
                 MOCK_PUBLISHER_LAYER_VERSION);
 
+        getMockPublisherClient().receiveSubscriptionState(); // Wait for subscription to propagate
         getMockPublisherClient().publish(MOCK_PUBLISHER_LAYER, MOCK_PUBLISHER_ID, PAYLOAD);
 
         VehiclePropValue message;
diff --git a/tests/carservice_test/src/com/android/car/audio/CarAudioZonesHelperTest.java b/tests/carservice_test/src/com/android/car/audio/CarAudioZonesHelperTest.java
index c87ad94..614d941 100644
--- a/tests/carservice_test/src/com/android/car/audio/CarAudioZonesHelperTest.java
+++ b/tests/carservice_test/src/com/android/car/audio/CarAudioZonesHelperTest.java
@@ -15,16 +15,24 @@
  */
 package com.android.car.audio;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.TestCase.fail;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
+import static org.testng.Assert.expectThrows;
 
+import android.car.media.CarAudioManager;
 import android.content.Context;
 import android.hardware.automotive.audiocontrol.V1_0.ContextNumber;
+import android.media.AudioDeviceAddress;
+import android.media.AudioDeviceInfo;
+import android.util.SparseIntArray;
 import android.view.DisplayAddress;
 
 import androidx.test.core.app.ApplicationProvider;
@@ -38,16 +46,18 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mockito;
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.Arrays;
 import java.util.List;
+import java.util.stream.Collectors;
 
 @RunWith(AndroidJUnit4.class)
 public class CarAudioZonesHelperTest {
-    private List<CarAudioDeviceInfo> mCarAudioDeviceInfos;
+    private List<CarAudioDeviceInfo> mCarAudioOutputDeviceInfos;
+    private AudioDeviceInfo[] mInputAudioDeviceInfos;
     private Context mContext;
     private InputStream mInputStream;
     private static final String BUS_0_ADDRESS = "bus0_media_out";
@@ -55,9 +65,18 @@
     private static final String BUS_3_ADDRESS = "bus3_call_ring_out";
     private static final String BUS_100_ADDRESS = "bus100_rear_seat";
 
+    private static final String PRIMARY_ZONE_MICROPHONE_ADDRESS = "Built-In Mic";
+    private static final String PRIMARY_ZONE_FM_TUNER_ADDRESS = "fm_tuner";
+    private static final String SECONDARY_ZONE_BACK_MICROPHONE_ADDRESS = "Built-In Back Mic";
+    private static final String SECONDARY_ZONE_BUS_1000_INPUT_ADDRESS = "bus_1000_input";
+
+    private static final int PRIMARY_OCCUPANT_ID = 1;
+    private static final int SECONDARY_ZONE_ID = 2;
+
     @Before
     public void setUp() {
-        mCarAudioDeviceInfos = generateCarDeviceInfos();
+        mCarAudioOutputDeviceInfos = generateCarDeviceInfos();
+        mInputAudioDeviceInfos = generateInputDeviceInfos();
         mContext = ApplicationProvider.getApplicationContext();
         mInputStream = mContext.getResources().openRawResource(R.raw.car_audio_configuration);
     }
@@ -79,12 +98,24 @@
                 generateCarAudioDeviceInfo(""),
                 generateCarAudioDeviceInfo(null),
                 generateCarAudioDeviceInfo(null)
-
         );
     }
 
+    private AudioDeviceInfo[] generateInputDeviceInfos() {
+        return new AudioDeviceInfo[] {
+                generateInputAudioDeviceInfo(PRIMARY_ZONE_MICROPHONE_ADDRESS,
+                        AudioDeviceInfo.TYPE_BUILTIN_MIC),
+                generateInputAudioDeviceInfo(PRIMARY_ZONE_FM_TUNER_ADDRESS,
+                        AudioDeviceInfo.TYPE_FM_TUNER),
+                generateInputAudioDeviceInfo(SECONDARY_ZONE_BACK_MICROPHONE_ADDRESS,
+                        AudioDeviceInfo.TYPE_BUS),
+                generateInputAudioDeviceInfo(SECONDARY_ZONE_BUS_1000_INPUT_ADDRESS,
+                        AudioDeviceInfo.TYPE_BUILTIN_MIC)
+        };
+    }
+
     private CarAudioDeviceInfo generateCarAudioDeviceInfo(String address) {
-        CarAudioDeviceInfo cadiMock = Mockito.mock(CarAudioDeviceInfo.class);
+        CarAudioDeviceInfo cadiMock = mock(CarAudioDeviceInfo.class);
         when(cadiMock.getStepValue()).thenReturn(1);
         when(cadiMock.getDefaultGain()).thenReturn(2);
         when(cadiMock.getMaxGain()).thenReturn(5);
@@ -93,20 +124,72 @@
         return cadiMock;
     }
 
+    private AudioDeviceInfo generateInputAudioDeviceInfo(String address, int type) {
+        AudioDeviceInfo inputMock = mock(AudioDeviceInfo.class);
+        when(inputMock.getAddress()).thenReturn(address);
+        when(inputMock.getType()).thenReturn(type);
+        when(inputMock.isSource()).thenReturn(true);
+        when(inputMock.isSink()).thenReturn(false);
+        return inputMock;
+    }
+
     @Test
-    public void loadAudioZones_parsesAllZones() throws IOException, XmlPullParserException {
+    public void loadAudioZones_parsesAllZones() throws Exception {
         CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, mInputStream,
-                mCarAudioDeviceInfos);
+                mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+
+        CarAudioZone[] zones = cazh.loadAudioZones();
+
+        assertThat(zones.length).isEqualTo(2);
+    }
+
+    @Test
+    public void loadAudioZones_versionOneParsesAllZones() throws Exception {
+        try (InputStream versionOneStream = mContext.getResources().openRawResource(
+                R.raw.car_audio_configuration_V1)) {
+            CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, versionOneStream,
+                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+
+            CarAudioZone[] zones = cazh.loadAudioZones();
+
+            assertThat(zones.length).isEqualTo(2);
+        }
+    }
+
+    @Test
+    public void loadAudioZones_parsesAudioZoneId() throws Exception {
+        CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, mInputStream,
+                mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+
+        CarAudioZone[] zones = cazh.loadAudioZones();
+
+        List<Integer> zoneIds = getListOfZoneIds(zones);
+        assertThat(zoneIds.size()).isEqualTo(2);
+        assertThat(zoneIds)
+                .containsAllOf(CarAudioManager.PRIMARY_AUDIO_ZONE, SECONDARY_ZONE_ID).inOrder();
+    }
+
+    @Test
+    public void loadAudioZones_parsesOccupantZoneId() throws Exception {
+        CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, mInputStream,
+                mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
 
         CarAudioZone[] zones = cazh.loadAudioZones();
 
         assertEquals(2, zones.length);
+
+        SparseIntArray audioZoneIdToOccupantZoneIdMapping =
+                cazh.getCarAudioZoneIdToOccupantZoneIdMapping();
+        assertThat(audioZoneIdToOccupantZoneIdMapping.get(CarAudioManager.PRIMARY_AUDIO_ZONE))
+                .isEqualTo(PRIMARY_OCCUPANT_ID);
+        assertThat(audioZoneIdToOccupantZoneIdMapping.get(SECONDARY_ZONE_ID, -1))
+                .isEqualTo(-1);
     }
 
     @Test
     public void loadAudioZones_parsesZoneName() throws IOException, XmlPullParserException {
         CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, mInputStream,
-                mCarAudioDeviceInfos);
+                mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
 
         CarAudioZone[] zones = cazh.loadAudioZones();
 
@@ -117,7 +200,7 @@
     @Test
     public void loadAudioZones_parsesIsPrimary() throws IOException, XmlPullParserException {
         CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, mInputStream,
-                mCarAudioDeviceInfos);
+                mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
 
         CarAudioZone[] zones = cazh.loadAudioZones();
 
@@ -131,7 +214,7 @@
     @Test
     public void loadAudioZones_parsesVolumeGroups() throws IOException, XmlPullParserException {
         CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, mInputStream,
-                mCarAudioDeviceInfos);
+                mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
 
         CarAudioZone[] zones = cazh.loadAudioZones();
 
@@ -142,7 +225,7 @@
     @Test
     public void loadAudioZones_parsesAddresses() throws IOException, XmlPullParserException {
         CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, mInputStream,
-                mCarAudioDeviceInfos);
+                mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
 
         CarAudioZone[] zones = cazh.loadAudioZones();
 
@@ -157,7 +240,7 @@
     @Test
     public void loadAudioZones_parsesContexts() throws IOException, XmlPullParserException {
         CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, mInputStream,
-                mCarAudioDeviceInfos);
+                mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
 
         CarAudioZone[] zones = cazh.loadAudioZones();
 
@@ -179,7 +262,7 @@
     public void loadAudioZones_parsesPhysicalDisplayAddresses()
             throws IOException, XmlPullParserException {
         CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, mInputStream,
-                mCarAudioDeviceInfos);
+                mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
 
         CarAudioZone[] zones = cazh.loadAudioZones();
 
@@ -194,7 +277,7 @@
     public void loadAudioZones_defaultsDisplayAddressesToEmptyList()
             throws IOException, XmlPullParserException {
         CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, mInputStream,
-                mCarAudioDeviceInfos);
+                mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
 
         CarAudioZone[] zones = cazh.loadAudioZones();
 
@@ -208,7 +291,7 @@
         try (InputStream duplicatePortStream = mContext.getResources().openRawResource(
                 R.raw.car_audio_configuration_duplicate_ports)) {
             CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, duplicatePortStream,
-                    mCarAudioDeviceInfos);
+                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
 
             cazh.loadAudioZones();
         }
@@ -220,7 +303,7 @@
         try (InputStream duplicatePortStream = mContext.getResources().openRawResource(
                 R.raw.car_audio_configuration_non_numerical_port)) {
             CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, duplicatePortStream,
-                    mCarAudioDeviceInfos);
+                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
 
             try {
                 cazh.loadAudioZones();
@@ -230,4 +313,258 @@
             }
         }
     }
+
+    @Test
+    public void loadAudioZones_passesOnMissingAudioZoneIdForPrimary() throws Exception {
+        try (InputStream missingAudioZoneIdStream = mContext.getResources().openRawResource(
+                R.raw.car_audio_configuration_no_audio_zone_id_for_primary_zone)) {
+            CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, missingAudioZoneIdStream,
+                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+
+            CarAudioZone[] zones = cazh.loadAudioZones();
+
+            List<Integer> zoneIds = getListOfZoneIds(zones);
+            assertThat(zoneIds.size()).isEqualTo(2);
+            assertThat(zoneIds).contains(CarAudioManager.PRIMARY_AUDIO_ZONE);
+            assertThat(zoneIds).contains(SECONDARY_ZONE_ID);
+        }
+    }
+
+    @Test
+    public void loadAudioZones_versionOneFailsOnAudioZoneId() throws Exception {
+        try (InputStream versionOneAudioZoneIdStream = mContext.getResources().openRawResource(
+                R.raw.car_audio_configuration_V1_with_audio_zone_id)) {
+            CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext,
+                    versionOneAudioZoneIdStream, mCarAudioOutputDeviceInfos,
+                    mInputAudioDeviceInfos);
+            IllegalArgumentException thrown =
+                    expectThrows(IllegalArgumentException.class,
+                            () -> cazh.loadAudioZones());
+            assertThat(thrown).hasMessageThat().contains("Invalid audio attribute audioZoneId");
+        }
+    }
+
+    @Test
+    public void loadAudioZones_versionOneFailsOnOccupantZoneId() throws Exception {
+        try (InputStream versionOneOccupantIdStream = mContext.getResources().openRawResource(
+                R.raw.car_audio_configuration_V1_with_occupant_zone_id)) {
+            CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, versionOneOccupantIdStream,
+                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+            IllegalArgumentException thrown =
+                    expectThrows(IllegalArgumentException.class,
+                            () -> cazh.loadAudioZones());
+            assertThat(thrown).hasMessageThat().contains("Invalid audio attribute occupantZoneId");
+        }
+    }
+
+    @Test
+    public void loadAudioZones_parsesInputDevices() throws Exception {
+        try (InputStream inputDevicesStream = mContext.getResources().openRawResource(
+                R.raw.car_audio_configuration_with_input_devices)) {
+            CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, inputDevicesStream,
+                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+
+            CarAudioZone[] zones = cazh.loadAudioZones();
+
+            CarAudioZone primaryZone = zones[0];
+            List<AudioDeviceAddress> primaryZoneInputDevices =
+                    primaryZone.getInputAudioDeviceAddresses();
+            assertThat(primaryZoneInputDevices).hasSize(2);
+
+            List<String> primaryZoneInputAddresses =
+                    primaryZoneInputDevices.stream().map(a ->a.getAddress()).collect(
+                            Collectors.toList());
+            assertThat(primaryZoneInputAddresses).containsAllOf(PRIMARY_ZONE_FM_TUNER_ADDRESS,
+                    PRIMARY_ZONE_MICROPHONE_ADDRESS).inOrder();
+
+            CarAudioZone secondaryZone = zones[1];
+            List<AudioDeviceAddress> secondaryZoneInputDevices =
+                    secondaryZone.getInputAudioDeviceAddresses();
+            List<String> secondaryZoneInputAddresses =
+                    secondaryZoneInputDevices.stream().map(a ->a.getAddress()).collect(
+                            Collectors.toList());
+            assertThat(secondaryZoneInputAddresses).containsAllOf(
+                    SECONDARY_ZONE_BUS_1000_INPUT_ADDRESS,
+                    SECONDARY_ZONE_BACK_MICROPHONE_ADDRESS).inOrder();
+        }
+    }
+
+    @Test
+    public void loadAudioZones_failsOnDuplicateOccupantZoneId() throws Exception {
+        try (InputStream duplicateOccupantZoneIdStream = mContext.getResources().openRawResource(
+                R.raw.car_audio_configuration_duplicate_occupant_zone_id)) {
+            CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext,
+                    duplicateOccupantZoneIdStream, mCarAudioOutputDeviceInfos,
+                    mInputAudioDeviceInfos);
+            IllegalArgumentException thrown =
+                    expectThrows(IllegalArgumentException.class,
+                            () -> cazh.loadAudioZones());
+            assertThat(thrown).hasMessageThat().contains("already associated with a zone");
+        }
+    }
+
+    @Test
+    public void loadAudioZones_failsOnDuplicateAudioZoneId() throws Exception {
+        try (InputStream duplicateAudioZoneIdStream = mContext.getResources().openRawResource(
+                R.raw.car_audio_configuration_duplicate_audio_zone_id)) {
+            CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, duplicateAudioZoneIdStream,
+                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+            IllegalArgumentException thrown =
+                    expectThrows(IllegalArgumentException.class,
+                            () -> cazh.loadAudioZones());
+            assertThat(thrown).hasMessageThat().contains("already associated with a zone");
+        }
+    }
+    @Test
+    public void loadAudioZones_failsOnEmptyInputDeviceAddress() throws Exception {
+        try (InputStream inputDevicesStream = mContext.getResources().openRawResource(
+                R.raw.car_audio_configuration_empty_input_device)) {
+            CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, inputDevicesStream,
+                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+
+            IllegalArgumentException thrown =
+                    expectThrows(IllegalArgumentException.class,
+                            () -> cazh.loadAudioZones());
+            assertThat(thrown).hasMessageThat().contains("empty.");
+        }
+    }
+
+    @Test
+    public void loadAudioZones_failsOnNonNumericalAudioZoneId() throws Exception {
+        try (InputStream nonNumericalStream = mContext.getResources().openRawResource(
+                R.raw.car_audio_configuration_non_numerical_audio_zone_id)) {
+            CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, nonNumericalStream,
+                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+            IllegalArgumentException thrown =
+                    expectThrows(IllegalArgumentException.class,
+                            () -> cazh.loadAudioZones());
+            assertThat(thrown).hasMessageThat().contains("was \"primary\" instead.");
+        }
+    }
+
+    @Test
+    public void loadAudioZones_failsOnNegativeAudioZoneId() throws Exception {
+        try (InputStream negativeAudioZoneIdStream = mContext.getResources().openRawResource(
+                R.raw.car_audio_configuration_negative_audio_zone_id)) {
+            CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, negativeAudioZoneIdStream,
+                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+            IllegalArgumentException thrown =
+                    expectThrows(IllegalArgumentException.class,
+                            () -> cazh.loadAudioZones());
+            assertThat(thrown).hasMessageThat().contains("but was \"-1\" instead.");
+        }
+    }
+
+    @Test
+    public void loadAudioZones_failsOnMissingInputDevice() throws Exception {
+        try (InputStream inputDevicesStream = mContext.getResources().openRawResource(
+                R.raw.car_audio_configuration_missing_address)) {
+            CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, inputDevicesStream,
+                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+
+            NullPointerException thrown =
+                    expectThrows(NullPointerException.class,
+                            () -> cazh.loadAudioZones());
+            assertThat(thrown).hasMessageThat().contains("present.");
+        }
+    }
+
+    @Test
+    public void loadAudioZones_failsOnNonNumericalOccupantZoneId() throws Exception {
+        try (InputStream nonNumericalStream = mContext.getResources().openRawResource(
+                R.raw.car_audio_configuration_non_numerical_occupant_zone_id)) {
+            CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, nonNumericalStream,
+                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+            IllegalArgumentException thrown =
+                    expectThrows(IllegalArgumentException.class,
+                            () -> cazh.loadAudioZones());
+            assertThat(thrown).hasMessageThat().contains("was \"one\" instead.");
+        }
+    }
+
+    @Test
+    public void loadAudioZones_failsOnNegativeOccupantZoneId() throws Exception {
+        try (InputStream negativeOccupantZoneIdStream = mContext.getResources().openRawResource(
+                R.raw.car_audio_configuration_negative_occupant_zone_id)) {
+            CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext,
+                    negativeOccupantZoneIdStream, mCarAudioOutputDeviceInfos,
+                    mInputAudioDeviceInfos);
+            IllegalArgumentException thrown =
+                    expectThrows(IllegalArgumentException.class,
+                            () -> cazh.loadAudioZones());
+            assertThat(thrown).hasMessageThat().contains("was \"-1\" instead.");
+        }
+    }
+
+    @Test
+    public void loadAudioZones_failsOnNonExistentInputDevice() throws Exception {
+        try (InputStream inputDevicesStream = mContext.getResources().openRawResource(
+                R.raw.car_audio_configuration_non_existent_input_device)) {
+            CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, inputDevicesStream,
+                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+
+            IllegalArgumentException thrown =
+                    expectThrows(IllegalArgumentException.class,
+                            () -> cazh.loadAudioZones());
+            assertThat(thrown).hasMessageThat().contains("does not exist");
+        }
+    }
+
+    @Test
+    public void loadAudioZones_failsOnEmptyOccupantZoneId() throws Exception {
+        try (InputStream emptyOccupantZoneIdStream = mContext.getResources().openRawResource(
+                R.raw.car_audio_configuration_empty_occupant_zone_id)) {
+            CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, emptyOccupantZoneIdStream,
+                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+            IllegalArgumentException thrown =
+                    expectThrows(IllegalArgumentException.class,
+                            () -> cazh.loadAudioZones());
+            assertThat(thrown).hasMessageThat().contains("but was \"\" instead.");
+        }
+    }
+
+    @Test
+    public void loadAudioZones_failsOnNonZeroAudioZoneIdForPrimary() throws Exception {
+        try (InputStream nonZeroForPrimaryStream = mContext.getResources().openRawResource(
+                R.raw.car_audio_configuration_primary_zone_with_non_zero_audio_zone_id)) {
+            CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, nonZeroForPrimaryStream,
+                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+            IllegalArgumentException thrown =
+                    expectThrows(IllegalArgumentException.class,
+                            () -> cazh.loadAudioZones());
+            assertThat(thrown).hasMessageThat().contains("it can be left empty.");
+        }
+    }
+
+    @Test
+    public void loadAudioZones_failsOnZeroAudioZoneIdForSecondary() throws Exception {
+        try (InputStream zeroZoneIdForSecondaryStream = mContext.getResources().openRawResource(
+                R.raw.car_audio_configuration_non_primary_zone_with_primary_audio_zone_id)) {
+            CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext,
+                    zeroZoneIdForSecondaryStream, mCarAudioOutputDeviceInfos,
+                    mInputAudioDeviceInfos);
+            IllegalArgumentException thrown =
+                    expectThrows(IllegalArgumentException.class,
+                            () -> cazh.loadAudioZones());
+            assertThat(thrown).hasMessageThat().contains("for primary zone");
+        }
+    }
+
+    @Test
+    public void loadAudioZones_failsOnRepeatedInputDevice() throws Exception {
+        try (InputStream inputDevicesStream = mContext.getResources().openRawResource(
+                R.raw.car_audio_configuration_repeat_input_device)) {
+            CarAudioZonesHelper cazh = new CarAudioZonesHelper(mContext, inputDevicesStream,
+                    mCarAudioOutputDeviceInfos, mInputAudioDeviceInfos);
+
+            IllegalArgumentException thrown =
+                    expectThrows(IllegalArgumentException.class,
+                            () -> cazh.loadAudioZones());
+            assertThat(thrown).hasMessageThat().contains("can not repeat.");
+        }
+    }
+
+    private List<Integer> getListOfZoneIds(CarAudioZone[] zones) {
+        return Arrays.stream(zones).map(CarAudioZone::getId).collect(Collectors.toList());
+    }
 }
diff --git a/tests/carservice_unit_test/src/com/android/car/CarPowerManagementServiceTest.java b/tests/carservice_unit_test/src/com/android/car/CarPowerManagementServiceTest.java
index 6269f54..35a4b3b 100644
--- a/tests/carservice_unit_test/src/com/android/car/CarPowerManagementServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/CarPowerManagementServiceTest.java
@@ -20,7 +20,9 @@
 import static android.os.UserHandle.USER_SYSTEM;
 import static android.os.UserManager.USER_TYPE_FULL_GUEST;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -31,7 +33,6 @@
 import static org.mockito.ArgumentMatchers.notNull;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import static java.lang.annotation.ElementType.METHOD;
@@ -66,7 +67,6 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TestRule;
@@ -75,6 +75,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoSession;
+import org.mockito.invocation.InvocationOnMock;
 import org.mockito.junit.MockitoJUnitRunner;
 import org.mockito.quality.Strictness;
 
@@ -84,6 +85,8 @@
 import java.lang.annotation.Target;
 import java.lang.reflect.Method;
 import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
@@ -123,6 +126,10 @@
     // Value used to set config_disableUserSwitchDuringResume - must be defined before initTest();
     private boolean mDisableUserSwitchDuringResume;
 
+    // Tracks Log.wtf() calls made during code execution / used on verifyWtfNeverLogged()
+    // TODO: move mechanism to common code / custom Rule
+    private final List<UnsupportedOperationException> mWtfs = new ArrayList<>();
+
     @Rule
     public final TestRule setWakeupTimeRule = new TestWatcher() {
         protected void starting(Description description) {
@@ -145,6 +152,7 @@
         mSession = mockitoSession()
                 .strictness(Strictness.LENIENT)
                 .spyStatic(ActivityManager.class)
+                .spyStatic(Log.class)
                 .startMocking();
         mPowerHal = new MockedPowerHalService(true /*isPowerStateSupported*/,
                 true /*isDeepSleepAllowed*/, true /*isTimedWakeupAllowed*/);
@@ -153,6 +161,12 @@
             .withSystemStateInterface(mSystemStateInterface)
             .withWakeLockInterface(mWakeLockInterface)
             .withIOInterface(mIOInterface).build();
+        doAnswer((invocation) -> {
+            return addWtf(invocation);
+        }).when(() -> Log.wtf(anyString(), anyString()));
+        doAnswer((invocation) -> {
+            return addWtf(invocation);
+        }).when(() -> Log.wtf(anyString(), anyString(), notNull()));
     }
 
     @After
@@ -164,6 +178,14 @@
         mSession.finishMocking();
     }
 
+
+    private Object addWtf(InvocationOnMock invocation) {
+        String message = "Called " + invocation;
+        Log.d(TAG, message); // Log always, as some test expect it
+        mWtfs.add(new UnsupportedOperationException(message));
+        return null;
+    }
+
     /**
      * Helper method to create mService and initialize a test case
      */
@@ -190,9 +212,21 @@
         assertStateReceived(MockedPowerHalService.SET_WAIT_FOR_VHAL, 0);
     }
 
+    /**
+     * Same as {@link #initTest()}, but it also assumes the current and initial users are user 10.
+     */
+    private void initTestForUser10() throws Exception {
+        initTest();
+        setUserInfo(10, NO_USER_INFO_FLAGS);
+        setCurrentUser(10);
+        setInitialUser(10);
+    }
+
     @Test
     public void testBootComplete() throws Exception {
         initTest();
+
+        verifyWtfNeverLogged();
     }
 
     @Test
@@ -206,12 +240,13 @@
 
         // display should be turned on as it started with off state.
         assertThat(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS)).isTrue();
+
+        verifyWtfNeverLogged();
     }
 
-    @Ignore("Disabled until b/147846930 is fixed")
     @Test
     public void testShutdown() throws Exception {
-        initTest();
+        initTestForUser10();
 
         // Transition to ON state
         mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.ON, 0));
@@ -227,11 +262,13 @@
         assertThat(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS)).isFalse();
         mPowerSignalListener.waitForShutdown(WAIT_TIMEOUT_MS);
         mSystemStateInterface.waitForShutdown(WAIT_TIMEOUT_MS);
+
+        verifyWtfNeverLogged();
     }
 
     @Test
     public void testSuspend() throws Exception {
-        initTest();
+        initTestForUser10();
 
         // Start in the ON state
         mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.ON, 0));
@@ -244,11 +281,13 @@
         // Verify suspend
         assertStateReceivedForShutdownOrSleepWithPostpone(
                 PowerHalService.SET_DEEP_SLEEP_ENTRY, WAIT_TIMEOUT_LONG_MS, mWakeupTime);
+
+        verifyWtfNeverLogged();
     }
 
     @Test
     public void testShutdownOnSuspend() throws Exception {
-        initTest();
+        initTestForUser10();
 
         // Start in the ON state
         mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.ON, 0));
@@ -280,11 +319,12 @@
         // Verify suspend
         assertStateReceivedForShutdownOrSleepWithPostpone(
                 PowerHalService.SET_DEEP_SLEEP_ENTRY, WAIT_TIMEOUT_LONG_MS, mWakeupTime);
+        verifyWtfNeverLogged();
     }
 
     @Test
     public void testShutdownCancel() throws Exception {
-        initTest();
+        initTestForUser10();
 
         // Start in the ON state
         mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.ON, 0));
@@ -307,12 +347,12 @@
                         VehicleApPowerStateShutdownParam.CAN_SLEEP));
         assertStateReceivedForShutdownOrSleepWithPostpone(
                 PowerHalService.SET_DEEP_SLEEP_ENTRY, WAIT_TIMEOUT_LONG_MS, mWakeupTime);
+        verifyWtfNeverLogged();
     }
 
-    @Ignore("Disabled until b/147846930 is fixed")
     @Test
     public void testSleepImmediately() throws Exception {
-        initTest();
+        initTestForUser10();
 
         // Transition to ON state
         mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.ON, 0));
@@ -328,13 +368,14 @@
         assertThat(mDisplayInterface.waitForDisplayStateChange(WAIT_TIMEOUT_MS)).isFalse();
         mPowerSignalListener.waitForShutdown(WAIT_TIMEOUT_MS);
         mSystemStateInterface.waitForShutdown(WAIT_TIMEOUT_MS);
+        verifyWtfNeverLogged();
     }
 
     @Test
     @WakeupTime(100)
     @FlakyTest
     public void testShutdownWithProcessing() throws Exception {
-        initTest();
+        initTestForUser10();
         mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.SHUTDOWN_PREPARE, 0));
         assertStateReceivedForShutdownOrSleepWithPostpone(
                 PowerHalService.SET_SHUTDOWN_START, WAIT_TIMEOUT_LONG_MS, mWakeupTime);
@@ -342,6 +383,7 @@
         // Send the finished signal
         mPowerHal.setCurrentPowerState(new PowerState(VehicleApPowerStateReq.FINISHED, 0));
         mSystemStateInterface.waitForShutdown(WAIT_TIMEOUT_MS);
+        verifyWtfNeverLogged();
     }
 
     @Test
@@ -358,6 +400,7 @@
         mSystemStateInterface.waitForSleepEntryAndWakeup(WAIT_TIMEOUT_MS);
         assertStateReceived(PowerHalService.SET_DEEP_SLEEP_EXIT, 0);
         mPowerSignalListener.waitForSleepExit(WAIT_TIMEOUT_MS);
+        verifyWtfNeverLogged();
     }
 
     @Test
@@ -372,6 +415,7 @@
         suspendAndResumeForUserSwitchingTests();
 
         verifyUserSwitched(11);
+        verifyWtfNeverLogged();
     }
 
     @Test
@@ -384,6 +428,7 @@
         suspendAndResumeForUserSwitchingTests();
 
         verifyUserNotSwitched();
+        verifyWtfNeverLogged();
     }
 
     @Test
@@ -397,6 +442,7 @@
         suspendAndResumeForUserSwitchingTests();
 
         verifyUserSwitched(11);
+        verifyWtfNeverLogged();
     }
 
     @Test
@@ -412,6 +458,7 @@
 
         verifyUserRemoved(10);
         verifyUserSwitched(11);
+        verifyWtfNeverLogged();
     }
 
     @Test
@@ -427,6 +474,7 @@
 
         verifyUserRemoved(11);
         verifyUserSwitched(12);
+        verifyWtfNeverLogged();
     }
 
     @Test
@@ -442,6 +490,7 @@
 
         verifyUserNotSwitched();
         verifyUserNotRemoved(10);
+        // expects WTF
     }
 
     @Test
@@ -457,6 +506,7 @@
 
         verifyUserRemoved(11);
         verifyUserSwitched(12);
+        verifyWtfNeverLogged();
     }
 
     @Test
@@ -471,6 +521,7 @@
 
         verifyUserNotSwitched();
         verifyNoGuestCreated();
+        verifyWtfNeverLogged();
     }
 
     @Test
@@ -482,6 +533,7 @@
         suspendAndResumeForUserSwitchingTests();
 
         verifyUserNotSwitched();
+        // expects WTF
     }
 
     @Test
@@ -496,6 +548,7 @@
         suspendAndResumeForUserSwitchingTests();
 
         verifyUserNotSwitched();
+        verifyWtfNeverLogged();
     }
 
     @Test
@@ -509,6 +562,7 @@
         suspendAndResumeForUserSwitchingTests();
 
         verifyUserNotSwitched();
+        verifyWtfNeverLogged();
     }
 
     @Test
@@ -523,6 +577,7 @@
         suspendAndResumeForUserSwitchingTests();
 
         verifyUserNotSwitched();
+        verifyWtfNeverLogged();
     }
 
     @Test
@@ -539,7 +594,7 @@
 
         verifyUserRemoved(10);
         verifyUserSwitched(11);
-
+        verifyWtfNeverLogged();
     }
 
     @Test
@@ -556,6 +611,7 @@
 
         verifyUserRemoved(11);
         verifyUserSwitched(12);
+        verifyWtfNeverLogged();
     }
 
     @Test
@@ -573,6 +629,7 @@
 
         verifyUserNotSwitched();
         verifyUserNotRemoved(10);
+        // expects WTF
     }
 
     @Test
@@ -590,6 +647,7 @@
 
         verifyUserRemoved(11);
         verifyUserSwitched(12);
+        verifyWtfNeverLogged();
     }
 
     @Test
@@ -605,6 +663,7 @@
 
         verifyUserNotSwitched();
         verifyNoGuestCreated();
+        verifyWtfNeverLogged();
     }
 
     @Test
@@ -617,6 +676,7 @@
         suspendAndResumeForUserSwitchingTests();
 
         verifyUserNotSwitched();
+        // expects WTF
     }
 
     private void suspendAndResumeForUserSwitchingTests() throws Exception {
@@ -702,6 +762,23 @@
         }
     }
 
+    // TODO: should be part of @After, but then it would hide the real test failure (if any). We'd
+    // need a custom rule (like CTS's SafeCleaner) for it...
+    private void verifyWtfNeverLogged() {
+        int size = mWtfs.size();
+
+        switch (size) {
+            case 0:
+                return;
+            case 1:
+                throw mWtfs.get(0);
+            default:
+                StringBuilder msg = new StringBuilder("wtf called ").append(size).append(" times")
+                        .append(": ").append(mWtfs);
+                fail(msg.toString());
+        }
+    }
+
     private static void waitForSemaphore(Semaphore semaphore, long timeoutMs)
             throws InterruptedException {
         if (!semaphore.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
diff --git a/tests/carservice_unit_test/src/com/android/car/pm/VendorServiceControllerTest.java b/tests/carservice_unit_test/src/com/android/car/pm/VendorServiceControllerTest.java
index d7d624c..fd86e09 100644
--- a/tests/carservice_unit_test/src/com/android/car/pm/VendorServiceControllerTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/pm/VendorServiceControllerTest.java
@@ -39,10 +39,10 @@
 import android.os.UserManager;
 
 import androidx.test.core.app.ApplicationProvider;
-import androidx.test.filters.FlakyTest;
 
 import com.android.car.CarLocalServices;
 import com.android.car.user.CarUserService;
+import com.android.internal.annotations.GuardedBy;
 
 import org.junit.After;
 import org.junit.Before;
@@ -138,28 +138,27 @@
     }
 
     @Test
-    @FlakyTest
     public void systemUserUnlocked() {
         mController.init();
         mContext.reset();
 
         // Unlock system user
         mockUserUnlock(UserHandle.USER_SYSTEM, true);
-        runOnMainThread(() -> mCarUserService.setUserLockStatus(UserHandle.USER_SYSTEM, true));
+        runOnMainThreadAndWaitForIdle(() -> mCarUserService.setUserLockStatus(
+                UserHandle.USER_SYSTEM, true));
 
         mContext.assertStartedService(SERVICE_START_SYSTEM_UNLOCKED);
         mContext.verifyNoMoreServiceLaunches();
     }
 
     @Test
-    @FlakyTest
     public void fgUserUnlocked() {
         mController.init();
         mContext.reset();
 
         // Switch user to foreground
         when(ActivityManager.getCurrentUser()).thenReturn(FG_USER_ID);
-        runOnMainThread(() -> mCarUserService.onSwitchUser(FG_USER_ID));
+        runOnMainThreadAndWaitForIdle(() -> mCarUserService.onSwitchUser(FG_USER_ID));
 
         // Expect only services with ASAP trigger to be started
         mContext.assertBoundService(SERVICE_BIND_ALL_USERS_ASAP);
@@ -167,14 +166,16 @@
 
         // Unlock foreground user
         mockUserUnlock(FG_USER_ID, true);
-        runOnMainThread(() -> mCarUserService.setUserLockStatus(FG_USER_ID, true));
+        runOnMainThreadAndWaitForIdle(() -> mCarUserService.setUserLockStatus(FG_USER_ID, true));
 
         mContext.assertBoundService(SERVICE_BIND_FG_USER_UNLOCKED);
         mContext.verifyNoMoreServiceLaunches();
     }
 
-    private void runOnMainThread(Runnable r) {
+    private void runOnMainThreadAndWaitForIdle(Runnable r) {
         Handler.getMain().runWithScissors(r, DEFAULT_TIMEOUT_MS);
+        // Run empty runnable to make sure that all posted handlers are done.
+        Handler.getMain().runWithScissors(() -> { }, DEFAULT_TIMEOUT_MS);
     }
 
     private void mockUserUnlock(int userId, boolean unlock) {
@@ -189,7 +190,11 @@
 
     /** Overrides framework behavior to succeed on binding/starting processes. */
     public class ServiceLauncherContext extends ContextWrapper {
+        private final Object mLock = new Object();
+
+        @GuardedBy("mLock")
         private List<Intent> mBoundIntents = new ArrayList<>();
+        @GuardedBy("mLock")
         private List<Intent> mStartedServicesIntents = new ArrayList<>();
 
         ServiceLauncherContext(Context base) {
@@ -198,14 +203,18 @@
 
         @Override
         public ComponentName startServiceAsUser(Intent service, UserHandle user) {
-            mStartedServicesIntents.add(service);
+            synchronized (mLock) {
+                mStartedServicesIntents.add(service);
+            }
             return service.getComponent();
         }
 
         @Override
         public boolean bindServiceAsUser(Intent service, ServiceConnection conn, int flags,
                 Handler handler, UserHandle user) {
-            mBoundIntents.add(service);
+            synchronized (mLock) {
+                mBoundIntents.add(service);
+            }
             conn.onServiceConnected(service.getComponent(), null);
             return true;
         }
@@ -222,27 +231,35 @@
         }
 
         void assertBoundService(String service) {
-            assertThat(mBoundIntents).hasSize(1);
-            assertThat(mBoundIntents.get(0).getComponent())
-                    .isEqualTo(ComponentName.unflattenFromString(service));
-            mBoundIntents.clear();
+            synchronized (mLock) {
+                assertThat(mBoundIntents).hasSize(1);
+                assertThat(mBoundIntents.get(0).getComponent())
+                        .isEqualTo(ComponentName.unflattenFromString(service));
+                mBoundIntents.clear();
+            }
         }
 
         void assertStartedService(String service) {
-            assertThat(mStartedServicesIntents).hasSize(1);
-            assertThat(mStartedServicesIntents.get(0).getComponent())
-                    .isEqualTo(ComponentName.unflattenFromString(service));
-            mStartedServicesIntents.clear();
+            synchronized (mLock) {
+                assertThat(mStartedServicesIntents).hasSize(1);
+                assertThat(mStartedServicesIntents.get(0).getComponent())
+                        .isEqualTo(ComponentName.unflattenFromString(service));
+                mStartedServicesIntents.clear();
+            }
         }
 
         void verifyNoMoreServiceLaunches() {
-            assertThat(mStartedServicesIntents).isEmpty();
-            assertThat(mBoundIntents).isEmpty();
+            synchronized (mLock) {
+                assertThat(mStartedServicesIntents).isEmpty();
+                assertThat(mBoundIntents).isEmpty();
+            }
         }
 
         void reset() {
-            mStartedServicesIntents.clear();
-            mBoundIntents.clear();
+            synchronized (mLock) {
+                mStartedServicesIntents.clear();
+                mBoundIntents.clear();
+            }
 
         }
 
diff --git a/tests/carservice_unit_test/src/com/android/car/user/CarUserNoticeServiceTest.java b/tests/carservice_unit_test/src/com/android/car/user/CarUserNoticeServiceTest.java
index 3c434f1..93d9500 100644
--- a/tests/carservice_unit_test/src/com/android/car/user/CarUserNoticeServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/user/CarUserNoticeServiceTest.java
@@ -41,6 +41,8 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Looper;
 import android.os.PowerManager;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -97,6 +99,8 @@
     private MockitoSession mSession;
     private CarUserNoticeService mCarUserNoticeService;
 
+    private final Handler mHandler = new Handler(Looper.getMainLooper());
+
     /**
      * Initialize all of the objects with the @Mock annotation.
      */
@@ -126,8 +130,7 @@
         doReturn(mMockAppOpsManager).when(mMockContext).getSystemService(AppOpsManager.class);
         doReturn(mMockPackageManager).when(mMockContext).getPackageManager();
         doReturn(1).when(mMockPackageManager).getPackageUidAsUser(any(), anyInt());
-
-        mCarUserNoticeService = new CarUserNoticeService(mMockContext);
+        mCarUserNoticeService = new CarUserNoticeService(mMockContext, mHandler);
         mCarUserNoticeService.init();
         verify(mMockCarUserService).addUserCallback(mUserCallback.capture());
         verify(mMockContext).registerReceiver(mDisplayBroadcastReceiver.capture(),
@@ -274,7 +277,7 @@
     private void sendBroadcast(String action) {
         Intent intent = new Intent();
         intent.setAction(action);
-        mDisplayBroadcastReceiver.getValue().onReceive(mMockContext, intent);
+        mHandler.post(() -> mDisplayBroadcastReceiver.getValue().onReceive(mMockContext, intent));
     }
 
     private void sendPowerStateChange(int state) {