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);
+ }
}