summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Laurent <elaurent@google.com>2023-12-05 15:28:42 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-12-05 15:28:42 +0000
commitff08b3c55f5dd07df55c5516e0efeeb3b594a9c7 (patch)
treee4a5e75f3b3e3c1627d8c493636077e8c3b6eff6
parentc84fa21c390d2426de344dd82a14d199b79d63a6 (diff)
parent47007e641fbbf96624d77a9c809cb20e962dfcf9 (diff)
downloadbase-ff08b3c55f5dd07df55c5516e0efeeb3b594a9c7.tar.gz
Merge changes from topic "anon_bt_address_tm_dev" into tm-dev am: 47007e641f
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/25240035 Change-Id: Ia1201e899bdfaeca021bf7be8c5debc174a32a1e Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--media/java/android/media/AudioDeviceAttributes.java26
-rw-r--r--services/core/java/com/android/server/audio/AdiDeviceState.java11
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java13
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceInventory.java107
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java197
5 files changed, 294 insertions, 60 deletions
diff --git a/media/java/android/media/AudioDeviceAttributes.java b/media/java/android/media/AudioDeviceAttributes.java
index af3c295b8d6c..5a274353f68e 100644
--- a/media/java/android/media/AudioDeviceAttributes.java
+++ b/media/java/android/media/AudioDeviceAttributes.java
@@ -68,7 +68,7 @@ public final class AudioDeviceAttributes implements Parcelable {
/**
* The unique address of the device. Some devices don't have addresses, only an empty string.
*/
- private final @NonNull String mAddress;
+ private @NonNull String mAddress;
/**
* The non-unique name of the device. Some devices don't have names, only an empty string.
* Should not be used as a unique identifier for a device.
@@ -188,6 +188,21 @@ public final class AudioDeviceAttributes implements Parcelable {
/**
* @hide
+ * Copy Constructor.
+ * @param ada the copied AudioDeviceAttributes
+ */
+ public AudioDeviceAttributes(AudioDeviceAttributes ada) {
+ mRole = ada.getRole();
+ mType = ada.getType();
+ mAddress = ada.getAddress();
+ mName = ada.getName();
+ mNativeType = ada.getInternalType();
+ mAudioProfiles = ada.getAudioProfiles();
+ mAudioDescriptors = ada.getAudioDescriptors();
+ }
+
+ /**
+ * @hide
* Returns the role of a device
* @return the role
*/
@@ -218,6 +233,15 @@ public final class AudioDeviceAttributes implements Parcelable {
/**
* @hide
+ * Sets the device address. Only used by audio service.
+ */
+ public void setAddress(@NonNull String address) {
+ Objects.requireNonNull(address);
+ mAddress = address;
+ }
+
+ /**
+ * @hide
* Returns the name of the audio device, or an empty string for devices without one
* @return the device name
*/
diff --git a/services/core/java/com/android/server/audio/AdiDeviceState.java b/services/core/java/com/android/server/audio/AdiDeviceState.java
index a27daf65b9f9..eab1eca90f6c 100644
--- a/services/core/java/com/android/server/audio/AdiDeviceState.java
+++ b/services/core/java/com/android/server/audio/AdiDeviceState.java
@@ -25,6 +25,7 @@ import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import java.util.Objects;
@@ -43,6 +44,8 @@ import java.util.Objects;
private final int mInternalDeviceType;
@NonNull
private final String mDeviceAddress;
+ /** Unique device id from internal device type and address. */
+ private final Pair<Integer, String> mDeviceId;
private boolean mSAEnabled;
private boolean mHasHeadTracker = false;
private boolean mHeadTrackerEnabled;
@@ -68,6 +71,11 @@ import java.util.Objects;
}
mDeviceAddress = isBluetoothDevice(mInternalDeviceType) ? Objects.requireNonNull(
address) : "";
+ mDeviceId = new Pair<>(mInternalDeviceType, mDeviceAddress);
+ }
+
+ public Pair<Integer, String> getDeviceId() {
+ return mDeviceId;
}
@@ -140,7 +148,8 @@ import java.util.Objects;
@Override
public String toString() {
- return "type: " + mDeviceType + "internal type: " + mInternalDeviceType
+ return "type: " + mDeviceType
+ + " internal type: 0x" + Integer.toHexString(mInternalDeviceType)
+ " addr: " + mDeviceAddress + " enabled: " + mSAEnabled
+ " HT: " + mHasHeadTracker + " HTenabled: " + mHeadTrackerEnabled;
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 0682b7c47621..6ccdd827b45a 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -833,8 +833,8 @@ public class AudioDeviceBroker {
}
/*package*/ void registerStrategyPreferredDevicesDispatcher(
- @NonNull IStrategyPreferredDevicesDispatcher dispatcher) {
- mDeviceInventory.registerStrategyPreferredDevicesDispatcher(dispatcher);
+ @NonNull IStrategyPreferredDevicesDispatcher dispatcher, boolean isPrivileged) {
+ mDeviceInventory.registerStrategyPreferredDevicesDispatcher(dispatcher, isPrivileged);
}
/*package*/ void unregisterStrategyPreferredDevicesDispatcher(
@@ -852,8 +852,8 @@ public class AudioDeviceBroker {
}
/*package*/ void registerCapturePresetDevicesRoleDispatcher(
- @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
- mDeviceInventory.registerCapturePresetDevicesRoleDispatcher(dispatcher);
+ @NonNull ICapturePresetDevicesRoleDispatcher dispatcher, boolean isPrivileged) {
+ mDeviceInventory.registerCapturePresetDevicesRoleDispatcher(dispatcher, isPrivileged);
}
/*package*/ void unregisterCapturePresetDevicesRoleDispatcher(
@@ -861,6 +861,11 @@ public class AudioDeviceBroker {
mDeviceInventory.unregisterCapturePresetDevicesRoleDispatcher(dispatcher);
}
+ /* package */ List<AudioDeviceAttributes> anonymizeAudioDeviceAttributesListUnchecked(
+ List<AudioDeviceAttributes> devices) {
+ return mAudioService.anonymizeAudioDeviceAttributesListUnchecked(devices);
+ }
+
/*package*/ void registerCommunicationDeviceDispatcher(
@NonNull ICommunicationDeviceDispatcher dispatcher) {
mCommDevDispatchers.register(dispatcher);
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 25a48f21468e..9524c54acc36 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -40,6 +40,7 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -47,7 +48,9 @@ import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Objects;
@@ -74,31 +77,58 @@ public class AudioDeviceInventory {
private final Object mDeviceInventoryLock = new Object();
@GuardedBy("mDeviceInventoryLock")
- private final ArrayList<AdiDeviceState> mDeviceInventory = new ArrayList<>(0);
-
+ private final HashMap<Pair<Integer, String>, AdiDeviceState> mDeviceInventory = new HashMap<>();
List<AdiDeviceState> getImmutableDeviceInventory() {
synchronized (mDeviceInventoryLock) {
- return List.copyOf(mDeviceInventory);
+ return new ArrayList<AdiDeviceState>(mDeviceInventory.values());
}
}
void addDeviceStateToInventory(AdiDeviceState deviceState) {
synchronized (mDeviceInventoryLock) {
- mDeviceInventory.add(deviceState);
+ mDeviceInventory.put(deviceState.getDeviceId(), deviceState);
+ }
+ }
+
+ /**
+ * Adds a new entry in mDeviceInventory if the AudioDeviceAttributes passed is an sink
+ * Bluetooth device and no corresponding entry already exists.
+ * @param ada the device to add if needed
+ */
+ void addAudioDeviceInInventoryIfNeeded(AudioDeviceAttributes ada) {
+ if (!AudioSystem.isBluetoothOutDevice(ada.getInternalType())) {
+ return;
+ }
+ synchronized (mDeviceInventoryLock) {
+ if (findDeviceStateForAudioDeviceAttributes(ada, ada.getType()) != null) {
+ return;
+ }
+ AdiDeviceState ads = new AdiDeviceState(
+ ada.getType(), ada.getInternalType(), ada.getAddress());
+ mDeviceInventory.put(ads.getDeviceId(), ads);
}
+ mDeviceBroker.persistAudioDeviceSettings();
}
+ /**
+ * Finds the device state that matches the passed {@link AudioDeviceAttributes} and device
+ * type. Note: currently this method only returns a valid device for A2DP and BLE devices.
+ *
+ * @param ada attributes of device to match
+ * @param canonicalDeviceType external device type to match
+ * @return the found {@link AdiDeviceState} matching a cached A2DP or BLE device or
+ * {@code null} otherwise.
+ */
AdiDeviceState findDeviceStateForAudioDeviceAttributes(AudioDeviceAttributes ada,
int canonicalDeviceType) {
final boolean isWireless = isBluetoothDevice(ada.getInternalType());
-
synchronized (mDeviceInventoryLock) {
- for (AdiDeviceState deviceSetting : mDeviceInventory) {
- if (deviceSetting.getDeviceType() == canonicalDeviceType
+ for (AdiDeviceState deviceState : mDeviceInventory.values()) {
+ if (deviceState.getDeviceType() == canonicalDeviceType
&& (!isWireless || ada.getAddress().equals(
- deviceSetting.getDeviceAddress()))) {
- return deviceSetting;
+ deviceState.getDeviceAddress()))) {
+ return deviceState;
}
}
}
@@ -308,7 +338,7 @@ public class AudioDeviceInventory {
+ " devices:" + devices); });
pw.println("\ndevices:\n");
synchronized (mDeviceInventoryLock) {
- for (AdiDeviceState device : mDeviceInventory) {
+ for (AdiDeviceState device : mDeviceInventory.values()) {
pw.println("\t" + device + "\n");
}
}
@@ -695,8 +725,8 @@ public class AudioDeviceInventory {
}
/*package*/ void registerStrategyPreferredDevicesDispatcher(
- @NonNull IStrategyPreferredDevicesDispatcher dispatcher) {
- mPrefDevDispatchers.register(dispatcher);
+ @NonNull IStrategyPreferredDevicesDispatcher dispatcher, boolean isPrivileged) {
+ mPrefDevDispatchers.register(dispatcher, isPrivileged);
}
/*package*/ void unregisterStrategyPreferredDevicesDispatcher(
@@ -730,8 +760,8 @@ public class AudioDeviceInventory {
}
/*package*/ void registerCapturePresetDevicesRoleDispatcher(
- @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
- mDevRoleCapturePresetDispatchers.register(dispatcher);
+ @NonNull ICapturePresetDevicesRoleDispatcher dispatcher, boolean isPrivileged) {
+ mDevRoleCapturePresetDispatchers.register(dispatcher, isPrivileged);
}
/*package*/ void unregisterCapturePresetDevicesRoleDispatcher(
@@ -809,6 +839,9 @@ public class AudioDeviceInventory {
mConnectedDevices.put(deviceKey, new DeviceInfo(
device, deviceName, address, AudioSystem.AUDIO_FORMAT_DEFAULT));
mDeviceBroker.postAccessoryPlugMediaUnmute(device);
+ if (AudioSystem.isBluetoothScoDevice(device)) {
+ addAudioDeviceInInventoryIfNeeded(attributes);
+ }
mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record();
return true;
} else if (!connect && isConnected) {
@@ -1038,8 +1071,9 @@ public class AudioDeviceInventory {
mDeviceBroker.setBluetoothA2dpOnInt(true, true /*fromA2dp*/, eventSource);
// at this point there could be another A2DP device already connected in APM, but it
// doesn't matter as this new one will overwrite the previous one
- final int res = mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
- AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, name),
+ AudioDeviceAttributes ada = new AudioDeviceAttributes(
+ AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, name);
+ final int res = mAudioSystem.setDeviceConnectionState(ada,
AudioSystem.DEVICE_STATE_AVAILABLE, a2dpCodec);
// TODO: log in MediaMetrics once distinction between connection failure and
@@ -1061,8 +1095,7 @@ public class AudioDeviceInventory {
// The convention for head tracking sensors associated with A2DP devices is to
// use a UUID derived from the MAC address as follows:
// time_low = 0, time_mid = 0, time_hi = 0, clock_seq = 0, node = MAC Address
- UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(
- new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address));
+ UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(ada);
final DeviceInfo di = new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name,
address, a2dpCodec, sensorUuid);
final String diKey = di.getKey();
@@ -1073,8 +1106,10 @@ public class AudioDeviceInventory {
mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
setCurrentAudioRouteNameIfPossible(name, true /*fromA2dp*/);
+ addAudioDeviceInInventoryIfNeeded(ada);
}
+
@GuardedBy("mDevicesLock")
private void makeA2dpDeviceUnavailableNow(String address, int a2dpCodec) {
MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "a2dp." + address)
@@ -1168,9 +1203,9 @@ public class AudioDeviceInventory {
final int hearingAidVolIndex = mDeviceBroker.getVssVolumeForDevice(streamType,
AudioSystem.DEVICE_OUT_HEARING_AID);
mDeviceBroker.postSetHearingAidVolumeIndex(hearingAidVolIndex, streamType);
-
- mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
- AudioSystem.DEVICE_OUT_HEARING_AID, address, name),
+ AudioDeviceAttributes ada = new AudioDeviceAttributes(
+ AudioSystem.DEVICE_OUT_HEARING_AID, address, name);
+ mAudioSystem.setDeviceConnectionState(ada,
AudioSystem.DEVICE_STATE_AVAILABLE,
AudioSystem.AUDIO_FORMAT_DEFAULT);
mConnectedDevices.put(
@@ -1181,6 +1216,7 @@ public class AudioDeviceInventory {
mDeviceBroker.postApplyVolumeOnDevice(streamType,
AudioSystem.DEVICE_OUT_HEARING_AID, "makeHearingAidDeviceAvailable");
setCurrentAudioRouteNameIfPossible(name, false /*fromA2dp*/);
+ addAudioDeviceInInventoryIfNeeded(ada);
new MediaMetrics.Item(mMetricsId + "makeHearingAidDeviceAvailable")
.set(MediaMetrics.Property.ADDRESS, address != null ? address : "")
.set(MediaMetrics.Property.DEVICE,
@@ -1217,13 +1253,15 @@ public class AudioDeviceInventory {
*/
mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource);
- AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(device, address, name),
+ AudioDeviceAttributes ada = new AudioDeviceAttributes(device, address, name);
+ AudioSystem.setDeviceConnectionState(ada,
AudioSystem.DEVICE_STATE_AVAILABLE,
AudioSystem.AUDIO_FORMAT_DEFAULT);
mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address),
new DeviceInfo(device, name, address, AudioSystem.AUDIO_FORMAT_DEFAULT));
mDeviceBroker.postAccessoryPlugMediaUnmute(device);
setCurrentAudioRouteNameIfPossible(name, /*fromA2dp=*/false);
+ addAudioDeviceInInventoryIfNeeded(ada);
}
if (streamType == AudioSystem.STREAM_DEFAULT) {
@@ -1524,6 +1562,9 @@ public class AudioDeviceInventory {
final int nbDispatchers = mPrefDevDispatchers.beginBroadcast();
for (int i = 0; i < nbDispatchers; i++) {
try {
+ if (!((Boolean) mPrefDevDispatchers.getBroadcastCookie(i))) {
+ devices = mDeviceBroker.anonymizeAudioDeviceAttributesListUnchecked(devices);
+ }
mPrefDevDispatchers.getBroadcastItem(i).dispatchPrefDevicesChanged(
strategy, devices);
} catch (RemoteException e) {
@@ -1537,6 +1578,9 @@ public class AudioDeviceInventory {
final int nbDispatchers = mDevRoleCapturePresetDispatchers.beginBroadcast();
for (int i = 0; i < nbDispatchers; ++i) {
try {
+ if (!((Boolean) mDevRoleCapturePresetDispatchers.getBroadcastCookie(i))) {
+ devices = mDeviceBroker.anonymizeAudioDeviceAttributesListUnchecked(devices);
+ }
mDevRoleCapturePresetDispatchers.getBroadcastItem(i).dispatchDevicesRoleChanged(
capturePreset, role, devices);
} catch (RemoteException e) {
@@ -1561,19 +1605,20 @@ public class AudioDeviceInventory {
int deviceCatalogSize = 0;
synchronized (mDeviceInventoryLock) {
deviceCatalogSize = mDeviceInventory.size();
- }
- final StringBuilder settingsBuilder = new StringBuilder(
+
+ final StringBuilder settingsBuilder = new StringBuilder(
deviceCatalogSize * AdiDeviceState.getPeristedMaxSize());
- synchronized (mDeviceInventoryLock) {
- for (int i = 0; i < mDeviceInventory.size(); i++) {
- settingsBuilder.append(mDeviceInventory.get(i).toPersistableString());
- if (i != mDeviceInventory.size() - 1) {
- settingsBuilder.append(SETTING_DEVICE_SEPARATOR_CHAR);
- }
+ Iterator<AdiDeviceState> iterator = mDeviceInventory.values().iterator();
+ if (iterator.hasNext()) {
+ settingsBuilder.append(iterator.next().toPersistableString());
+ }
+ while (iterator.hasNext()) {
+ settingsBuilder.append(SETTING_DEVICE_SEPARATOR_CHAR);
+ settingsBuilder.append(iterator.next().toPersistableString());
}
+ return settingsBuilder.toString();
}
- return settingsBuilder.toString();
}
/*package*/ void setDeviceSettings(String settings) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 20d0b483601f..e90f08347c44 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -2656,8 +2656,11 @@ public class AudioService extends IAudioService.Stub
return AudioSystem.ERROR;
}
enforceModifyAudioRoutingPermission();
+
+ devices = retrieveBluetoothAddresses(devices);
+
final String logString = String.format(
- "setPreferredDeviceForStrategy u/pid:%d/%d strat:%d dev:%s",
+ "setPreferredDevicesForStrategy u/pid:%d/%d strat:%d dev:%s",
Binder.getCallingUid(), Binder.getCallingPid(), strategy,
devices.stream().map(e -> e.toString()).collect(Collectors.joining(",")));
sDeviceLogger.log(new AudioEventLogger.StringEvent(logString).printLog(TAG));
@@ -2705,7 +2708,7 @@ public class AudioService extends IAudioService.Stub
status, strategy));
return new ArrayList<AudioDeviceAttributes>();
} else {
- return devices;
+ return anonymizeAudioDeviceAttributesList(devices);
}
}
@@ -2718,7 +2721,8 @@ public class AudioService extends IAudioService.Stub
return;
}
enforceModifyAudioRoutingPermission();
- mDeviceBroker.registerStrategyPreferredDevicesDispatcher(dispatcher);
+ mDeviceBroker.registerStrategyPreferredDevicesDispatcher(
+ dispatcher, isBluetoothPrividged());
}
/** @see AudioManager#removeOnPreferredDevicesForStrategyChangedListener(
@@ -2734,7 +2738,7 @@ public class AudioService extends IAudioService.Stub
}
/**
- * @see AudioManager#setPreferredDeviceForCapturePreset(int, AudioDeviceAttributes)
+ * @see AudioManager#setPreferredDevicesForCapturePreset(int, AudioDeviceAttributes)
*/
public int setPreferredDevicesForCapturePreset(
int capturePreset, List<AudioDeviceAttributes> devices) {
@@ -2753,6 +2757,8 @@ public class AudioService extends IAudioService.Stub
return AudioSystem.ERROR;
}
+ devices = retrieveBluetoothAddresses(devices);
+
final int status = mDeviceBroker.setPreferredDevicesForCapturePresetSync(
capturePreset, devices);
if (status != AudioSystem.SUCCESS) {
@@ -2791,7 +2797,7 @@ public class AudioService extends IAudioService.Stub
status, capturePreset));
return new ArrayList<AudioDeviceAttributes>();
} else {
- return devices;
+ return anonymizeAudioDeviceAttributesList(devices);
}
}
@@ -2805,7 +2811,8 @@ public class AudioService extends IAudioService.Stub
return;
}
enforceModifyAudioRoutingPermission();
- mDeviceBroker.registerCapturePresetDevicesRoleDispatcher(dispatcher);
+ mDeviceBroker.registerCapturePresetDevicesRoleDispatcher(
+ dispatcher, isBluetoothPrividged());
}
/**
@@ -2825,7 +2832,9 @@ public class AudioService extends IAudioService.Stub
public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributes(
@NonNull AudioAttributes attributes) {
enforceQueryStateOrModifyRoutingPermission();
- return getDevicesForAttributesInt(attributes, false /* forVolume */);
+
+ return new ArrayList<AudioDeviceAttributes>(anonymizeAudioDeviceAttributesList(
+ getDevicesForAttributesInt(attributes, false /* forVolume */)));
}
/** @see AudioManager#getAudioDevicesForAttributes(AudioAttributes)
@@ -2835,7 +2844,8 @@ public class AudioService extends IAudioService.Stub
*/
public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributesUnprotected(
@NonNull AudioAttributes attributes) {
- return getDevicesForAttributesInt(attributes, false /* forVolume */);
+ return new ArrayList<AudioDeviceAttributes>(anonymizeAudioDeviceAttributesList(
+ getDevicesForAttributesInt(attributes, false /* forVolume */)));
}
/**
@@ -6657,6 +6667,9 @@ public class AudioService extends IAudioService.Stub
// verify arguments
Objects.requireNonNull(device);
AudioManager.enforceValidVolumeBehavior(deviceVolumeBehavior);
+
+ device = retrieveBluetoothAddress(device);
+
sVolumeLogger.log(new AudioEventLogger.StringEvent("setDeviceVolumeBehavior: dev:"
+ AudioSystem.getOutputDeviceName(device.getInternalType()) + " addr:"
+ device.getAddress() + " behavior:"
@@ -6733,6 +6746,8 @@ public class AudioService extends IAudioService.Stub
// verify permissions
enforceQueryStateOrModifyRoutingPermission();
+ device = retrieveBluetoothAddress(device);
+
return getDeviceVolumeBehaviorInt(device);
}
@@ -6809,6 +6824,9 @@ public class AudioService extends IAudioService.Stub
public void setWiredDeviceConnectionState(AudioDeviceAttributes attributes,
@ConnectionState int state, String caller) {
enforceModifyAudioRoutingPermission();
+
+ attributes = retrieveBluetoothAddress(attributes);
+
if (state != CONNECTION_STATE_CONNECTED
&& state != CONNECTION_STATE_DISCONNECTED) {
throw new IllegalArgumentException("Invalid state " + state);
@@ -6830,6 +6848,9 @@ public class AudioService extends IAudioService.Stub
boolean connected) {
Objects.requireNonNull(device);
enforceModifyAudioRoutingPermission();
+
+ device = retrieveBluetoothAddress(device);
+
mDeviceBroker.setTestDeviceConnectionState(device,
connected ? CONNECTION_STATE_CONNECTED : CONNECTION_STATE_DISCONNECTED);
// simulate a routing update from native
@@ -9135,6 +9156,100 @@ public class AudioService extends IAudioService.Stub
mSpatializerHelper.setFeatureEnabled(mHasSpatializerEffect);
}
+ private boolean isBluetoothPrividged() {
+ return PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.BLUETOOTH_CONNECT)
+ || Binder.getCallingUid() == Process.SYSTEM_UID;
+ }
+
+ List<AudioDeviceAttributes> retrieveBluetoothAddresses(List<AudioDeviceAttributes> devices) {
+ if (isBluetoothPrividged()) {
+ return devices;
+ }
+
+ List<AudioDeviceAttributes> checkedDevices = new ArrayList<AudioDeviceAttributes>();
+ for (AudioDeviceAttributes ada : devices) {
+ if (ada == null) {
+ continue;
+ }
+ checkedDevices.add(retrieveBluetoothAddressUncheked(ada));
+ }
+ return checkedDevices;
+ }
+
+ AudioDeviceAttributes retrieveBluetoothAddress(@NonNull AudioDeviceAttributes ada) {
+ if (isBluetoothPrividged()) {
+ return ada;
+ }
+ return retrieveBluetoothAddressUncheked(ada);
+ }
+
+ AudioDeviceAttributes retrieveBluetoothAddressUncheked(@NonNull AudioDeviceAttributes ada) {
+ Objects.requireNonNull(ada);
+ if (AudioSystem.isBluetoothDevice(ada.getInternalType())) {
+ String anonymizedAddress = anonymizeBluetoothAddress(ada.getAddress());
+ for (AdiDeviceState ads : mDeviceBroker.getImmutableDeviceInventory()) {
+ if (!(AudioSystem.isBluetoothDevice(ads.getInternalDeviceType())
+ && (ada.getInternalType() == ads.getInternalDeviceType())
+ && anonymizedAddress.equals(anonymizeBluetoothAddress(
+ ads.getDeviceAddress())))) {
+ continue;
+ }
+ ada.setAddress(ads.getDeviceAddress());
+ break;
+ }
+ }
+ return ada;
+ }
+
+ /**
+ * Convert a Bluetooth MAC address to an anonymized one when exposed to a non privileged app
+ * Must match the implementation of BluetoothUtils.toAnonymizedAddress()
+ * @param address Mac address to be anonymized
+ * @return anonymized mac address
+ */
+ static String anonymizeBluetoothAddress(String address) {
+ if (address == null || address.length() != "AA:BB:CC:DD:EE:FF".length()) {
+ return null;
+ }
+ return "XX:XX:XX:XX" + address.substring("XX:XX:XX:XX".length());
+ }
+
+ private List<AudioDeviceAttributes> anonymizeAudioDeviceAttributesList(
+ List<AudioDeviceAttributes> devices) {
+ if (isBluetoothPrividged()) {
+ return devices;
+ }
+ return anonymizeAudioDeviceAttributesListUnchecked(devices);
+ }
+
+ /* package */ List<AudioDeviceAttributes> anonymizeAudioDeviceAttributesListUnchecked(
+ List<AudioDeviceAttributes> devices) {
+ List<AudioDeviceAttributes> anonymizedDevices = new ArrayList<AudioDeviceAttributes>();
+ for (AudioDeviceAttributes ada : devices) {
+ anonymizedDevices.add(anonymizeAudioDeviceAttributesUnchecked(ada));
+ }
+ return anonymizedDevices;
+ }
+
+ private AudioDeviceAttributes anonymizeAudioDeviceAttributesUnchecked(
+ AudioDeviceAttributes ada) {
+ if (!AudioSystem.isBluetoothDevice(ada.getInternalType())) {
+ return ada;
+ }
+ AudioDeviceAttributes res = new AudioDeviceAttributes(ada);
+ res.setAddress(anonymizeBluetoothAddress(ada.getAddress()));
+ return res;
+ }
+
+ private AudioDeviceAttributes anonymizeAudioDeviceAttributes(AudioDeviceAttributes ada) {
+ if (isBluetoothPrividged()) {
+ return ada;
+ }
+
+ return anonymizeAudioDeviceAttributesUnchecked(ada);
+ }
+
//==========================================================================================
private boolean readCameraSoundForced() {
return SystemProperties.getBoolean("audio.camerasound.force", false) ||
@@ -9162,13 +9277,16 @@ public class AudioService extends IAudioService.Stub
Objects.requireNonNull(usages);
Objects.requireNonNull(device);
enforceModifyAudioRoutingPermission();
+
+ final AudioDeviceAttributes ada = retrieveBluetoothAddress(device);
+
if (timeOutMs <= 0 || usages.length == 0) {
throw new IllegalArgumentException("Invalid timeOutMs/usagesToMute");
}
Log.i(TAG, "muteAwaitConnection dev:" + device + " timeOutMs:" + timeOutMs
+ " usages:" + Arrays.toString(usages));
- if (mDeviceBroker.isDeviceConnected(device)) {
+ if (mDeviceBroker.isDeviceConnected(ada)) {
// not throwing an exception as there could be a race between a connection (server-side,
// notification of connection in flight) and a mute operation (client-side)
Log.i(TAG, "muteAwaitConnection ignored, device (" + device + ") already connected");
@@ -9180,19 +9298,26 @@ public class AudioService extends IAudioService.Stub
+ mMutingExpectedDevice);
throw new IllegalStateException("muteAwaitConnection already in progress");
}
- mMutingExpectedDevice = device;
+ mMutingExpectedDevice = ada;
mMutedUsagesAwaitingConnection = usages;
- mPlaybackMonitor.muteAwaitConnection(usages, device, timeOutMs);
+ mPlaybackMonitor.muteAwaitConnection(usages, ada, timeOutMs);
}
- dispatchMuteAwaitConnection(cb -> { try {
- cb.dispatchOnMutedUntilConnection(device, usages); } catch (RemoteException e) { } });
+ dispatchMuteAwaitConnection((cb, isPrivileged) -> {
+ try {
+ AudioDeviceAttributes dev = ada;
+ if (!isPrivileged) {
+ dev = anonymizeAudioDeviceAttributesUnchecked(ada);
+ }
+ cb.dispatchOnMutedUntilConnection(dev, usages);
+ } catch (RemoteException e) { }
+ });
}
/** @see AudioManager#getMutingExpectedDevice */
public @Nullable AudioDeviceAttributes getMutingExpectedDevice() {
enforceModifyAudioRoutingPermission();
synchronized (mMuteAwaitConnectionLock) {
- return mMutingExpectedDevice;
+ return anonymizeAudioDeviceAttributes(mMutingExpectedDevice);
}
}
@@ -9201,6 +9326,9 @@ public class AudioService extends IAudioService.Stub
public void cancelMuteAwaitConnection(@NonNull AudioDeviceAttributes device) {
Objects.requireNonNull(device);
enforceModifyAudioRoutingPermission();
+
+ final AudioDeviceAttributes ada = retrieveBluetoothAddress(device);
+
Log.i(TAG, "cancelMuteAwaitConnection for device:" + device);
final int[] mutedUsages;
synchronized (mMuteAwaitConnectionLock) {
@@ -9210,7 +9338,7 @@ public class AudioService extends IAudioService.Stub
Log.i(TAG, "cancelMuteAwaitConnection ignored, no expected device");
return;
}
- if (!device.equalTypeAddress(mMutingExpectedDevice)) {
+ if (!ada.equalTypeAddress(mMutingExpectedDevice)) {
Log.e(TAG, "cancelMuteAwaitConnection ignored, got " + device
+ "] but expected device is" + mMutingExpectedDevice);
throw new IllegalStateException("cancelMuteAwaitConnection for wrong device");
@@ -9220,8 +9348,14 @@ public class AudioService extends IAudioService.Stub
mMutedUsagesAwaitingConnection = null;
mPlaybackMonitor.cancelMuteAwaitConnection("cancelMuteAwaitConnection dev:" + device);
}
- dispatchMuteAwaitConnection(cb -> { try { cb.dispatchOnUnmutedEvent(
- AudioManager.MuteAwaitConnectionCallback.EVENT_CANCEL, device, mutedUsages);
+ dispatchMuteAwaitConnection((cb, isPrivileged) -> {
+ try {
+ AudioDeviceAttributes dev = ada;
+ if (!isPrivileged) {
+ dev = anonymizeAudioDeviceAttributesUnchecked(ada);
+ }
+ cb.dispatchOnUnmutedEvent(
+ AudioManager.MuteAwaitConnectionCallback.EVENT_CANCEL, dev, mutedUsages);
} catch (RemoteException e) { } });
}
@@ -9233,7 +9367,7 @@ public class AudioService extends IAudioService.Stub
boolean register) {
enforceModifyAudioRoutingPermission();
if (register) {
- mMuteAwaitConnectionDispatchers.register(cb);
+ mMuteAwaitConnectionDispatchers.register(cb, isBluetoothPrividged());
} else {
mMuteAwaitConnectionDispatchers.unregister(cb);
}
@@ -9257,8 +9391,14 @@ public class AudioService extends IAudioService.Stub
mPlaybackMonitor.cancelMuteAwaitConnection(
"checkMuteAwaitConnection device " + device + " connected, unmuting");
}
- dispatchMuteAwaitConnection(cb -> { try { cb.dispatchOnUnmutedEvent(
- AudioManager.MuteAwaitConnectionCallback.EVENT_CONNECTION, device, mutedUsages);
+ dispatchMuteAwaitConnection((cb, isPrivileged) -> {
+ try {
+ AudioDeviceAttributes ada = device;
+ if (!isPrivileged) {
+ ada = anonymizeAudioDeviceAttributesUnchecked(device);
+ }
+ cb.dispatchOnUnmutedEvent(AudioManager.MuteAwaitConnectionCallback.EVENT_CONNECTION,
+ ada, mutedUsages);
} catch (RemoteException e) { } });
}
@@ -9278,7 +9418,8 @@ public class AudioService extends IAudioService.Stub
mMutingExpectedDevice = null;
mMutedUsagesAwaitingConnection = null;
}
- dispatchMuteAwaitConnection(cb -> { try {
+ dispatchMuteAwaitConnection((cb, isPrivileged) -> {
+ try {
cb.dispatchOnUnmutedEvent(
AudioManager.MuteAwaitConnectionCallback.EVENT_TIMEOUT,
timedOutDevice, mutedUsages);
@@ -9286,13 +9427,14 @@ public class AudioService extends IAudioService.Stub
}
private void dispatchMuteAwaitConnection(
- java.util.function.Consumer<IMuteAwaitConnectionCallback> callback) {
+ java.util.function.BiConsumer<IMuteAwaitConnectionCallback, Boolean> callback) {
final int nbDispatchers = mMuteAwaitConnectionDispatchers.beginBroadcast();
// lazy initialization as errors unlikely
ArrayList<IMuteAwaitConnectionCallback> errorList = null;
for (int i = 0; i < nbDispatchers; i++) {
try {
- callback.accept(mMuteAwaitConnectionDispatchers.getBroadcastItem(i));
+ callback.accept(mMuteAwaitConnectionDispatchers.getBroadcastItem(i),
+ (Boolean) mMuteAwaitConnectionDispatchers.getBroadcastCookie(i));
} catch (Exception e) {
if (errorList == null) {
errorList = new ArrayList<>(1);
@@ -11545,6 +11687,9 @@ public class AudioService extends IAudioService.Stub
@NonNull AudioDeviceAttributes device, @IntRange(from = 0) long delayMillis) {
Objects.requireNonNull(device, "device must not be null");
enforceModifyAudioRoutingPermission();
+
+ device = retrieveBluetoothAddress(device);
+
final String getterKey = "additional_output_device_delay="
+ device.getInternalType() + "," + device.getAddress(); // "getter" key as an id.
final String setterKey = getterKey + "," + delayMillis; // append the delay for setter
@@ -11565,6 +11710,9 @@ public class AudioService extends IAudioService.Stub
@IntRange(from = 0)
public long getAdditionalOutputDeviceDelay(@NonNull AudioDeviceAttributes device) {
Objects.requireNonNull(device, "device must not be null");
+
+ device = retrieveBluetoothAddress(device);
+
final String key = "additional_output_device_delay";
final String reply = AudioSystem.getParameters(
key + "=" + device.getInternalType() + "," + device.getAddress());
@@ -11592,6 +11740,9 @@ public class AudioService extends IAudioService.Stub
@IntRange(from = 0)
public long getMaxAdditionalOutputDeviceDelay(@NonNull AudioDeviceAttributes device) {
Objects.requireNonNull(device, "device must not be null");
+
+ device = retrieveBluetoothAddress(device);
+
final String key = "max_additional_output_device_delay";
final String reply = AudioSystem.getParameters(
key + "=" + device.getInternalType() + "," + device.getAddress());