Added API to manage uids in each zone
Added API to set, get zones for each uid. Also added API to
request zone ids.
Bug: 121344631
Test: Though the api is not currently used I build and ran emulator and
everything continues to work as expected.
Change-Id: Iec2ffc7fa78936692ca32a9c8a2be02b0454b03e
diff --git a/car-lib/src/android/car/media/CarAudioManager.java b/car-lib/src/android/car/media/CarAudioManager.java
index 6c2e1cd..c3338a9 100644
--- a/car-lib/src/android/car/media/CarAudioManager.java
+++ b/car-lib/src/android/car/media/CarAudioManager.java
@@ -412,6 +412,71 @@
}
/**
+ * Gets the audio zones currently available
+ *
+ * @return audio zone ids
+ * @hide
+ */
+ @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
+ public @NonNull int[] getAudioZoneIds() {
+ try {
+ return mService.getAudioZoneIds();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the audio zone id currently mapped to uId,
+ * defaults to PRIMARY_AUDIO_ZONE if no mapping exist
+ *
+ * @param uid The uid to map
+ * @return zone id mapped to uid
+ * @hide
+ */
+ @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
+ public int getZoneIdForUid(int uid) {
+ try {
+ return mService.getZoneIdForUid(uid);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Maps the audio zone id to uid
+ *
+ * @param zoneId The audio zone id
+ * @param uid The uid to map
+ * @return true if the uid is successfully mapped
+ * @hide
+ */
+ @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
+ public boolean setZoneIdForUid(int zoneId, int uid) {
+ try {
+ return mService.setZoneIdForUid(zoneId, uid);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Clears the current zone mapping of the uid
+ *
+ * @param uid The uid to clear
+ * @return true if the zone was successfully cleared
+ * @hide
+ */
+ @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
+ public boolean clearZoneIdForUid(int uid) {
+ try {
+ return mService.clearZoneIdForUid(uid);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Gets array of {@link AudioAttributes} usages for a volume group in a zone.
*
* @param zoneId The zone id whose volume group is queried.
diff --git a/car-lib/src/android/car/media/ICarAudio.aidl b/car-lib/src/android/car/media/ICarAudio.aidl
index 178f75a..a2f33ad 100644
--- a/car-lib/src/android/car/media/ICarAudio.aidl
+++ b/car-lib/src/android/car/media/ICarAudio.aidl
@@ -43,6 +43,11 @@
int getVolumeGroupIdForUsage(int zoneId, int usage);
int[] getUsagesForVolumeGroupId(int zoneId, int groupId);
+ int[] getAudioZoneIds();
+ int getZoneIdForUid(int uid);
+ boolean setZoneIdForUid(int zoneId, int uid);
+ boolean clearZoneIdForUid(int uid);
+
/**
* IBinder is ICarVolumeCallback but passed as IBinder due to aidl hidden.
*/
diff --git a/service/src/com/android/car/audio/CarAudioFocus.java b/service/src/com/android/car/audio/CarAudioFocus.java
index 45c2903..c2e0a1b 100644
--- a/service/src/com/android/car/audio/CarAudioFocus.java
+++ b/service/src/com/android/car/audio/CarAudioFocus.java
@@ -490,17 +490,22 @@
}
}
- public synchronized void dump(PrintWriter writer) {
- writer.println("*CarAudioFocus*");
+ /**
+ * dumps the current state of the CarAudioFocus object
+ * @param indent indent to add to each line in the current stream
+ * @param writer stream to write to
+ */
+ public synchronized void dump(String indent, PrintWriter writer) {
+ writer.printf("%s*CarAudioFocus*\n", indent);
- writer.println(" Current Focus Holders:");
+ writer.printf("%s\tCurrent Focus Holders:\n", indent);
for (String clientId : mFocusHolders.keySet()) {
- System.out.println(clientId);
+ writer.printf("%s\t\t%s\n", indent, clientId);
}
- writer.println(" Transient Focus Losers:");
+ writer.printf("%s\tTransient Focus Losers:\n", indent);
for (String clientId : mFocusLosers.keySet()) {
- System.out.println(clientId);
+ writer.printf("%s\t\t%s\n", indent, clientId);
}
}
diff --git a/service/src/com/android/car/audio/CarAudioService.java b/service/src/com/android/car/audio/CarAudioService.java
index 88d3463..8cde85b 100644
--- a/service/src/com/android/car/audio/CarAudioService.java
+++ b/service/src/com/android/car/audio/CarAudioService.java
@@ -65,7 +65,9 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.stream.Collectors;
@@ -191,6 +193,10 @@
private String mCarAudioConfigurationPath;
private CarAudioZone[] mCarAudioZones;
+ // TODO do not store uid mapping here instead use the uid
+ // device affinity in audio policy when available
+ private Map<Integer, Integer> mUidToZoneMap;
+
public CarAudioService(Context context) {
mContext = context;
mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
@@ -198,6 +204,7 @@
mUseDynamicRouting = mContext.getResources().getBoolean(R.bool.audioUseDynamicRouting);
mPersistMasterMuteState = mContext.getResources().getBoolean(
R.bool.audioPersistMasterMuteState);
+ mUidToZoneMap = new HashMap<>();
}
/**
@@ -277,7 +284,18 @@
for (CarAudioZone zone : mCarAudioZones) {
zone.dump("\t", writer);
}
+ writer.println();
+ writer.println("\tUID to Zone Mapping:");
+ for (int callingId : mUidToZoneMap.keySet()) {
+ writer.printf("\t\tUID %d mapped to zone %d\n",
+ callingId,
+ mUidToZoneMap.get(callingId));
+ }
+ //Print focus handler info
+ writer.println();
+ mFocusHandler.dump("\t", writer);
}
+
}
@Override
@@ -736,6 +754,127 @@
}
}
+ /**
+ * Gets the ids of all available audio zones
+ *
+ * @return Array of available audio zones ids
+ */
+ @Override
+ public @NonNull int[] getAudioZoneIds() {
+ enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS);
+ synchronized (mImplLock) {
+ return Arrays.stream(mCarAudioZones).mapToInt(CarAudioZone::getId).toArray();
+ }
+ }
+
+ /**
+ * Gets the audio zone id currently mapped to uid,
+ * defaults to PRIMARY_AUDIO_ZONE if no mapping exist
+ *
+ * @param uid The uid
+ * @return zone id mapped to uid
+ */
+ @Override
+ public int getZoneIdForUid(int uid) {
+ enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS);
+ synchronized (mImplLock) {
+ if (!mUidToZoneMap.containsKey(uid)) {
+ Log.i(CarLog.TAG_AUDIO, "getZoneIdForUid uid "
+ + uid + " does not have a zone. Defaulting to PRIMARY_AUDIO_ZONE: "
+ + CarAudioManager.PRIMARY_AUDIO_ZONE);
+
+ //Must be added to PRIMARY_AUDIO_ZONE otherwise audio may be routed to other devices
+ // that match the audio criterion (i.e. usage)
+ setZoneIdForUidNoCheckLocked(CarAudioManager.PRIMARY_AUDIO_ZONE, uid);
+ }
+
+ return mUidToZoneMap.get(uid);
+ }
+ }
+
+ /**
+ * Maps the audio zone id to uid
+ *
+ * @param zoneId The audio zone id
+ * @param uid The uid to map
+ * @return true if the device affinities, for devices in zone, are successfully set
+ */
+ @Override
+ public boolean setZoneIdForUid(int zoneId, int uid) {
+ enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS);
+ synchronized (mImplLock) {
+ Log.i(CarLog.TAG_AUDIO, "setZoneIdForUid Calling uid "
+ + uid + " mapped to : "
+ + zoneId);
+ //if the current uid is in the list
+ //remove it from the list
+ if (checkAndRemoveUidLocked(uid)) {
+ return setZoneIdForUidNoCheckLocked(zoneId, uid);
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Removes the current mapping of the Uid
+ *
+ * @param uid The uid to remove
+ * return true if all the devices affinities currently
+ * mapped to uid are successfully removed
+ */
+ @Override
+ public boolean clearZoneIdForUid(int uid) {
+ enforcePermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS);
+ synchronized (mImplLock) {
+ return checkAndRemoveUidLocked(uid);
+ }
+ }
+
+ /**
+ * Sets the zone id for uid
+ * @param zoneId zone id to map to uid
+ * @param uid uid to map
+ * @return true if setting uid device affinity is successful
+ */
+ private boolean setZoneIdForUidNoCheckLocked(int zoneId, int uid) {
+ Log.d(CarLog.TAG_AUDIO, "setZoneIdForUidNoCheck Calling uid "
+ + uid + " mapped to " + zoneId);
+ //Request to add uid device affinity
+ if (mAudioPolicy.setUidDeviceAffinity(uid, mCarAudioZones[zoneId].getAudioDeviceInfos())) {
+ // TODO do not store uid mapping here instead use the uid
+ // device affinity in audio policy when available
+ mUidToZoneMap.put(uid, zoneId);
+ return true;
+ }
+ Log.w(CarLog.TAG_AUDIO, "setZoneIdForUidNoCheck Failed set device affinity for uid "
+ + uid + " in zone " + zoneId);
+ return false;
+ }
+
+ /**
+ * Checks if the uid is contained in map and removes it if so
+ * @param uid uid to remove from mapping
+ * @return true if the removing the uid mapping succeeds
+ */
+ private boolean checkAndRemoveUidLocked(int uid) {
+ if (mUidToZoneMap.containsKey(uid)) {
+ int zoneId = mUidToZoneMap.get(uid);
+ Log.i(CarLog.TAG_AUDIO, "checkAndRemoveUid removing Calling uid "
+ + uid + " from zone " + zoneId);
+ if (mAudioPolicy.removeUidDeviceAffinity(uid)) {
+ // TODO use the uid device affinity in audio policy when available
+ mUidToZoneMap.remove(uid);
+ return true;
+ }
+ //failed to remove device affinity from zone devices
+ Log.w(CarLog.TAG_AUDIO,
+ "checkAndRemoveUid Failed remove device affinity for uid "
+ + uid + " in zone " + zoneId);
+ return false;
+ }
+ return true;
+ }
+
@Override
public void registerVolumeCallback(@NonNull IBinder binder) {
synchronized (mImplLock) {
diff --git a/service/src/com/android/car/audio/CarAudioZone.java b/service/src/com/android/car/audio/CarAudioZone.java
index 36bbf42..4bdf0b7 100644
--- a/service/src/com/android/car/audio/CarAudioZone.java
+++ b/service/src/com/android/car/audio/CarAudioZone.java
@@ -16,6 +16,7 @@
package com.android.car.audio;
import android.car.media.CarAudioManager;
+import android.media.AudioDeviceInfo;
import android.util.Log;
import android.view.DisplayAddress;
@@ -74,6 +75,19 @@
return mVolumeGroups.get(groupId);
}
+ /**
+ * @return Snapshot of available {@link AudioDeviceInfo}s in List.
+ */
+ List<AudioDeviceInfo> getAudioDeviceInfos() {
+ final List<AudioDeviceInfo> devices = new ArrayList<>();
+ for (CarVolumeGroup group : mVolumeGroups) {
+ for (int busNumber : group.getBusNumbers()) {
+ devices.add(group.getCarAudioDeviceInfoForBus(busNumber).getAudioDeviceInfo());
+ }
+ }
+ return devices;
+ }
+
int getVolumeGroupCount() {
return mVolumeGroups.size();
}