Skip to content

Commit a035450

Browse files
Eric LaurentAndroid Build Coastguard Worker
authored andcommitted
AudioService: anonymize Bluetooth MAC addresses
Make sure APIs returning AudioDeviceAttributes from AudioService anonymize the Bluetooth MAC addresses because those are considered privacy sensitive. Only expose the full MAC address to system and apps with BLUETOOTH_CONNECT permission. setters, getters and listeners for preferred device for strategy, preferred device for capture preset and mute await connection are modified: - when entering AudioService, full MAC addresses are retrieved based on the known Bluetooth devices stored in AudioDeviceInventory.mDeviceInventory - when exiting AudioService, MAC addresses are anonymized if the client app does not have BLUETOOTH_CONNECT permission or is not a system component APIs based on AudioDeviceInfo do not need to be modified as the AudioDeviceInfo MAC address is for the AudioPort cached in the app process and AudioPorts are anonymized by the native audioserver before being returned to client apps. Bug: 285588444 Test: atest AudioManagerTest Test: atest RoutingTest Test: atest AudioCommunicationDeviceTest (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:4c1d23ed19823b2706866201eded0c556f83e018) Merged-In: I67bbba2ba941c97138a068d640079b17650e3d86 Change-Id: I67bbba2ba941c97138a068d640079b17650e3d86
1 parent e9ad19e commit a035450

5 files changed

Lines changed: 294 additions & 60 deletions

File tree

media/java/android/media/AudioDeviceAttributes.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public final class AudioDeviceAttributes implements Parcelable {
6868
/**
6969
* The unique address of the device. Some devices don't have addresses, only an empty string.
7070
*/
71-
private final @NonNull String mAddress;
71+
private @NonNull String mAddress;
7272
/**
7373
* The non-unique name of the device. Some devices don't have names, only an empty string.
7474
* Should not be used as a unique identifier for a device.
@@ -186,6 +186,21 @@ public AudioDeviceAttributes(int nativeType, @NonNull String address, @NonNull S
186186
mAudioDescriptors = new ArrayList<>();
187187
}
188188

189+
/**
190+
* @hide
191+
* Copy Constructor.
192+
* @param ada the copied AudioDeviceAttributes
193+
*/
194+
public AudioDeviceAttributes(AudioDeviceAttributes ada) {
195+
mRole = ada.getRole();
196+
mType = ada.getType();
197+
mAddress = ada.getAddress();
198+
mName = ada.getName();
199+
mNativeType = ada.getInternalType();
200+
mAudioProfiles = ada.getAudioProfiles();
201+
mAudioDescriptors = ada.getAudioDescriptors();
202+
}
203+
189204
/**
190205
* @hide
191206
* Returns the role of a device
@@ -216,6 +231,15 @@ public AudioDeviceAttributes(int nativeType, @NonNull String address, @NonNull S
216231
return mAddress;
217232
}
218233

234+
/**
235+
* @hide
236+
* Sets the device address. Only used by audio service.
237+
*/
238+
public void setAddress(@NonNull String address) {
239+
Objects.requireNonNull(address);
240+
mAddress = address;
241+
}
242+
219243
/**
220244
* @hide
221245
* Returns the name of the audio device, or an empty string for devices without one

services/core/java/com/android/server/audio/AdiDeviceState.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import android.media.AudioDeviceInfo;
2626
import android.text.TextUtils;
2727
import android.util.Log;
28+
import android.util.Pair;
2829

2930
import java.util.Objects;
3031

@@ -43,6 +44,8 @@
4344
private final int mInternalDeviceType;
4445
@NonNull
4546
private final String mDeviceAddress;
47+
/** Unique device id from internal device type and address. */
48+
private final Pair<Integer, String> mDeviceId;
4649
private boolean mSAEnabled;
4750
private boolean mHasHeadTracker = false;
4851
private boolean mHeadTrackerEnabled;
@@ -68,6 +71,11 @@
6871
}
6972
mDeviceAddress = isBluetoothDevice(mInternalDeviceType) ? Objects.requireNonNull(
7073
address) : "";
74+
mDeviceId = new Pair<>(mInternalDeviceType, mDeviceAddress);
75+
}
76+
77+
public Pair<Integer, String> getDeviceId() {
78+
return mDeviceId;
7179
}
7280

7381

@@ -140,7 +148,8 @@ public int hashCode() {
140148

141149
@Override
142150
public String toString() {
143-
return "type: " + mDeviceType + "internal type: " + mInternalDeviceType
151+
return "type: " + mDeviceType
152+
+ " internal type: 0x" + Integer.toHexString(mInternalDeviceType)
144153
+ " addr: " + mDeviceAddress + " enabled: " + mSAEnabled
145154
+ " HT: " + mHasHeadTracker + " HTenabled: " + mHeadTrackerEnabled;
146155
}

services/core/java/com/android/server/audio/AudioDeviceBroker.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -833,8 +833,8 @@ private void btMediaMetricRecord(@NonNull BluetoothDevice device, String state,
833833
}
834834

835835
/*package*/ void registerStrategyPreferredDevicesDispatcher(
836-
@NonNull IStrategyPreferredDevicesDispatcher dispatcher) {
837-
mDeviceInventory.registerStrategyPreferredDevicesDispatcher(dispatcher);
836+
@NonNull IStrategyPreferredDevicesDispatcher dispatcher, boolean isPrivileged) {
837+
mDeviceInventory.registerStrategyPreferredDevicesDispatcher(dispatcher, isPrivileged);
838838
}
839839

840840
/*package*/ void unregisterStrategyPreferredDevicesDispatcher(
@@ -852,15 +852,20 @@ private void btMediaMetricRecord(@NonNull BluetoothDevice device, String state,
852852
}
853853

854854
/*package*/ void registerCapturePresetDevicesRoleDispatcher(
855-
@NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
856-
mDeviceInventory.registerCapturePresetDevicesRoleDispatcher(dispatcher);
855+
@NonNull ICapturePresetDevicesRoleDispatcher dispatcher, boolean isPrivileged) {
856+
mDeviceInventory.registerCapturePresetDevicesRoleDispatcher(dispatcher, isPrivileged);
857857
}
858858

859859
/*package*/ void unregisterCapturePresetDevicesRoleDispatcher(
860860
@NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
861861
mDeviceInventory.unregisterCapturePresetDevicesRoleDispatcher(dispatcher);
862862
}
863863

864+
/* package */ List<AudioDeviceAttributes> anonymizeAudioDeviceAttributesListUnchecked(
865+
List<AudioDeviceAttributes> devices) {
866+
return mAudioService.anonymizeAudioDeviceAttributesListUnchecked(devices);
867+
}
868+
864869
/*package*/ void registerCommunicationDeviceDispatcher(
865870
@NonNull ICommunicationDeviceDispatcher dispatcher) {
866871
mCommDevDispatchers.register(dispatcher);

services/core/java/com/android/server/audio/AudioDeviceInventory.java

Lines changed: 76 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,17 @@
4040
import android.util.ArrayMap;
4141
import android.util.ArraySet;
4242
import android.util.Log;
43+
import android.util.Pair;
4344
import android.util.Slog;
4445

4546
import com.android.internal.annotations.GuardedBy;
4647
import com.android.internal.annotations.VisibleForTesting;
4748

4849
import java.io.PrintWriter;
4950
import java.util.ArrayList;
51+
import java.util.HashMap;
5052
import java.util.HashSet;
53+
import java.util.Iterator;
5154
import java.util.LinkedHashMap;
5255
import java.util.List;
5356
import java.util.Objects;
@@ -74,31 +77,58 @@ public class AudioDeviceInventory {
7477

7578
private final Object mDeviceInventoryLock = new Object();
7679
@GuardedBy("mDeviceInventoryLock")
77-
private final ArrayList<AdiDeviceState> mDeviceInventory = new ArrayList<>(0);
78-
80+
private final HashMap<Pair<Integer, String>, AdiDeviceState> mDeviceInventory = new HashMap<>();
7981

8082
List<AdiDeviceState> getImmutableDeviceInventory() {
8183
synchronized (mDeviceInventoryLock) {
82-
return List.copyOf(mDeviceInventory);
84+
return new ArrayList<AdiDeviceState>(mDeviceInventory.values());
8385
}
8486
}
8587

8688
void addDeviceStateToInventory(AdiDeviceState deviceState) {
8789
synchronized (mDeviceInventoryLock) {
88-
mDeviceInventory.add(deviceState);
90+
mDeviceInventory.put(deviceState.getDeviceId(), deviceState);
91+
}
92+
}
93+
94+
/**
95+
* Adds a new entry in mDeviceInventory if the AudioDeviceAttributes passed is an sink
96+
* Bluetooth device and no corresponding entry already exists.
97+
* @param ada the device to add if needed
98+
*/
99+
void addAudioDeviceInInventoryIfNeeded(AudioDeviceAttributes ada) {
100+
if (!AudioSystem.isBluetoothOutDevice(ada.getInternalType())) {
101+
return;
102+
}
103+
synchronized (mDeviceInventoryLock) {
104+
if (findDeviceStateForAudioDeviceAttributes(ada, ada.getType()) != null) {
105+
return;
106+
}
107+
AdiDeviceState ads = new AdiDeviceState(
108+
ada.getType(), ada.getInternalType(), ada.getAddress());
109+
mDeviceInventory.put(ads.getDeviceId(), ads);
89110
}
111+
mDeviceBroker.persistAudioDeviceSettings();
90112
}
91113

114+
/**
115+
* Finds the device state that matches the passed {@link AudioDeviceAttributes} and device
116+
* type. Note: currently this method only returns a valid device for A2DP and BLE devices.
117+
*
118+
* @param ada attributes of device to match
119+
* @param canonicalDeviceType external device type to match
120+
* @return the found {@link AdiDeviceState} matching a cached A2DP or BLE device or
121+
* {@code null} otherwise.
122+
*/
92123
AdiDeviceState findDeviceStateForAudioDeviceAttributes(AudioDeviceAttributes ada,
93124
int canonicalDeviceType) {
94125
final boolean isWireless = isBluetoothDevice(ada.getInternalType());
95-
96126
synchronized (mDeviceInventoryLock) {
97-
for (AdiDeviceState deviceSetting : mDeviceInventory) {
98-
if (deviceSetting.getDeviceType() == canonicalDeviceType
127+
for (AdiDeviceState deviceState : mDeviceInventory.values()) {
128+
if (deviceState.getDeviceType() == canonicalDeviceType
99129
&& (!isWireless || ada.getAddress().equals(
100-
deviceSetting.getDeviceAddress()))) {
101-
return deviceSetting;
130+
deviceState.getDeviceAddress()))) {
131+
return deviceState;
102132
}
103133
}
104134
}
@@ -308,7 +338,7 @@ public String toString() {
308338
+ " devices:" + devices); });
309339
pw.println("\ndevices:\n");
310340
synchronized (mDeviceInventoryLock) {
311-
for (AdiDeviceState device : mDeviceInventory) {
341+
for (AdiDeviceState device : mDeviceInventory.values()) {
312342
pw.println("\t" + device + "\n");
313343
}
314344
}
@@ -695,8 +725,8 @@ void onSetBtActiveDevice(@NonNull AudioDeviceBroker.BtDeviceInfo btInfo, int str
695725
}
696726

697727
/*package*/ void registerStrategyPreferredDevicesDispatcher(
698-
@NonNull IStrategyPreferredDevicesDispatcher dispatcher) {
699-
mPrefDevDispatchers.register(dispatcher);
728+
@NonNull IStrategyPreferredDevicesDispatcher dispatcher, boolean isPrivileged) {
729+
mPrefDevDispatchers.register(dispatcher, isPrivileged);
700730
}
701731

702732
/*package*/ void unregisterStrategyPreferredDevicesDispatcher(
@@ -730,8 +760,8 @@ void onSetBtActiveDevice(@NonNull AudioDeviceBroker.BtDeviceInfo btInfo, int str
730760
}
731761

732762
/*package*/ void registerCapturePresetDevicesRoleDispatcher(
733-
@NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
734-
mDevRoleCapturePresetDispatchers.register(dispatcher);
763+
@NonNull ICapturePresetDevicesRoleDispatcher dispatcher, boolean isPrivileged) {
764+
mDevRoleCapturePresetDispatchers.register(dispatcher, isPrivileged);
735765
}
736766

737767
/*package*/ void unregisterCapturePresetDevicesRoleDispatcher(
@@ -809,6 +839,9 @@ public boolean isDeviceConnected(@NonNull AudioDeviceAttributes device) {
809839
mConnectedDevices.put(deviceKey, new DeviceInfo(
810840
device, deviceName, address, AudioSystem.AUDIO_FORMAT_DEFAULT));
811841
mDeviceBroker.postAccessoryPlugMediaUnmute(device);
842+
if (AudioSystem.isBluetoothScoDevice(device)) {
843+
addAudioDeviceInInventoryIfNeeded(attributes);
844+
}
812845
mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record();
813846
return true;
814847
} else if (!connect && isConnected) {
@@ -1038,8 +1071,9 @@ private void makeA2dpDeviceAvailable(String address, String name, String eventSo
10381071
mDeviceBroker.setBluetoothA2dpOnInt(true, true /*fromA2dp*/, eventSource);
10391072
// at this point there could be another A2DP device already connected in APM, but it
10401073
// doesn't matter as this new one will overwrite the previous one
1041-
final int res = mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
1042-
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, name),
1074+
AudioDeviceAttributes ada = new AudioDeviceAttributes(
1075+
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, name);
1076+
final int res = mAudioSystem.setDeviceConnectionState(ada,
10431077
AudioSystem.DEVICE_STATE_AVAILABLE, a2dpCodec);
10441078

10451079
// TODO: log in MediaMetrics once distinction between connection failure and
@@ -1061,8 +1095,7 @@ private void makeA2dpDeviceAvailable(String address, String name, String eventSo
10611095
// The convention for head tracking sensors associated with A2DP devices is to
10621096
// use a UUID derived from the MAC address as follows:
10631097
// time_low = 0, time_mid = 0, time_hi = 0, clock_seq = 0, node = MAC Address
1064-
UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(
1065-
new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address));
1098+
UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(ada);
10661099
final DeviceInfo di = new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name,
10671100
address, a2dpCodec, sensorUuid);
10681101
final String diKey = di.getKey();
@@ -1073,8 +1106,10 @@ private void makeA2dpDeviceAvailable(String address, String name, String eventSo
10731106

10741107
mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
10751108
setCurrentAudioRouteNameIfPossible(name, true /*fromA2dp*/);
1109+
addAudioDeviceInInventoryIfNeeded(ada);
10761110
}
10771111

1112+
10781113
@GuardedBy("mDevicesLock")
10791114
private void makeA2dpDeviceUnavailableNow(String address, int a2dpCodec) {
10801115
MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "a2dp." + address)
@@ -1168,9 +1203,9 @@ private void makeHearingAidDeviceAvailable(
11681203
final int hearingAidVolIndex = mDeviceBroker.getVssVolumeForDevice(streamType,
11691204
AudioSystem.DEVICE_OUT_HEARING_AID);
11701205
mDeviceBroker.postSetHearingAidVolumeIndex(hearingAidVolIndex, streamType);
1171-
1172-
mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
1173-
AudioSystem.DEVICE_OUT_HEARING_AID, address, name),
1206+
AudioDeviceAttributes ada = new AudioDeviceAttributes(
1207+
AudioSystem.DEVICE_OUT_HEARING_AID, address, name);
1208+
mAudioSystem.setDeviceConnectionState(ada,
11741209
AudioSystem.DEVICE_STATE_AVAILABLE,
11751210
AudioSystem.AUDIO_FORMAT_DEFAULT);
11761211
mConnectedDevices.put(
@@ -1181,6 +1216,7 @@ private void makeHearingAidDeviceAvailable(
11811216
mDeviceBroker.postApplyVolumeOnDevice(streamType,
11821217
AudioSystem.DEVICE_OUT_HEARING_AID, "makeHearingAidDeviceAvailable");
11831218
setCurrentAudioRouteNameIfPossible(name, false /*fromA2dp*/);
1219+
addAudioDeviceInInventoryIfNeeded(ada);
11841220
new MediaMetrics.Item(mMetricsId + "makeHearingAidDeviceAvailable")
11851221
.set(MediaMetrics.Property.ADDRESS, address != null ? address : "")
11861222
.set(MediaMetrics.Property.DEVICE,
@@ -1217,13 +1253,15 @@ private void makeLeAudioDeviceAvailable(String address, String name, int streamT
12171253
*/
12181254
mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource);
12191255

1220-
AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(device, address, name),
1256+
AudioDeviceAttributes ada = new AudioDeviceAttributes(device, address, name);
1257+
AudioSystem.setDeviceConnectionState(ada,
12211258
AudioSystem.DEVICE_STATE_AVAILABLE,
12221259
AudioSystem.AUDIO_FORMAT_DEFAULT);
12231260
mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address),
12241261
new DeviceInfo(device, name, address, AudioSystem.AUDIO_FORMAT_DEFAULT));
12251262
mDeviceBroker.postAccessoryPlugMediaUnmute(device);
12261263
setCurrentAudioRouteNameIfPossible(name, /*fromA2dp=*/false);
1264+
addAudioDeviceInInventoryIfNeeded(ada);
12271265
}
12281266

12291267
if (streamType == AudioSystem.STREAM_DEFAULT) {
@@ -1524,6 +1562,9 @@ private void dispatchPreferredDevice(int strategy,
15241562
final int nbDispatchers = mPrefDevDispatchers.beginBroadcast();
15251563
for (int i = 0; i < nbDispatchers; i++) {
15261564
try {
1565+
if (!((Boolean) mPrefDevDispatchers.getBroadcastCookie(i))) {
1566+
devices = mDeviceBroker.anonymizeAudioDeviceAttributesListUnchecked(devices);
1567+
}
15271568
mPrefDevDispatchers.getBroadcastItem(i).dispatchPrefDevicesChanged(
15281569
strategy, devices);
15291570
} catch (RemoteException e) {
@@ -1537,6 +1578,9 @@ private void dispatchDevicesRoleForCapturePreset(
15371578
final int nbDispatchers = mDevRoleCapturePresetDispatchers.beginBroadcast();
15381579
for (int i = 0; i < nbDispatchers; ++i) {
15391580
try {
1581+
if (!((Boolean) mDevRoleCapturePresetDispatchers.getBroadcastCookie(i))) {
1582+
devices = mDeviceBroker.anonymizeAudioDeviceAttributesListUnchecked(devices);
1583+
}
15401584
mDevRoleCapturePresetDispatchers.getBroadcastItem(i).dispatchDevicesRoleChanged(
15411585
capturePreset, role, devices);
15421586
} catch (RemoteException e) {
@@ -1561,19 +1605,20 @@ UUID getDeviceSensorUuid(AudioDeviceAttributes device) {
15611605
int deviceCatalogSize = 0;
15621606
synchronized (mDeviceInventoryLock) {
15631607
deviceCatalogSize = mDeviceInventory.size();
1564-
}
1565-
final StringBuilder settingsBuilder = new StringBuilder(
1608+
1609+
final StringBuilder settingsBuilder = new StringBuilder(
15661610
deviceCatalogSize * AdiDeviceState.getPeristedMaxSize());
15671611

1568-
synchronized (mDeviceInventoryLock) {
1569-
for (int i = 0; i < mDeviceInventory.size(); i++) {
1570-
settingsBuilder.append(mDeviceInventory.get(i).toPersistableString());
1571-
if (i != mDeviceInventory.size() - 1) {
1572-
settingsBuilder.append(SETTING_DEVICE_SEPARATOR_CHAR);
1573-
}
1612+
Iterator<AdiDeviceState> iterator = mDeviceInventory.values().iterator();
1613+
if (iterator.hasNext()) {
1614+
settingsBuilder.append(iterator.next().toPersistableString());
1615+
}
1616+
while (iterator.hasNext()) {
1617+
settingsBuilder.append(SETTING_DEVICE_SEPARATOR_CHAR);
1618+
settingsBuilder.append(iterator.next().toPersistableString());
15741619
}
1620+
return settingsBuilder.toString();
15751621
}
1576-
return settingsBuilder.toString();
15771622
}
15781623

15791624
/*package*/ void setDeviceSettings(String settings) {

0 commit comments

Comments
 (0)