Merge "Added VALET_MODE_ENABLED to API" into main
diff --git a/car_product/distant_display/apps/CarDistantDisplaySystemUI/res/values/config.xml b/car_product/distant_display/apps/CarDistantDisplaySystemUI/res/values/config.xml
index fd17914..8316adb 100644
--- a/car_product/distant_display/apps/CarDistantDisplaySystemUI/res/values/config.xml
+++ b/car_product/distant_display/apps/CarDistantDisplaySystemUI/res/values/config.xml
@@ -33,7 +33,7 @@
     </string-array>
 
     <!-- display Id for distant display.  -->
-    <integer name="config_distantDisplayId">2</integer>
+    <integer name="config_distantDisplayId">3</integer>
 
     <string-array name="config_navigationActivities" translatable="false">
         <item>com.google.android.apps.maps/com.google.android.maps.MapsActivity</item>
diff --git a/car_product/distant_display/apps/CarDistantDisplaySystemUI/src/com/android/systemui/car/distantdisplay/activity/window/ActivityWindowController.java b/car_product/distant_display/apps/CarDistantDisplaySystemUI/src/com/android/systemui/car/distantdisplay/activity/window/ActivityWindowController.java
index 1a8954c..062ea41 100644
--- a/car_product/distant_display/apps/CarDistantDisplaySystemUI/src/com/android/systemui/car/distantdisplay/activity/window/ActivityWindowController.java
+++ b/car_product/distant_display/apps/CarDistantDisplaySystemUI/src/com/android/systemui/car/distantdisplay/activity/window/ActivityWindowController.java
@@ -17,10 +17,8 @@
 
 import android.app.ActivityOptions;
 import android.content.Context;
-import android.hardware.display.DisplayManager;
 import android.os.UserHandle;
 import android.util.Log;
-import android.view.Display;
 
 import com.android.systemui.R;
 import com.android.systemui.car.CarDeviceProvisionedController;
@@ -31,6 +29,7 @@
 
 import javax.inject.Inject;
 
+
 /**
  * Controller that decides who will host the TaskView based on the flags. Host can either be an
  * activity window or a window hosted by systemUI. This controller is also responsible for creating
@@ -41,14 +40,12 @@
     public static final String TAG = ActivityWindowController.class.getSimpleName();
     private final UserUnlockReceiver mUserUnlockReceiver = new UserUnlockReceiver();
     private final Context mContext;
-    private final DisplayManager mDisplayManager;
     private final int mDistantDisplayId;
     private final CarDeviceProvisionedController mCarDeviceProvisionedController;
     private boolean mUserSetupInProgress;
     private boolean mIsUserUnlocked;
     private boolean mDistantDisplayActivityStarted;
 
-
     private final CarDeviceProvisionedListener mCarDeviceProvisionedListener =
             new CarDeviceProvisionedListener() {
                 @Override
@@ -64,9 +61,8 @@
     public ActivityWindowController(Context context,
             CarDeviceProvisionedController deviceProvisionedController) {
         mContext = context;
-        mDisplayManager = mContext.getSystemService(DisplayManager.class);
-        mDistantDisplayId = findDisplay(
-                mContext.getResources().getInteger(R.integer.config_distantDisplayId));
+        mDistantDisplayId =
+                mContext.getResources().getInteger(R.integer.config_distantDisplayId);
         mCarDeviceProvisionedController = deviceProvisionedController;
         mCarDeviceProvisionedController.addCallback(mCarDeviceProvisionedListener);
     }
@@ -101,20 +97,9 @@
             return;
         }
         ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
-        options.setLaunchDisplayId(
-                mContext.getResources().getInteger(R.integer.config_distantDisplayId));
+        options.setLaunchDisplayId(mDistantDisplayId);
         mContext.startActivityAsUser(DistantDisplayActivity.createIntent(mContext),
                 options.toBundle(), UserHandle.CURRENT);
         mDistantDisplayActivityStarted = true;
     }
-
-    private int findDisplay(int distantDisplayId) {
-        for (Display display : mDisplayManager.getDisplays()) {
-            if (display.getDisplayId() == distantDisplayId) {
-                return display.getDisplayId();
-            }
-        }
-        Log.e(TAG, "Could not find a display id" + distantDisplayId);
-        return Display.INVALID_DISPLAY;
-    }
 }
diff --git a/car_product/distant_display/display/display_settings.xml b/car_product/distant_display/display/display_settings.xml
index 1ee623c..6551b87 100644
--- a/car_product/distant_display/display/display_settings.xml
+++ b/car_product/distant_display/display/display_settings.xml
@@ -19,9 +19,9 @@
     <!-- Use physical port number instead of local id -->
     <config identifier="1" />
 
-    <display name="port:130"
-             forcedDensity="160"
+    <display name="port:68"
+             forcedDensity="213"
              forcedWidth="4000"
-             forcedHeight="1080" />
+             forcedHeight="800" />
 
 </display-settings>
\ No newline at end of file
diff --git a/lib/backported-car-property-helper-lib/Android.bp b/lib/backported-car-property-helper-lib/Android.bp
new file mode 100644
index 0000000..5b8d471
--- /dev/null
+++ b/lib/backported-car-property-helper-lib/Android.bp
@@ -0,0 +1,35 @@
+// Copyright (C) 2023 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_library {
+    name: "com.android.car.property.backported",
+    srcs: [
+        "src/**/*.java",
+    ],
+
+    libs: [
+        "android.car",
+    ],
+
+    static_libs: [
+        "androidx.annotation_annotation",
+    ],
+
+    // Need to be at most 28 to be used in ATS.
+    min_sdk_version: "28",
+}
diff --git a/lib/backported-car-property-helper-lib/AndroidManifest.xml b/lib/backported-car-property-helper-lib/AndroidManifest.xml
new file mode 100644
index 0000000..77f3713
--- /dev/null
+++ b/lib/backported-car-property-helper-lib/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2023 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.car.property.backported">
+</manifest>
diff --git a/lib/backported-car-property-helper-lib/src/com/android/car/property/backported/CarPropertyHelper.java b/lib/backported-car-property-helper-lib/src/com/android/car/property/backported/CarPropertyHelper.java
new file mode 100644
index 0000000..07ea476
--- /dev/null
+++ b/lib/backported-car-property-helper-lib/src/com/android/car/property/backported/CarPropertyHelper.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.property.backported;
+
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.car.VehiclePropertyIds.LOCATION_CHARACTERIZATION;
+import static android.car.hardware.property.VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_INFO;
+
+import android.car.VehiclePropertyIds;
+import android.car.hardware.property.CarPropertyManager;
+import android.util.SparseBooleanArray;
+
+import androidx.annotation.GuardedBy;
+import androidx.annotation.Nullable;
+
+import java.util.Map;
+
+/**
+ * A helper class for mapping a property name to the supported property ID/required permissions.
+ */
+public final class CarPropertyHelper {
+
+    private final Object mLock = new Object();
+    private final CarPropertyManager mCarPropertyManager;
+
+   // These are the same values as defined in VHAL interface.
+    private static final int VEHICLE_PROPERTY_GROUP_MASK = 0xf0000000;
+    private static final int VEHICLE_PROPERTY_GROUP_BACKPORTED = 0x30000000;
+
+    private static final Map<String, PropertyInfo> PROPERTY_INFO_BY_NAME = Map.ofEntries(
+            Map.entry(VehiclePropertyIds.toString(LOCATION_CHARACTERIZATION),
+                    new PropertyInfo(LOCATION_CHARACTERIZATION)
+                            .setSystemReadPermission(ACCESS_FINE_LOCATION)
+                            .setVendorReadPermission(PERMISSION_GET_CAR_VENDOR_CATEGORY_INFO))
+    );
+
+
+    // A cache for storing whether each propertyId is supported.
+    @GuardedBy("mLock")
+    private final SparseBooleanArray mPropertySupportedById = new SparseBooleanArray();
+
+    private static final class PropertyInfo {
+        public int systemPropertyId;
+        public String systemReadPermission;
+        public String systemWritePermission;
+        public String vendorReadPermission;
+        public String vendorWritePermission;
+
+        PropertyInfo(int systemPropertyId) {
+            this.systemPropertyId = systemPropertyId;
+        }
+
+        public PropertyInfo setSystemReadPermission(String permission) {
+            systemReadPermission = permission;
+            return this;
+        }
+
+        public PropertyInfo setSystemWritePermission(String permission) {
+            systemWritePermission = permission;
+            return this;
+        }
+
+        public PropertyInfo setVendorReadPermission(String permission) {
+            vendorReadPermission = permission;
+            return this;
+        }
+
+        public PropertyInfo setVendorWritePermission(String permission) {
+            vendorWritePermission = permission;
+            return this;
+        }
+    }
+
+    public CarPropertyHelper(CarPropertyManager carPropertyManager) {
+        mCarPropertyManager = carPropertyManager;
+    }
+
+    private static int getVendorPropertyId(int systemPropertyId) {
+        return (systemPropertyId | VEHICLE_PROPERTY_GROUP_MASK) & VEHICLE_PROPERTY_GROUP_BACKPORTED;
+    }
+
+    private boolean isPropertySupported(int propertyId) {
+        synchronized (mLock) {
+            // If we already know from cache, use it.
+            int index = mPropertySupportedById.indexOfKey(propertyId);
+            if (index >= 0) {
+                return mPropertySupportedById.valueAt(index);
+            }
+
+            boolean isSupported = (mCarPropertyManager.getCarPropertyConfig(propertyId) != null);
+
+            // Store the result into cache.
+            mPropertySupportedById.put(propertyId, isSupported);
+            return isSupported;
+        }
+    }
+
+    /**
+     * Maps a property name to a supported property ID.
+     *
+     * <p>If the system property ID is supported, returns the system property ID.
+     *
+     * <p>If the system property ID is not supported but the backported property ID is supported,
+     * returns the backported property ID.
+     *
+     * <p>If both are not supported, returns {@code null}.
+     *
+     * @param propertyName The name for the property.
+     * @return The supported property ID or {@code null}.
+     */
+    @Nullable
+    public Integer getPropertyId(String propertyName) {
+        PropertyInfo propertyInfo = PROPERTY_INFO_BY_NAME.get(propertyName);
+        if (propertyInfo == null) {
+            return null;
+        }
+        int systemPropertyId = propertyInfo.systemPropertyId;
+        if (isPropertySupported(systemPropertyId)) {
+            return systemPropertyId;
+        }
+        int vendorPropertyId = getVendorPropertyId(systemPropertyId);
+        if (isPropertySupported(vendorPropertyId)) {
+            return vendorPropertyId;
+        }
+        return null;
+    }
+
+    /**
+     * Gets the required permission for reading the property.
+     *
+     * <p>If the system property ID is supported, returns the read permission for it.
+     *
+     * <p>If the system property ID is not supported but the backported property ID is supported,
+     * returns the vendor read permission for it.
+     *
+     * <p>If both are not supported or the property is not readable, returns {@code null}.
+     *
+     * @param propertyName The name for the property.
+     * @return The required permission for reading the property or {@code null}.
+     */
+    @Nullable
+    public String getReadPermission(String propertyName) {
+        PropertyInfo propertyInfo = PROPERTY_INFO_BY_NAME.get(propertyName);
+        if (propertyInfo == null) {
+            return null;
+        }
+        int systemPropertyId = propertyInfo.systemPropertyId;
+        if (isPropertySupported(systemPropertyId)) {
+            return propertyInfo.systemReadPermission;
+        }
+        int vendorPropertyId = getVendorPropertyId(systemPropertyId);
+        if (isPropertySupported(vendorPropertyId)) {
+            return propertyInfo.vendorReadPermission;
+        }
+        return null;
+    }
+
+    /**
+     * Gets the required permission for writing the property.
+     *
+     * <p>If the system property ID is supported, returns the write permission for it.
+     *
+     * <p>If the system property ID is not supported but the backported property ID is supported,
+     * returns the vendor write permission for it.
+     *
+     * <p>If both are not supported or the property is not writable, returns {@code null}.
+     *
+     * @param propertyName The name for the property.
+     * @return The required permission for writing the property or {@code null}.
+     */
+    @Nullable
+    public String getWritePermission(String propertyName) {
+        PropertyInfo propertyInfo = PROPERTY_INFO_BY_NAME.get(propertyName);
+        if (propertyInfo == null) {
+            return null;
+        }
+        int systemPropertyId = propertyInfo.systemPropertyId;
+        if (isPropertySupported(systemPropertyId)) {
+            return propertyInfo.systemWritePermission;
+        }
+        int vendorPropertyId = getVendorPropertyId(systemPropertyId);
+        if (isPropertySupported(vendorPropertyId)) {
+            return propertyInfo.vendorWritePermission;
+        }
+        return null;
+    }
+}
diff --git a/lib/backported-car-property-helper-lib/tests/Android.bp b/lib/backported-car-property-helper-lib/tests/Android.bp
new file mode 100644
index 0000000..9f7ec1d
--- /dev/null
+++ b/lib/backported-car-property-helper-lib/tests/Android.bp
@@ -0,0 +1,45 @@
+// Copyright (C) 2023 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+    name: "com.android.car.property.backported-unit-test",
+    srcs: [
+        "src/**/*.java",
+    ],
+
+    libs: [
+        "android.car",
+        "android.test.base",
+        "android.test.runner",
+    ],
+
+    static_libs: [
+        "androidx.test.rules",
+        "com.android.car.property.backported",
+        "junit",
+        "mockito-target-extended",
+        "truth",
+    ],
+
+    // mockito-target-inline dependency
+    jni_libs: [
+        "libdexmakerjvmtiagent",
+    ],
+
+    test_suites: ["general-tests"],
+}
diff --git a/lib/backported-car-property-helper-lib/tests/AndroidManifest.xml b/lib/backported-car-property-helper-lib/tests/AndroidManifest.xml
new file mode 100644
index 0000000..9030993
--- /dev/null
+++ b/lib/backported-car-property-helper-lib/tests/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2023 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.car.property.backported.unittest">
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="android.car.property.backported.unittest"
+        android:label="Tests for android.car.property.backported"/>
+    <application
+        android:debuggable="true">
+        <uses-library android:name="android.test.runner" />
+    </application>
+</manifest>
diff --git a/lib/backported-car-property-helper-lib/tests/src/com/android/car/property/backported/CarPropertyHelperUnitTest.java b/lib/backported-car-property-helper-lib/tests/src/com/android/car/property/backported/CarPropertyHelperUnitTest.java
new file mode 100644
index 0000000..b76720c
--- /dev/null
+++ b/lib/backported-car-property-helper-lib/tests/src/com/android/car/property/backported/CarPropertyHelperUnitTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.property.backported;
+
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.car.VehiclePropertyIds.LOCATION_CHARACTERIZATION;
+import static android.car.hardware.property.VehicleVendorPermission.PERMISSION_GET_CAR_VENDOR_CATEGORY_INFO;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.car.hardware.CarPropertyConfig;
+import android.car.hardware.property.CarPropertyManager;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.mockito.quality.Strictness;
+
+@RunWith(MockitoJUnitRunner.class)
+public final class CarPropertyHelperUnitTest {
+
+    private static final int VENDOR_LOCATION_CHARACTERIZATION = 0x31400c10;
+    private static final String PROPERTY_NAME = "LOCATION_CHARACTERIZATION";
+
+    private MockitoSession mMockitoSession;
+
+    @Mock
+    private CarPropertyManager mMockCarPropertyManager;
+    @Mock
+    private CarPropertyConfig mMockCarPropertyConfig;
+
+    private CarPropertyHelper mCarPropertyHelper;
+
+
+    @Before
+    public void setUp() {
+        mMockitoSession = mockitoSession()
+            .strictness(Strictness.LENIENT)
+            .initMocks(this)
+            .startMocking();
+        mCarPropertyHelper = new CarPropertyHelper(mMockCarPropertyManager);
+    }
+
+    @After
+    public void tearDown() {
+        mMockitoSession.finishMocking();
+    }
+
+    @Test
+    public void testGetPropertyId_systemPropertyIdSupported() {
+        when(mMockCarPropertyManager.getCarPropertyConfig(LOCATION_CHARACTERIZATION))
+                .thenReturn(mMockCarPropertyConfig);
+
+        assertThat(mCarPropertyHelper.getPropertyId(PROPERTY_NAME))
+                .isEqualTo(LOCATION_CHARACTERIZATION);
+    }
+
+    @Test
+    public void testGetPropertyId_vendorPropertyIdSupported() {
+        when(mMockCarPropertyManager.getCarPropertyConfig(LOCATION_CHARACTERIZATION))
+                .thenReturn(null);
+        when(mMockCarPropertyManager.getCarPropertyConfig(VENDOR_LOCATION_CHARACTERIZATION))
+                .thenReturn(mMockCarPropertyConfig);
+
+        assertThat(mCarPropertyHelper.getPropertyId(PROPERTY_NAME))
+                .isEqualTo(VENDOR_LOCATION_CHARACTERIZATION);
+    }
+
+    @Test
+    public void testGetPropertyId_vendorPropertyNotSupported() {
+        when(mMockCarPropertyManager.getCarPropertyConfig(LOCATION_CHARACTERIZATION))
+                .thenReturn(null);
+        when(mMockCarPropertyManager.getCarPropertyConfig(VENDOR_LOCATION_CHARACTERIZATION))
+                .thenReturn(null);
+
+        assertThat(mCarPropertyHelper.getPropertyId(PROPERTY_NAME)).isNull();
+    }
+
+    @Test
+    public void testGetPropertyId_invalidPropertyName() {
+        assertThat(mCarPropertyHelper.getPropertyId("Invalid_property")).isNull();
+    }
+
+    @Test
+    public void testGetReadPermissions_systemPermission() {
+        when(mMockCarPropertyManager.getCarPropertyConfig(LOCATION_CHARACTERIZATION))
+                .thenReturn(mMockCarPropertyConfig);
+
+        assertThat(mCarPropertyHelper.getReadPermission(PROPERTY_NAME))
+                .isEqualTo(ACCESS_FINE_LOCATION);
+    }
+
+    @Test
+    public void testGetReadPermissions_vendorPermission() {
+        when(mMockCarPropertyManager.getCarPropertyConfig(LOCATION_CHARACTERIZATION))
+                .thenReturn(null);
+        when(mMockCarPropertyManager.getCarPropertyConfig(VENDOR_LOCATION_CHARACTERIZATION))
+                .thenReturn(mMockCarPropertyConfig);
+
+        assertThat(mCarPropertyHelper.getReadPermission(PROPERTY_NAME))
+                .isEqualTo(PERMISSION_GET_CAR_VENDOR_CATEGORY_INFO);
+    }
+
+    @Test
+    public void testGetWritePermissions_onlyReadable() {
+        when(mMockCarPropertyManager.getCarPropertyConfig(LOCATION_CHARACTERIZATION))
+                .thenReturn(mMockCarPropertyConfig);
+
+        assertThat(mCarPropertyHelper.getWritePermission(PROPERTY_NAME))
+                .isNull();
+    }
+}
diff --git a/service/src/com/android/car/audio/CarAudioZone.java b/service/src/com/android/car/audio/CarAudioZone.java
index baefea9..f035042 100644
--- a/service/src/com/android/car/audio/CarAudioZone.java
+++ b/service/src/com/android/car/audio/CarAudioZone.java
@@ -225,6 +225,7 @@
             mCurrentConfigId = configInfoSwitchedTo.getConfigId();
             CarAudioZoneConfig current = mCarAudioZoneConfigs.get(mCurrentConfigId);
             current.setIsSelected(true);
+            current.updateVolumeDevices(mCarAudioContext.useCoreAudioRouting());
         }
     }
 
@@ -241,6 +242,7 @@
                 mCurrentConfigId = config.getZoneConfigId();
             }
             config.setIsSelected(true);
+            config.updateVolumeDevices(mCarAudioContext.useCoreAudioRouting());
         }
     }
 
diff --git a/service/src/com/android/car/audio/CarAudioZoneConfig.java b/service/src/com/android/car/audio/CarAudioZoneConfig.java
index 2c85c73..9c2ed6c 100644
--- a/service/src/com/android/car/audio/CarAudioZoneConfig.java
+++ b/service/src/com/android/car/audio/CarAudioZoneConfig.java
@@ -499,6 +499,12 @@
         return updated;
     }
 
+    void updateVolumeDevices(boolean useCoreAudioRouting) {
+        for (int c = 0; c < mVolumeGroups.size(); c++) {
+            mVolumeGroups.get(c).updateDevices(useCoreAudioRouting);
+        }
+    }
+
     static final class Builder {
         private final int mZoneId;
         private final int mZoneConfigId;
diff --git a/service/src/com/android/car/audio/CarVolumeGroup.java b/service/src/com/android/car/audio/CarVolumeGroup.java
index 79bdf94..de0d225 100644
--- a/service/src/com/android/car/audio/CarVolumeGroup.java
+++ b/service/src/com/android/car/audio/CarVolumeGroup.java
@@ -346,6 +346,10 @@
         return carAudioContexts;
     }
 
+    protected AudioAttributes[] getAudioAttributesForContext(int context) {
+        return mCarAudioContext.getAudioAttributesForContext(context);
+    }
+
     /**
      * Returns the id of the volume group.
      * <p> Note that all clients are already developed in the way that when they get the number of
@@ -920,6 +924,9 @@
         }
     }
 
+    void updateDevices(boolean useCoreAudioRouting) {
+    }
+
     /**
      * Calculates the new gain stages from list of assigned audio device infos
      *
diff --git a/service/src/com/android/car/audio/CoreAudioHelper.java b/service/src/com/android/car/audio/CoreAudioHelper.java
index 9302d35..11a76a4 100644
--- a/service/src/com/android/car/audio/CoreAudioHelper.java
+++ b/service/src/com/android/car/audio/CoreAudioHelper.java
@@ -102,7 +102,7 @@
      * given {@link AudioAttributes} if found, {@link #INVALID_STRATEGY} id otherwise.
      */
     public static int getStrategyForAudioAttributes(AudioAttributes attributes) {
-        Preconditions.checkNotNull(attributes, "Audio Attributes must not be null");
+        Preconditions.checkNotNull(attributes, "Audio Attributes can not be null");
         for (int index = 0; index < getAudioProductStrategies().size(); index++) {
             AudioProductStrategy strategy = getAudioProductStrategies().get(index);
             if (strategy.supportsAudioAttributes(attributes)) {
@@ -113,7 +113,7 @@
     }
 
     public static int getStrategyForContextName(String contextName) {
-        Preconditions.checkNotNull(contextName, "Context name must not be null");
+        Preconditions.checkNotNull(contextName, "Context name can not be null");
         for (int index = 0; index < getAudioProductStrategies().size(); index++) {
             AudioProductStrategy strategy = getAudioProductStrategies().get(index);
             if (Objects.equals(strategy.getName(), contextName)) {
@@ -139,6 +139,20 @@
                 ? getStrategyForAudioAttributes(DEFAULT_ATTRIBUTES) : strategyId;
     }
 
+    @Nullable
+    static AudioProductStrategy getProductStrategyForAudioAttributes(
+            AudioAttributes attributes) {
+        Preconditions.checkNotNull(attributes, "Audio attributes can not be null");
+        for (int index = 0; index < getAudioProductStrategies().size(); index++) {
+            AudioProductStrategy strategy = getAudioProductStrategies().get(index);
+            if (!strategy.supportsAudioAttributes(attributes)) {
+                continue;
+            }
+            return strategy;
+        }
+        return null;
+    }
+
     /**
      * Gets the {@link AudioProductStrategy} referred by its unique identifier.
      *
@@ -236,7 +250,7 @@
      */
     @Nullable
     public static String getVolumeGroupNameForAudioAttributes(AudioAttributes attributes) {
-        Preconditions.checkNotNull(attributes, "Audio Attributes must not be null");
+        Preconditions.checkNotNull(attributes, "Audio Attributes can not be null");
         int volumeGroupId = getVolumeGroupIdForAudioAttributes(attributes);
         return volumeGroupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP
                 ? getVolumeGroupNameFromCoreId(volumeGroupId) : null;
@@ -261,7 +275,7 @@
      * if found, {@link #INVALID_GROUP_ID} otherwise.
      */
     public static int getVolumeGroupIdForAudioAttributes(AudioAttributes attributes) {
-        Preconditions.checkNotNull(attributes, "Audio Attributes must not be null");
+        Preconditions.checkNotNull(attributes, "Audio Attributes can not be null");
         for (int index = 0; index < getAudioProductStrategies().size(); index++) {
             AudioProductStrategy strategy = getAudioProductStrategies().get(index);
             int volumeGroupId =
diff --git a/service/src/com/android/car/audio/CoreAudioVolumeGroup.java b/service/src/com/android/car/audio/CoreAudioVolumeGroup.java
index 7e41886..fea621f 100644
--- a/service/src/com/android/car/audio/CoreAudioVolumeGroup.java
+++ b/service/src/com/android/car/audio/CoreAudioVolumeGroup.java
@@ -20,18 +20,25 @@
 import static android.car.media.CarVolumeGroupEvent.EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED;
 
 import static com.android.car.CarLog.TAG_AUDIO;
+import static com.android.car.audio.CoreAudioHelper.getProductStrategyForAudioAttributes;
 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO;
 
 import android.car.builtin.media.AudioManagerHelper;
 import android.car.builtin.util.Slogf;
 import android.media.AudioAttributes;
+import android.media.AudioDeviceAttributes;
 import android.media.AudioManager;
+import android.media.audiopolicy.AudioProductStrategy;
+import android.util.ArraySet;
 import android.util.SparseArray;
 
 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
 import com.android.car.internal.util.IndentingPrintWriter;
 import com.android.internal.annotations.GuardedBy;
 
+import java.util.Arrays;
+import java.util.List;
+
 /**
  * A class encapsulates a volume group in car.
  *
@@ -283,6 +290,41 @@
     }
 
     @Override
+    void updateDevices(boolean useCoreAudioRouting) {
+        // If not using core audio routing, than device need to be updated to match the information
+        // for audio attributes to core volume groups.
+        if (useCoreAudioRouting) {
+            return;
+        }
+
+        int[] contexts = getContexts();
+        for (int c = 0; c < contexts.length; c++) {
+            int context = contexts[c];
+            AudioAttributes[] audioAttributes = getAudioAttributesForContext(context);
+            AudioDeviceAttributes device = getAudioDeviceForContext(context);
+            setPreferredDeviceForAudioAttribute(Arrays.asList(audioAttributes), device);
+        }
+
+    }
+
+    private void setPreferredDeviceForAudioAttribute(List<AudioAttributes> audioAttributes,
+            AudioDeviceAttributes audioDeviceAttributes) {
+        ArraySet<Integer> strategiesSet = new ArraySet<>();
+        for (int c = 0; c < audioAttributes.size(); c++) {
+            AudioProductStrategy strategy =
+                    getProductStrategyForAudioAttributes(audioAttributes.get(c));
+            if (strategy == null) {
+                continue;
+            }
+            if (!strategiesSet.add(strategy.getId())) {
+                continue;
+            }
+            mAudioManager.setPreferredDeviceForStrategy(strategy, audioDeviceAttributes);
+        }
+    }
+
+
+    @Override
     protected int getDefaultGainIndex() {
         return mDefaultGainIndex;
     }
diff --git a/tests/carservice_unit_test/src/com/android/car/audio/CarAudioZoneConfigUnitTest.java b/tests/carservice_unit_test/src/com/android/car/audio/CarAudioZoneConfigUnitTest.java
index 8eb88db..9f6b808 100644
--- a/tests/carservice_unit_test/src/com/android/car/audio/CarAudioZoneConfigUnitTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/audio/CarAudioZoneConfigUnitTest.java
@@ -282,6 +282,30 @@
     }
 
     @Test
+    public void updateVolumeDevices_withUseCoreAudioRoutingEnabled() {
+        CarAudioZoneConfig zoneConfig = mTestAudioZoneConfigBuilder.addVolumeGroup(mMockMusicGroup)
+                .addVolumeGroup(mMockNavGroup).build();
+        boolean useCoreAudioRouting = true;
+
+        zoneConfig.updateVolumeDevices(useCoreAudioRouting);
+
+        verify(mMockMusicGroup).updateDevices(useCoreAudioRouting);
+        verify(mMockNavGroup).updateDevices(useCoreAudioRouting);
+    }
+
+    @Test
+    public void updateVolumeDevices_withUseCoreAudioRoutingDisabled() {
+        CarAudioZoneConfig zoneConfig = mTestAudioZoneConfigBuilder.addVolumeGroup(mMockMusicGroup)
+                .addVolumeGroup(mMockNavGroup).build();
+        boolean useCoreAudioRouting = false;
+
+        zoneConfig.updateVolumeDevices(useCoreAudioRouting);
+
+        verify(mMockMusicGroup).updateDevices(useCoreAudioRouting);
+        verify(mMockNavGroup).updateDevices(useCoreAudioRouting);
+    }
+
+    @Test
     public void validateCanUseDynamicMixRouting_addressSharedAmongGroups_forbidUseDynamicRouting() {
         CarAudioDeviceInfo musicCarAudioDeviceInfo = Mockito.mock(CarAudioDeviceInfo.class);
         CarVolumeGroup mockMusicGroup = new VolumeGroupBuilder()
diff --git a/tests/carservice_unit_test/src/com/android/car/audio/CarAudioZoneUnitTest.java b/tests/carservice_unit_test/src/com/android/car/audio/CarAudioZoneUnitTest.java
index eb53ca1..13cc386 100644
--- a/tests/carservice_unit_test/src/com/android/car/audio/CarAudioZoneUnitTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/audio/CarAudioZoneUnitTest.java
@@ -260,7 +260,7 @@
     public void isCurrentZoneConfig_forCurrentConfig_returnsFalse() {
         mTestAudioZone.addZoneConfig(mMockZoneConfig0);
         mTestAudioZone.addZoneConfig(mMockZoneConfig1);
-        CarAudioZoneConfigInfo nonCurrentZoneConfigInfo = getNonCurrentZoneConfigInfo();
+        CarAudioZoneConfigInfo nonCurrentZoneConfigInfo = getFirstNonCurrentZoneConfigInfo();
 
         expectWithMessage("Non-current zone config info")
                 .that(mTestAudioZone.isCurrentZoneConfig(nonCurrentZoneConfigInfo))
@@ -268,10 +268,10 @@
     }
 
     @Test
-    public void setCurrentCarZoneConfig() {
+    public void setCurrentCarZoneConfig_withCoreAudioRoutingDisabled() {
         mTestAudioZone.addZoneConfig(mMockZoneConfig0);
         mTestAudioZone.addZoneConfig(mMockZoneConfig1);
-        CarAudioZoneConfigInfo currentZoneConfigInfoToSwitch = getNonCurrentZoneConfigInfo();
+        CarAudioZoneConfigInfo currentZoneConfigInfoToSwitch = getFirstNonCurrentZoneConfigInfo();
 
         mTestAudioZone.setCurrentCarZoneConfig(currentZoneConfigInfoToSwitch);
 
@@ -279,6 +279,29 @@
                 .that(mTestAudioZone.isCurrentZoneConfig(currentZoneConfigInfoToSwitch))
                 .isTrue();
         verify(mMockZoneConfig1).setIsSelected(true);
+        verify(mMockZoneConfig1).updateVolumeDevices(/* useCoreAudioRouting= */ false);
+        verify(mMockZoneConfig0).setIsSelected(false);
+    }
+
+    @Test
+    public void setCurrentCarZoneConfig_withCoreAudioRoutingEnabled() {
+        CarAudioContext contextWithCoreAudioRouting =
+                new CarAudioContext(CarAudioContext.getAllContextsInfo(),
+                        /* useCoreAudioRouting= */ true);
+        CarAudioZone testAudioZone = new CarAudioZone(contextWithCoreAudioRouting, TEST_ZONE_NAME,
+                TEST_ZONE_ID);
+        testAudioZone.addZoneConfig(mMockZoneConfig0);
+        testAudioZone.addZoneConfig(mMockZoneConfig1);
+        CarAudioZoneConfigInfo currentZoneConfigInfoToSwitch =
+                getFirstNonCurrentZoneConfigInfo(testAudioZone);
+
+        testAudioZone.setCurrentCarZoneConfig(currentZoneConfigInfoToSwitch);
+
+        expectWithMessage("Current zone config info after switching zone configuration"
+                + "with core audio routing")
+                .that(testAudioZone.isCurrentZoneConfig(currentZoneConfigInfoToSwitch)).isTrue();
+        verify(mMockZoneConfig1).setIsSelected(true);
+        verify(mMockZoneConfig1).updateVolumeDevices(/* useCoreAudioRouting= */ true);
         verify(mMockZoneConfig0).setIsSelected(false);
     }
 
@@ -819,11 +842,10 @@
                 .hasMessageThat().contains("Audio devices");
     }
 
-    private CarAudioZoneConfigInfo getNonCurrentZoneConfigInfo() {
-        CarAudioZoneConfigInfo currentZoneConfigInfo = mTestAudioZone
-                .getCurrentCarAudioZoneConfig().getCarAudioZoneConfigInfo();
-        List<CarAudioZoneConfigInfo> zoneConfigInfoList = mTestAudioZone
-                .getCarAudioZoneConfigInfos();
+    private CarAudioZoneConfigInfo getFirstNonCurrentZoneConfigInfo(CarAudioZone audioZone) {
+        CarAudioZoneConfigInfo currentZoneConfigInfo = audioZone.getCurrentCarAudioZoneConfig()
+                .getCarAudioZoneConfigInfo();
+        List<CarAudioZoneConfigInfo> zoneConfigInfoList = audioZone.getCarAudioZoneConfigInfos();
         for (int index = 0; index < zoneConfigInfoList.size(); index++) {
             CarAudioZoneConfigInfo zoneConfigInfo = zoneConfigInfoList.get(index);
             if (!currentZoneConfigInfo.equals(zoneConfigInfo)) {
@@ -833,6 +855,10 @@
         return null;
     }
 
+    private CarAudioZoneConfigInfo getFirstNonCurrentZoneConfigInfo() {
+        return getFirstNonCurrentZoneConfigInfo(mTestAudioZone);
+    }
+
     private static final class TestCarAudioZoneConfigBuilder {
         private static final int INVALID_GROUP_ID = -1;
         private static final int INVALID_EVENT_TYPE = 0;
diff --git a/tests/carservice_unit_test/src/com/android/car/audio/CoreAudioHelperTest.java b/tests/carservice_unit_test/src/com/android/car/audio/CoreAudioHelperTest.java
index 3fa9ab2..6b329b7 100644
--- a/tests/carservice_unit_test/src/com/android/car/audio/CoreAudioHelperTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/audio/CoreAudioHelperTest.java
@@ -45,6 +45,8 @@
 import static com.android.car.audio.CoreAudioRoutingUtils.UNSUPPORTED_ATTRIBUTES;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 
+import static org.junit.Assert.assertThrows;
+
 import android.car.test.mocks.AbstractExtendedMockitoTestCase;
 import android.media.AudioManager;
 import android.media.audiopolicy.AudioProductStrategy;
@@ -123,6 +125,35 @@
     }
 
     @Test
+    public void getProductStrategyForAudioAttributes_withValidAttributes_succeeds() {
+        expectWithMessage("Music product strategy")
+                .that(CoreAudioHelper.getProductStrategyForAudioAttributes(MUSIC_ATTRIBUTES))
+                .isEqualTo(MUSIC_STRATEGY);
+        expectWithMessage("Navigation product strategy")
+                .that(CoreAudioHelper.getProductStrategyForAudioAttributes(NAV_ATTRIBUTES))
+                .isEqualTo(NAV_STRATEGY);
+        expectWithMessage("OEM product strategy")
+                .that(CoreAudioHelper.getProductStrategyForAudioAttributes(OEM_ATTRIBUTES))
+                .isEqualTo(OEM_STRATEGY);
+    }
+
+    @Test
+    public void getProductStrategyForAudioAttributes_withInvalidAttributes_returnsNull() {
+        expectWithMessage("Null product strategy for invalid audio attribute")
+                .that(CoreAudioHelper.getProductStrategyForAudioAttributes(UNSUPPORTED_ATTRIBUTES))
+                .isNull();
+    }
+
+    @Test
+    public void getProductStrategyForAudioAttributes_withNullAttributes_fails() {
+        NullPointerException exception = assertThrows(NullPointerException.class, () ->
+                CoreAudioHelper.getProductStrategyForAudioAttributes(null));
+
+        expectWithMessage("Null audio attributes exception").that(exception).hasMessageThat()
+                .contains("Audio attributes");
+    }
+
+    @Test
     public void getStrategyForContextName_succeeds() {
         expectWithMessage("Music strategy for context name (%s)", MUSIC_CONTEXT_NAME)
                 .that(CoreAudioHelper.getStrategyForContextName(MUSIC_CONTEXT_NAME))
diff --git a/tests/carservice_unit_test/src/com/android/car/audio/CoreAudioVolumeGroupTest.java b/tests/carservice_unit_test/src/com/android/car/audio/CoreAudioVolumeGroupTest.java
index 403337b..da34646 100644
--- a/tests/carservice_unit_test/src/com/android/car/audio/CoreAudioVolumeGroupTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/audio/CoreAudioVolumeGroupTest.java
@@ -23,6 +23,7 @@
 import static android.car.test.mocks.AndroidMockitoHelper.mockCarGetPlatformVersion;
 
 import static com.android.car.audio.CoreAudioRoutingUtils.MEDIA_CONTEXT_INFO;
+import static com.android.car.audio.CoreAudioRoutingUtils.MOVIE_ATTRIBUTES;
 import static com.android.car.audio.CoreAudioRoutingUtils.MUSIC_AM_INIT_INDEX;
 import static com.android.car.audio.CoreAudioRoutingUtils.MUSIC_ATTRIBUTES;
 import static com.android.car.audio.CoreAudioRoutingUtils.MUSIC_CAR_GROUP_ID;
@@ -31,6 +32,7 @@
 import static com.android.car.audio.CoreAudioRoutingUtils.MUSIC_GROUP_NAME;
 import static com.android.car.audio.CoreAudioRoutingUtils.MUSIC_MAX_INDEX;
 import static com.android.car.audio.CoreAudioRoutingUtils.MUSIC_MIN_INDEX;
+import static com.android.car.audio.CoreAudioRoutingUtils.MUSIC_STRATEGY;
 import static com.android.car.audio.CoreAudioRoutingUtils.MUSIC_STRATEGY_ID;
 import static com.android.car.audio.CoreAudioRoutingUtils.NAV_ATTRIBUTES;
 import static com.android.car.audio.CoreAudioRoutingUtils.NAV_CAR_GROUP_ID;
@@ -57,11 +59,14 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.when;
 
 import android.car.Car;
 import android.car.builtin.media.AudioManagerHelper;
 import android.car.test.mocks.AbstractExtendedMockitoTestCase;
+import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
 import android.util.SparseArray;
 
@@ -93,6 +98,7 @@
     private CoreAudioVolumeGroup mMusicCoreAudioVolumeGroup;
     private CoreAudioVolumeGroup mNavCoreAudioVolumeGroup;
     private CoreAudioVolumeGroup mOemCoreAudioVolumeGroup;
+    private AudioDeviceAttributes mMusicDeviceAttributes;
 
     public CoreAudioVolumeGroupTest() {
         super(CoreAudioVolumeGroup.TAG);
@@ -107,8 +113,11 @@
     void setupMock() {
         doReturn(MUSIC_GROUP_ID)
                 .when(() -> CoreAudioHelper.getVolumeGroupIdForAudioAttributes(MUSIC_ATTRIBUTES));
-        doReturn(MUSIC_ATTRIBUTES)
-                .when(() ->
+        doReturn(MUSIC_STRATEGY)
+                .when(() -> CoreAudioHelper.getProductStrategyForAudioAttributes(MUSIC_ATTRIBUTES));
+        doReturn(MUSIC_STRATEGY)
+                .when(() -> CoreAudioHelper.getProductStrategyForAudioAttributes(MOVIE_ATTRIBUTES));
+        doReturn(MUSIC_ATTRIBUTES).when(() ->
                         CoreAudioHelper.selectAttributesForVolumeGroupName(eq(MUSIC_GROUP_NAME)));
         when(mMockAudioManager.getMinVolumeIndexForAttributes(MUSIC_ATTRIBUTES))
                 .thenReturn(MUSIC_MIN_INDEX);
@@ -156,9 +165,12 @@
         mOemContext = new CarAudioContext(
                 List.of(OEM_CONTEXT_INFO), /* useCoreAudioRouting= */ true);
 
+        mMusicDeviceAttributes = new AudioDeviceAttributes(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
+                MUSIC_DEVICE_ADDRESS);
         SparseArray<CarAudioDeviceInfo> musicContextToDeviceInfo = new SparseArray<>();
         musicContextToDeviceInfo.put(MUSIC_STRATEGY_ID, mOemInfoMock);
         when(mOemInfoMock.getAddress()).thenReturn(MUSIC_DEVICE_ADDRESS);
+        when(mOemInfoMock.getAudioDevice()).thenReturn(mMusicDeviceAttributes);
         mMusicCoreAudioVolumeGroup = new CoreAudioVolumeGroup(mMockAudioManager, mMusicContext,
                 mSettingsMock, musicContextToDeviceInfo, PRIMARY_AUDIO_ZONE, ZONE_CONFIG_ID,
                 MUSIC_CAR_GROUP_ID, MUSIC_GROUP_NAME, /* useCarVolumeGroupMute= */ false);
@@ -513,4 +525,24 @@
                 .that(mMusicCoreAudioVolumeGroup.getCurrentGainIndex())
                 .isEqualTo(MUSIC_AM_INIT_INDEX + 1);
     }
+
+    @Test
+    public void updateDevices_withCoreAudioRoutingDisabled() {
+        boolean useCoreAudioRouting = false;
+
+        mMusicCoreAudioVolumeGroup.updateDevices(useCoreAudioRouting);
+
+        verify(mMockAudioManager).setPreferredDeviceForStrategy(MUSIC_STRATEGY,
+                mMusicDeviceAttributes);
+    }
+
+    @Test
+    public void updateDevices_withCoreAudioRoutingEnabled() {
+        boolean useCoreAudioRouting = true;
+
+        mMusicCoreAudioVolumeGroup.updateDevices(useCoreAudioRouting);
+
+        verify(mMockAudioManager, never()).setPreferredDeviceForStrategy(MUSIC_STRATEGY,
+                mMusicDeviceAttributes);
+    }
 }