Merge "Added audio zone info to occupant zone service." into rvc-dev
diff --git a/car-lib/src/android/car/ICarOccupantZone.aidl b/car-lib/src/android/car/ICarOccupantZone.aidl
index 904bc84..09335a7 100644
--- a/car-lib/src/android/car/ICarOccupantZone.aidl
+++ b/car-lib/src/android/car/ICarOccupantZone.aidl
@@ -30,7 +30,6 @@
int getDisplayType(in int displayId);
int getUserForOccupant(in int occupantZoneId);
int getOccupantZoneIdForUserId(in int userId);
- void setAudioZoneIdsForOccupantZoneIds(in int[] audioZoneIds, in int[] occupantZoneIds);
void registerCallback(in ICarOccupantZoneCallback callback);
void unregisterCallback(in ICarOccupantZoneCallback callback);
}
diff --git a/service/src/com/android/car/CarOccupantZoneService.java b/service/src/com/android/car/CarOccupantZoneService.java
index 9fd84fb..dae1667 100644
--- a/service/src/com/android/car/CarOccupantZoneService.java
+++ b/service/src/com/android/car/CarOccupantZoneService.java
@@ -39,13 +39,13 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
+import android.util.SparseIntArray;
import android.view.Display;
import android.view.DisplayAddress;
import com.android.car.user.CarUserService;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -61,6 +61,8 @@
public final class CarOccupantZoneService extends ICarOccupantZone.Stub
implements CarServiceBase {
+ private static final int INVALID_OCCUPANT_ZONE_ID = -1;
+
private final Object mLock = new Object();
private final Context mContext;
private final DisplayManager mDisplayManager;
@@ -98,7 +100,7 @@
/** key: audio zone id */
@GuardedBy("mLock")
- private final HashMap<Integer, Integer> mAudioZoneConfig = new HashMap<>();
+ private final SparseIntArray mAudioZoneIdToOccupantZoneIdMapping = new SparseIntArray();
@VisibleForTesting
static class DisplayInfo {
@@ -314,7 +316,7 @@
synchronized (mLock) {
mOccupantsConfig.clear();
mDisplayConfigs.clear();
- mAudioZoneConfig.clear();
+ mAudioZoneIdToOccupantZoneIdMapping.clear();
mActiveOccupantConfigs.clear();
}
}
@@ -340,9 +342,9 @@
/** Return cloned mAudioConfigs for testing */
@VisibleForTesting
@NonNull
- HashMap<Integer, Integer> getAudioConfigs() {
+ SparseIntArray getAudioConfigs() {
synchronized (mLock) {
- return (HashMap<Integer, Integer>) mAudioZoneConfig.clone();
+ return mAudioZoneIdToOccupantZoneIdMapping.clone();
}
}
@@ -369,10 +371,11 @@
writer.println(" port=" + Integer.toHexString(entry.getKey())
+ " config=" + entry.getValue().toString());
}
- writer.println("**mAudioZoneConfigs**");
- for (Map.Entry<Integer, Integer> entry : mAudioZoneConfig.entrySet()) {
- writer.println(" audioZoneId=" + Integer.toHexString(entry.getKey())
- + " zoneId=" + entry.getValue());
+ writer.println("**mAudioZoneIdToOccupantZoneIdMapping**");
+ for (int index = 0; index < mAudioZoneIdToOccupantZoneIdMapping.size(); index++) {
+ int audioZoneId = mAudioZoneIdToOccupantZoneIdMapping.keyAt(index);
+ writer.println(" audioZoneId=" + Integer.toHexString(audioZoneId)
+ + " zoneId=" + mAudioZoneIdToOccupantZoneIdMapping.get(audioZoneId));
}
writer.println("**mActiveOccupantConfigs**");
for (Map.Entry<Integer, OccupantConfig> entry : mActiveOccupantConfigs.entrySet()) {
@@ -445,9 +448,10 @@
}
private int getAudioZoneIdForOccupantLocked(int occupantZoneId) {
- for (Map.Entry<Integer, Integer> entry : mAudioZoneConfig.entrySet()) {
- if (occupantZoneId == entry.getValue()) {
- return entry.getKey();
+ for (int index = 0; index < mAudioZoneIdToOccupantZoneIdMapping.size(); index++) {
+ int audioZoneId = mAudioZoneIdToOccupantZoneIdMapping.keyAt(index);
+ if (occupantZoneId == mAudioZoneIdToOccupantZoneIdMapping.get(audioZoneId)) {
+ return audioZoneId;
}
}
return CarAudioManager.INVALID_AUDIO_ZONE;
@@ -457,8 +461,9 @@
public CarOccupantZoneManager.OccupantZoneInfo getOccupantForAudioZoneId(int audioZoneId) {
enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS);
synchronized (mLock) {
- Integer occupantZoneId = mAudioZoneConfig.get(audioZoneId);
- if (occupantZoneId == null) {
+ int occupantZoneId = mAudioZoneIdToOccupantZoneIdMapping.get(audioZoneId,
+ INVALID_OCCUPANT_ZONE_ID);
+ if (occupantZoneId == INVALID_OCCUPANT_ZONE_ID) {
return null;
}
// To support headless zones return the occupant configuration.
@@ -521,19 +526,23 @@
}
}
- @Override
- public void setAudioZoneIdsForOccupantZoneIds(@NonNull int[] audioZoneIds,
- @NonNull int[] occupantZoneIds) {
- Objects.requireNonNull(audioZoneIds, "audioZoneIds can not be null");
- Objects.requireNonNull(audioZoneIds, "occupantZoneIds can not be null");
- Preconditions.checkArgument(audioZoneIds.length == occupantZoneIds.length,
- "audioZoneIds and occupantZoneIds must have the same size.");
- boolean activeConfigChange = false;
+ /**
+ * Sets the mapping for audio zone id to occupant zone id.
+ *
+ * @param audioZoneIdToOccupantZoneMapping map for audio zone id, where key is the audio zone id
+ * and value is the occupant zone id.
+ */
+ public void setAudioZoneIdsForOccupantZoneIds(
+ @NonNull SparseIntArray audioZoneIdToOccupantZoneMapping) {
+ Objects.requireNonNull(audioZoneIdToOccupantZoneMapping,
+ "audioZoneIdToOccupantZoneMapping can not be null");
synchronized (mLock) {
- validateOccupantZoneIdsLocked(occupantZoneIds);
- mAudioZoneConfig.clear();
- for (int i = 0; i < audioZoneIds.length; i++) {
- mAudioZoneConfig.put(audioZoneIds[i], occupantZoneIds[i]);
+ validateOccupantZoneIdsLocked(audioZoneIdToOccupantZoneMapping);
+ mAudioZoneIdToOccupantZoneIdMapping.clear();
+ for (int index = 0; index < audioZoneIdToOccupantZoneMapping.size(); index++) {
+ int audioZoneId = audioZoneIdToOccupantZoneMapping.keyAt(index);
+ mAudioZoneIdToOccupantZoneIdMapping.put(audioZoneId,
+ audioZoneIdToOccupantZoneMapping.get(audioZoneId));
}
//If there are any active displays for the zone send change event
handleAudioZoneChangesLocked();
@@ -541,10 +550,12 @@
sendConfigChangeEvent(CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_AUDIO);
}
- private void validateOccupantZoneIdsLocked(int[] occupantZoneIds) {
- for (int i = 0; i < occupantZoneIds.length; i++) {
- if (!mOccupantsConfig.containsKey(occupantZoneIds[i])) {
- throw new IllegalArgumentException("occupantZoneId " + occupantZoneIds[i]
+ private void validateOccupantZoneIdsLocked(SparseIntArray audioZoneIdToOccupantZoneMapping) {
+ for (int i = 0; i < audioZoneIdToOccupantZoneMapping.size(); i++) {
+ int occupantZoneId =
+ audioZoneIdToOccupantZoneMapping.get(audioZoneIdToOccupantZoneMapping.keyAt(i));
+ if (!mOccupantsConfig.containsKey(occupantZoneId)) {
+ throw new IllegalArgumentException("occupantZoneId " + occupantZoneId
+ " does not exist.");
}
}
@@ -813,8 +824,9 @@
}
private void handleAudioZoneChangesLocked() {
- for (Map.Entry<Integer, Integer> entry: mAudioZoneConfig.entrySet()) {
- int occupantZoneId = entry.getValue();
+ for (int index = 0; index < mAudioZoneIdToOccupantZoneIdMapping.size(); index++) {
+ int audioZoneId = mAudioZoneIdToOccupantZoneIdMapping.keyAt(index);
+ int occupantZoneId = mAudioZoneIdToOccupantZoneIdMapping.get(audioZoneId);
OccupantConfig occupantConfig =
mActiveOccupantConfigs.get(occupantZoneId);
if (occupantConfig == null) {
@@ -822,7 +834,7 @@
continue;
}
// Found an active configuration, add audio to it.
- occupantConfig.audioZoneId = entry.getKey();
+ occupantConfig.audioZoneId = audioZoneId;
}
}
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index 1923d9a..0926776 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -260,6 +260,7 @@
CarLocalServices.addService(PerUserCarServiceHelper.class, mPerUserCarServiceHelper);
CarLocalServices.addService(FixedActivityService.class, mFixedActivityService);
CarLocalServices.addService(VmsNewBrokerService.class, mVmsBrokerService);
+ CarLocalServices.addService(CarOccupantZoneService.class, mCarOccupantZoneService);
// Be careful with order. Service depending on other service should be inited later.
List<CarServiceBase> allServices = new ArrayList<>();
diff --git a/service/src/com/android/car/audio/CarAudioService.java b/service/src/com/android/car/audio/CarAudioService.java
index 63cda19..b0c80a1 100644
--- a/service/src/com/android/car/audio/CarAudioService.java
+++ b/service/src/com/android/car/audio/CarAudioService.java
@@ -51,11 +51,13 @@
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
+import android.util.SparseIntArray;
import android.view.DisplayAddress;
import android.view.KeyEvent;
import com.android.car.CarLocalServices;
import com.android.car.CarLog;
+import com.android.car.CarOccupantZoneService;
import com.android.car.CarServiceBase;
import com.android.car.R;
import com.android.car.audio.CarAudioContext.AudioContext;
@@ -122,6 +124,8 @@
private final CarUserService.UserCallback mReceiver = new CarAudioServiceUserCallback();
+ private CarOccupantZoneService mOccupantZoneService;
+
private final AudioPolicy.AudioPolicyVolumeCallback mAudioPolicyVolumeCallback =
new AudioPolicy.AudioPolicyVolumeCallback() {
@Override
@@ -191,6 +195,7 @@
private AudioPolicy mAudioPolicy;
private CarZonesAudioFocus mFocusHandler;
private String mCarAudioConfigurationPath;
+ private SparseIntArray mAudioZoneIdToOccupantZoneIdMapping;
private CarAudioZone[] mCarAudioZones;
private final CarVolumeCallbackHandler mCarVolumeCallbackHandler;
@@ -218,6 +223,7 @@
public void init() {
synchronized (mImplLock) {
CarLocalServices.getService(CarUserService.class).addUserCallback(mReceiver);
+ mOccupantZoneService = CarLocalServices.getService(CarOccupantZoneService.class);
if (mUseDynamicRouting) {
setupDynamicRouting();
} else {
@@ -422,6 +428,8 @@
try (InputStream inputStream = new FileInputStream(mCarAudioConfigurationPath)) {
CarAudioZonesHelper zonesHelper = new CarAudioZonesHelper(mContext, inputStream,
carAudioDeviceInfos, inputDevices);
+ mAudioZoneIdToOccupantZoneIdMapping =
+ zonesHelper.getCarAudioZoneIdToOccupantZoneIdMapping();
return zonesHelper.loadAudioZones();
} catch (IOException | XmlPullParserException e) {
throw new RuntimeException("Failed to parse audio zone configuration", e);
@@ -494,6 +502,12 @@
if (r != AudioManager.SUCCESS) {
throw new RuntimeException("registerAudioPolicy failed " + r);
}
+
+ setupOccupantZoneInfo();
+ }
+
+ private void setupOccupantZoneInfo() {
+ mOccupantZoneService.setAudioZoneIdsForOccupantZoneIds(mAudioZoneIdToOccupantZoneIdMapping);
}
/**
diff --git a/tests/carservice_unit_test/src/com/android/car/CarOccupantZoneServiceTest.java b/tests/carservice_unit_test/src/com/android/car/CarOccupantZoneServiceTest.java
index 691bdf4..2db5555 100644
--- a/tests/carservice_unit_test/src/com/android/car/CarOccupantZoneServiceTest.java
+++ b/tests/carservice_unit_test/src/com/android/car/CarOccupantZoneServiceTest.java
@@ -24,17 +24,20 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
+import static org.testng.Assert.expectThrows;
import android.app.ActivityManager;
import android.car.Car;
import android.car.CarOccupantZoneManager;
import android.car.CarOccupantZoneManager.OccupantZoneInfo;
import android.car.VehicleAreaSeat;
+import android.car.media.CarAudioManager;
import android.content.Context;
import android.content.res.Resources;
import android.hardware.display.DisplayManager;
import android.os.Looper;
import android.os.UserHandle;
+import android.util.SparseIntArray;
import android.view.Display;
import android.view.DisplayAddress;
@@ -104,6 +107,13 @@
"occupantZoneId=3,occupantType=REAR_PASSENGER,seatRow=2,seatSide=right"
};
+ private static final int PRIMARY_AUDIO_ZONE_ID = 0;
+ private static final int PRIMARY_AUDIO_ZONE_ID_OCCUPANT = 0;
+ private static final int SECONDARY_AUDIO_ZONE_ID = 1;
+ private static final int SECONDARY_AUDIO_ZONE_ID_OCCUPANT = 3;
+ private static final int UNMAPPED_AUDIO_ZONE_ID_OCCUPANT = 2;
+ private static final int INVALID_AUDIO_ZONE_ID_OCCUPANT = 100;
+
// LHD : Left Hand Drive
private final OccupantZoneInfo mZoneDriverLHD = new OccupantZoneInfo(0,
CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER,
@@ -223,6 +233,13 @@
assertThat(mZoneRearRight).isEqualTo(configs.get(3));
}
+ @Test
+ public void testDefaultAudioZoneConfig() {
+ mService.init();
+ SparseIntArray audioConfigs = mService.getAudioConfigs();
+ assertThat(audioConfigs.size()).isEqualTo(0);
+ }
+
/** RHD: Right Hand Drive */
@Test
public void testDefaultOccupantConfigForRHD() {
@@ -283,6 +300,84 @@
}
@Test
+ public void testSetAudioConfigMapping() {
+ mService.init();
+
+ SparseIntArray audioZoneIdToOccupantZoneMapping =
+ getDefaultAudioZoneToOccupantZoneMapping();
+
+ mService.setAudioZoneIdsForOccupantZoneIds(audioZoneIdToOccupantZoneMapping);
+
+ assertThat(mService.getAudioZoneIdForOccupant(PRIMARY_AUDIO_ZONE_ID_OCCUPANT))
+ .isEqualTo(PRIMARY_AUDIO_ZONE_ID);
+
+ assertThat(mService.getAudioZoneIdForOccupant(SECONDARY_AUDIO_ZONE_ID_OCCUPANT))
+ .isEqualTo(SECONDARY_AUDIO_ZONE_ID);
+ }
+
+ private SparseIntArray getDefaultAudioZoneToOccupantZoneMapping() {
+ SparseIntArray audioZoneIdToOccupantZoneMapping = new SparseIntArray(2);
+ audioZoneIdToOccupantZoneMapping.put(PRIMARY_AUDIO_ZONE_ID,
+ PRIMARY_AUDIO_ZONE_ID_OCCUPANT);
+ audioZoneIdToOccupantZoneMapping.put(SECONDARY_AUDIO_ZONE_ID,
+ SECONDARY_AUDIO_ZONE_ID_OCCUPANT);
+ return audioZoneIdToOccupantZoneMapping;
+ }
+
+ @Test
+ public void testOccupantZoneConfigInfoForAudio() {
+ mService.init();
+ SparseIntArray audioZoneIdToOccupantZoneMapping =
+ getDefaultAudioZoneToOccupantZoneMapping();
+
+ HashMap<Integer, CarOccupantZoneManager.OccupantZoneInfo> occupantZoneConfigs =
+ mService.getOccupantsConfig();
+
+ mService.setAudioZoneIdsForOccupantZoneIds(audioZoneIdToOccupantZoneMapping);
+
+ CarOccupantZoneManager.OccupantZoneInfo primaryOccupantInfo =
+ mService.getOccupantForAudioZoneId(PRIMARY_AUDIO_ZONE_ID);
+ assertThat(primaryOccupantInfo).isEqualTo(
+ occupantZoneConfigs.get(PRIMARY_AUDIO_ZONE_ID_OCCUPANT));
+
+ CarOccupantZoneManager.OccupantZoneInfo secondaryOccupantInfo =
+ mService.getOccupantForAudioZoneId(SECONDARY_AUDIO_ZONE_ID);
+ assertThat(secondaryOccupantInfo).isEqualTo(
+ occupantZoneConfigs.get(SECONDARY_AUDIO_ZONE_ID_OCCUPANT));
+
+ CarOccupantZoneManager.OccupantZoneInfo nullOccupantInfo =
+ mService.getOccupantForAudioZoneId(UNMAPPED_AUDIO_ZONE_ID_OCCUPANT);
+ assertThat(nullOccupantInfo).isNull();
+ }
+
+ @Test
+ public void testMissingAudioConfigMapping() {
+ mService.init();
+ SparseIntArray audioZoneIdToOccupantZoneMapping =
+ getDefaultAudioZoneToOccupantZoneMapping();
+
+ mService.setAudioZoneIdsForOccupantZoneIds(audioZoneIdToOccupantZoneMapping);
+
+ assertThat(mService.getAudioZoneIdForOccupant(UNMAPPED_AUDIO_ZONE_ID_OCCUPANT))
+ .isEqualTo(CarAudioManager.INVALID_AUDIO_ZONE);
+ }
+
+ @Test
+ public void testSetInvalidAudioConfigMapping() {
+ mService.init();
+ SparseIntArray audioZoneIdToOccupantZoneMapping = new SparseIntArray(2);
+ audioZoneIdToOccupantZoneMapping.put(PRIMARY_AUDIO_ZONE_ID,
+ PRIMARY_AUDIO_ZONE_ID_OCCUPANT);
+ audioZoneIdToOccupantZoneMapping.put(SECONDARY_AUDIO_ZONE_ID,
+ INVALID_AUDIO_ZONE_ID_OCCUPANT);
+ IllegalArgumentException thrown =
+ expectThrows(IllegalArgumentException.class,
+ () -> mService.setAudioZoneIdsForOccupantZoneIds(
+ audioZoneIdToOccupantZoneMapping));
+ thrown.getMessage().contains("does not exist");
+ }
+
+ @Test
public void testActiveOccupantConfigs() {
mService.init();
@@ -606,4 +701,29 @@
mService.mUserCallback.onSwitchUser(0);
assertThat(waitForConfigChangeEventAndAssertFlag(eventWaitTimeMs, 0)).isFalse();
}
+
+ @Test
+ public void testManagerRegisterUnregisterForAudioConfigs() {
+ mService.init();
+
+ long eventWaitTimeMs = 300;
+
+ mManager.registerOccupantZoneConfigChangeListener(mChangeListener);
+
+ resetConfigChangeEventWait();
+
+ SparseIntArray audioZoneIdToOccupantZoneMapping =
+ getDefaultAudioZoneToOccupantZoneMapping();
+
+ mService.setAudioZoneIdsForOccupantZoneIds(audioZoneIdToOccupantZoneMapping);
+
+ assertThat(waitForConfigChangeEventAndAssertFlag(eventWaitTimeMs,
+ CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_AUDIO)).isTrue();
+
+ resetConfigChangeEventWait();
+ mManager.unregisterOccupantZoneConfigChangeListener(mChangeListener);
+ mService.setAudioZoneIdsForOccupantZoneIds(audioZoneIdToOccupantZoneMapping);
+ assertThat(waitForConfigChangeEventAndAssertFlag(eventWaitTimeMs,
+ CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_AUDIO)).isFalse();
+ }
}