diff options
Diffstat (limited to 'services/core/java/com/android/server/audio/AudioDeviceInventory.java')
-rw-r--r-- | services/core/java/com/android/server/audio/AudioDeviceInventory.java | 154 |
1 files changed, 141 insertions, 13 deletions
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 0c7f11f98809..5332acae13ad 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -15,6 +15,8 @@ */ package com.android.server.audio; +import static android.media.AudioSystem.isBluetoothDevice; + import android.annotation.NonNull; import android.annotation.Nullable; import android.bluetooth.BluetoothAdapter; @@ -59,6 +61,7 @@ import com.google.android.collect.Sets; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; @@ -78,12 +81,82 @@ public class AudioDeviceInventory { private static final String TAG = "AS.AudioDeviceInventory"; + private static final String SETTING_DEVICE_SEPARATOR_CHAR = "|"; + private static final String SETTING_DEVICE_SEPARATOR = "\\|"; + // lock to synchronize all access to mConnectedDevices and mApmConnectedDevices private final Object mDevicesLock = new Object(); //Audio Analytics ids. private static final String mMetricsId = "audio.device."; + private final Object mDeviceInventoryLock = new Object(); + @GuardedBy("mDeviceInventoryLock") + private final HashMap<Pair<Integer, String>, AdiDeviceState> mDeviceInventory = new HashMap<>(); + + List<AdiDeviceState> getImmutableDeviceInventory() { + synchronized (mDeviceInventoryLock) { + return new ArrayList<AdiDeviceState>(mDeviceInventory.values()); + } + } + + void addDeviceStateToInventory(AdiDeviceState deviceState) { + synchronized (mDeviceInventoryLock) { + 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. + */ + @Nullable + AdiDeviceState findDeviceStateForAudioDeviceAttributes(AudioDeviceAttributes ada, + int canonicalDeviceType) { + final boolean isWireless = isBluetoothDevice(ada.getInternalType()); + synchronized (mDeviceInventoryLock) { + for (AdiDeviceState deviceState : mDeviceInventory.values()) { + if (deviceState.getDeviceType() == canonicalDeviceType + && (!isWireless || ada.getAddress().equals( + deviceState.getDeviceAddress()))) { + return deviceState; + } + } + } + return null; + } + + void clearDeviceInventory() { + synchronized (mDeviceInventoryLock) { + mDeviceInventory.clear(); + } + } + // List of connected devices // Key for map created from DeviceInfo.makeDeviceListKey() @GuardedBy("mDevicesLock") @@ -341,6 +414,12 @@ public class AudioDeviceInventory { mAppliedPresetRolesInt.forEach((key, devices) -> { pw.println(" " + prefix + "preset: " + key.first + " role:" + key.second + " devices:" + devices); }); + pw.println("\ndevices:\n"); + synchronized (mDeviceInventoryLock) { + for (AdiDeviceState device : mDeviceInventory.values()) { + pw.println("\t" + device + "\n"); + } + } } //------------------------------------------------------------ @@ -884,8 +963,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( @@ -894,8 +973,8 @@ public class AudioDeviceInventory { } /*package*/ void registerStrategyNonDefaultDevicesDispatcher( - @NonNull IStrategyNonDefaultDevicesDispatcher dispatcher) { - mNonDefDevDispatchers.register(dispatcher); + @NonNull IStrategyNonDefaultDevicesDispatcher dispatcher, boolean isPrivileged) { + mNonDefDevDispatchers.register(dispatcher, isPrivileged); } /*package*/ void unregisterStrategyNonDefaultDevicesDispatcher( @@ -976,8 +1055,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( @@ -1198,7 +1277,7 @@ public class AudioDeviceInventory { AudioDeviceInfo device = Stream.of(connectedDevices) .filter(d -> d.getInternalType() == ada.getInternalType()) - .filter(d -> (!AudioSystem.isBluetoothDevice(d.getInternalType()) + .filter(d -> (!isBluetoothDevice(d.getInternalType()) || (d.getAddress().equals(ada.getAddress())))) .findFirst() .orElse(null); @@ -1304,6 +1383,8 @@ public class AudioDeviceInventory { updateBluetoothPreferredModes_l(connect ? btDevice : null /*connectedDevice*/); if (!connect) { purgeDevicesRoles_l(); + } else { + addAudioDeviceInInventoryIfNeeded(attributes); } } mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record(); @@ -1570,6 +1651,7 @@ public class AudioDeviceInventory { setCurrentAudioRouteNameIfPossible(name, true /*fromA2dp*/); updateBluetoothPreferredModes_l(btInfo.mDevice /*connectedDevice*/); + addAudioDeviceInInventoryIfNeeded(ada); } static final int[] CAPTURE_PRESETS = new int[] {AudioSource.MIC, AudioSource.CAMCORDER, @@ -1619,7 +1701,7 @@ public class AudioDeviceInventory { } for (DeviceInfo di : mConnectedDevices.values()) { - if (!AudioSystem.isBluetoothDevice(di.mDeviceType)) { + if (!isBluetoothDevice(di.mDeviceType)) { continue; } AudioDeviceAttributes ada = @@ -1733,7 +1815,7 @@ public class AudioDeviceInventory { } HashSet<String> processedAddresses = new HashSet<>(0); for (DeviceInfo di : mConnectedDevices.values()) { - if (!AudioSystem.isBluetoothDevice(di.mDeviceType) + if (!isBluetoothDevice(di.mDeviceType) || processedAddresses.contains(di.mDeviceAddress)) { continue; } @@ -1743,7 +1825,7 @@ public class AudioDeviceInventory { + di.mDeviceAddress + ", preferredProfiles: " + preferredProfiles); } for (DeviceInfo di2 : mConnectedDevices.values()) { - if (!AudioSystem.isBluetoothDevice(di2.mDeviceType) + if (!isBluetoothDevice(di2.mDeviceType) || !di.mDeviceAddress.equals(di2.mDeviceAddress)) { continue; } @@ -1869,9 +1951,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( @@ -1881,6 +1963,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, @@ -1983,6 +2066,7 @@ public class AudioDeviceInventory { sensorUuid)); mDeviceBroker.postAccessoryPlugMediaUnmute(device); setCurrentAudioRouteNameIfPossible(name, /*fromA2dp=*/false); + addAudioDeviceInInventoryIfNeeded(ada); } if (streamType == AudioSystem.STREAM_DEFAULT) { @@ -2313,6 +2397,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) { @@ -2326,6 +2413,9 @@ public class AudioDeviceInventory { final int nbDispatchers = mNonDefDevDispatchers.beginBroadcast(); for (int i = 0; i < nbDispatchers; i++) { try { + if (!((Boolean) mNonDefDevDispatchers.getBroadcastCookie(i))) { + devices = mDeviceBroker.anonymizeAudioDeviceAttributesListUnchecked(devices); + } mNonDefDevDispatchers.getBroadcastItem(i).dispatchNonDefDevicesChanged( strategy, devices); } catch (RemoteException e) { @@ -2339,6 +2429,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) { @@ -2359,6 +2452,41 @@ public class AudioDeviceInventory { } } + /*package*/ String getDeviceSettings() { + int deviceCatalogSize = 0; + synchronized (mDeviceInventoryLock) { + deviceCatalogSize = mDeviceInventory.size(); + + final StringBuilder settingsBuilder = new StringBuilder( + deviceCatalogSize * AdiDeviceState.getPeristedMaxSize()); + + 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(); + } + } + + /*package*/ void setDeviceSettings(String settings) { + clearDeviceInventory(); + String[] devSettings = TextUtils.split(Objects.requireNonNull(settings), + SETTING_DEVICE_SEPARATOR); + // small list, not worth overhead of Arrays.stream(devSettings) + for (String setting : devSettings) { + AdiDeviceState devState = AdiDeviceState.fromPersistedString(setting); + // Note if the device is not compatible with spatialization mode or the device + // type is not canonical, it will be ignored in {@link SpatializerHelper}. + if (devState != null) { + addDeviceStateToInventory(devState); + } + } + } + //---------------------------------------------------------- // For tests only |