Loading android/app/src/com/android/bluetooth/a2dp/A2dpService.java +14 −0 Original line number Original line Diff line number Diff line Loading @@ -432,6 +432,8 @@ public class A2dpService extends ProfileService { if (device == null) { if (device == null) { // Clear the active device // Clear the active device mActiveDevice = null; mActiveDevice = null; // This needs to happen before we inform the audio manager that the device // disconnected. Please see comment in broadcastActiveDevice() for why. broadcastActiveDevice(null); broadcastActiveDevice(null); if (previousActiveDevice != null) { if (previousActiveDevice != null) { // Make sure the Audio Manager knows the previous Active device is disconnected // Make sure the Audio Manager knows the previous Active device is disconnected Loading Loading @@ -467,6 +469,8 @@ public class A2dpService extends ProfileService { boolean deviceChanged = !Objects.equals(device, mActiveDevice); boolean deviceChanged = !Objects.equals(device, mActiveDevice); mActiveDevice = device; mActiveDevice = device; // This needs to happen before we inform the audio manager that the device // disconnected. Please see comment in broadcastActiveDevice() for why. broadcastActiveDevice(mActiveDevice); broadcastActiveDevice(mActiveDevice); if (deviceChanged) { if (deviceChanged) { // Send an intent with the active device codec config // Send an intent with the active device codec config Loading Loading @@ -786,6 +790,16 @@ public class A2dpService extends ProfileService { Log.d(TAG, "broadcastActiveDevice(" + device + ")"); Log.d(TAG, "broadcastActiveDevice(" + device + ")"); } } // Currently the audio service can only remember the volume for a single device. We send // active device changed intent after informing AVRCP that the device switched so it can // set the stream volume to the new device before A2DP informs the audio service that the // device has changed. This is to avoid the indeterminate volume state that exists when // in the middle of switching devices. if (AvrcpTargetService.get() != null) { AvrcpTargetService.get().volumeDeviceSwitched( device != null ? device.getAddress() : ""); } Intent intent = new Intent(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED); Intent intent = new Intent(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT Loading android/app/src/com/android/bluetooth/newavrcp/AvrcpTargetService.java +27 −2 Original line number Original line Diff line number Diff line Loading @@ -55,6 +55,7 @@ public class AvrcpTargetService extends ProfileService { private AudioManager mAudioManager; private AudioManager mAudioManager; private AvrcpBroadcastReceiver mReceiver; private AvrcpBroadcastReceiver mReceiver; private AvrcpNativeInterface mNativeInterface; private AvrcpNativeInterface mNativeInterface; private AvrcpVolumeManager mVolumeManager; // Only used to see if the metadata has changed from its previous value // Only used to see if the metadata has changed from its previous value private MediaData mCurrentData; private MediaData mCurrentData; Loading Loading @@ -157,6 +158,8 @@ public class AvrcpTargetService extends ProfileService { mNativeInterface = AvrcpNativeInterface.getInterface(); mNativeInterface = AvrcpNativeInterface.getInterface(); mNativeInterface.init(AvrcpTargetService.this); mNativeInterface.init(AvrcpTargetService.this); mVolumeManager = new AvrcpVolumeManager(this, mAudioManager, mNativeInterface); // Only allow the service to be used once it is initialized // Only allow the service to be used once it is initialized sInstance = this; sInstance = this; Loading Loading @@ -186,12 +189,25 @@ public class AvrcpTargetService extends ProfileService { void deviceConnected(String bdaddr, boolean absoluteVolume) { void deviceConnected(String bdaddr, boolean absoluteVolume) { Log.i(TAG, "deviceConnected: bdaddr=" + bdaddr + " absoluteVolume=" + absoluteVolume); Log.i(TAG, "deviceConnected: bdaddr=" + bdaddr + " absoluteVolume=" + absoluteVolume); mAudioManager.avrcpSupportsAbsoluteVolume(bdaddr, absoluteVolume); mVolumeManager.deviceConnected(bdaddr, absoluteVolume); MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.AVRCP); MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.AVRCP); } } void deviceDisconnected(String bdaddr) { void deviceDisconnected(String bdaddr) { // Do nothing Log.i(TAG, "deviceDisconnected: bdaddr=" + bdaddr); mVolumeManager.deviceDisconnected(bdaddr); } /** * Signal to the service that the current audio out device has changed. The current volume * for the old device is saved and the new device has its volume restored. If there is no * saved volume use the current system volume. */ public void volumeDeviceSwitched(String bdaddr) { if (DEBUG) { Log.d(TAG, "volumeDeviceSwitched: bdaddr=" + bdaddr); } mVolumeManager.volumeDeviceSwitched(bdaddr); } } // TODO (apanicke): Add checks to blacklist Absolute Volume devices if they behave poorly. // TODO (apanicke): Add checks to blacklist Absolute Volume devices if they behave poorly. Loading Loading @@ -291,11 +307,20 @@ public class AvrcpTargetService extends ProfileService { * Dump debugging information to the string builder * Dump debugging information to the string builder */ */ public void dump(StringBuilder sb) { public void dump(StringBuilder sb) { sb.append("\nProfile: AvrcpTargetService:\n"); if (sInstance == null) { sb.append("AvrcpTargetService not running"); return; } if (mMediaPlayerList != null) { if (mMediaPlayerList != null) { mMediaPlayerList.dump(sb); mMediaPlayerList.dump(sb); } else { } else { sb.append("\nMedia Player List is empty\n"); sb.append("\nMedia Player List is empty\n"); } } mVolumeManager.dump(sb); sb.append("\n"); } } private static class AvrcpTargetBinder extends IBluetoothAvrcpTarget.Stub private static class AvrcpTargetBinder extends IBluetoothAvrcpTarget.Stub Loading android/app/src/com/android/bluetooth/newavrcp/AvrcpVolumeManager.java 0 → 100644 +191 −0 Original line number Original line Diff line number Diff line /* * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.bluetooth.avrcp; import android.content.Context; import android.content.SharedPreferences; import android.media.AudioManager; import android.util.Log; import java.util.HashMap; import java.util.Map; class AvrcpVolumeManager { public static final String TAG = "NewAvrcpVolumeManager"; public static final boolean DEBUG = true; // All volumes are stored at system volume values, not AVRCP values public static final String VOLUME_MAP = "bluetooth_volume_map"; public static final String VOLUME_BLACKLIST = "absolute_volume_blacklist"; public static final int AVRCP_MAX_VOL = 127; public static int sDeviceMaxVolume = 0; public static final int STREAM_MUSIC = AudioManager.STREAM_MUSIC; Context mContext; AudioManager mAudioManager; AvrcpNativeInterface mNativeInterface; HashMap<String, Boolean> mDeviceMap = new HashMap<String, Boolean>(); HashMap<String, Integer> mVolumeMap = new HashMap<String, Integer>(); String mCurrentDeviceAddr = ""; boolean mAbsoluteVolumeSupported = false; int avrcpToSystemVolume(int avrcpVolume) { return (int) Math.floor((double) avrcpVolume * sDeviceMaxVolume / AVRCP_MAX_VOL); } int systemToAvrcpVolume(int deviceVolume) { int avrcpVolume = (int) Math.floor((double) deviceVolume * AVRCP_MAX_VOL / sDeviceMaxVolume); if (avrcpVolume > 127) avrcpVolume = 127; return avrcpVolume; } SharedPreferences getVolumeMap() { return mContext.getSharedPreferences(VOLUME_MAP, Context.MODE_PRIVATE); } AvrcpVolumeManager(Context context, AudioManager audioManager, AvrcpNativeInterface nativeInterface) { mContext = context; mAudioManager = audioManager; mNativeInterface = nativeInterface; sDeviceMaxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); // Load the volume map into a hash map since shared preferences are slow Map<String, ?> allKeys = getVolumeMap().getAll(); for (Map.Entry<String, ?> entry : allKeys.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); if (value instanceof Integer) { mVolumeMap.put(key, (Integer) value); } } } int getVolume(String bdaddr, int defaultValue) { if (!mVolumeMap.containsKey(bdaddr)) { Log.w(TAG, "getVolume: Couldn't find volume preference for device: " + bdaddr); return defaultValue; } return mVolumeMap.get(bdaddr); } void storeVolume() { SharedPreferences.Editor pref = getVolumeMap().edit(); int storeVolume = mAudioManager.getStreamVolume(STREAM_MUSIC); Log.i(TAG, "storeVolume: Storing stream volume level for device " + mCurrentDeviceAddr + " : " + storeVolume); mVolumeMap.put(mCurrentDeviceAddr, storeVolume); pref.putInt(mCurrentDeviceAddr, storeVolume); pref.apply(); } void deviceConnected(String bdaddr, boolean absoluteVolume) { if (DEBUG) { Log.d(TAG, "deviceConnected: bdaddr=" + bdaddr + " absoluteVolume=" + absoluteVolume); } mDeviceMap.put(bdaddr.toUpperCase(), absoluteVolume); // AVRCP features lookup has completed after the device became active. Switch to the new // device now. if (bdaddr == mCurrentDeviceAddr) { switchVolumeDevice(bdaddr); } } void volumeDeviceSwitched(String bdaddr) { if (DEBUG) { Log.d(TAG, "activeDeviceChanged: mCurrentDeviceAddr=" + mCurrentDeviceAddr + " bdaddr=" + bdaddr); } if (bdaddr == null || bdaddr.equals(mCurrentDeviceAddr)) { return; } // Store the previous volume if a device was active. if (!mCurrentDeviceAddr.isEmpty()) { storeVolume(); } // Set the current volume device to the new device. mCurrentDeviceAddr = bdaddr; // No new active device. if (bdaddr.isEmpty()) { return; } // A2DP can sometimes connect and set a device to active before AVRCP has determined if the // device supports absolute volume. Defer switching the device until AVRCP returns the // info. if (!mDeviceMap.containsKey(bdaddr)) { Log.w(TAG, "Device isn't connected: " + bdaddr); return; } switchVolumeDevice(bdaddr); } void switchVolumeDevice(String bdaddr) { // Inform the audio manager that the device has changed mAudioManager.avrcpSupportsAbsoluteVolume(bdaddr, mDeviceMap.get(bdaddr)); // Get the current system volume and try to get the preference volume int currVolume = mAudioManager.getStreamVolume(STREAM_MUSIC); int savedVolume = getVolume(bdaddr, currVolume); // If the preference volume isn't equal to the current stream volume then that means // we had a stored preference. if (DEBUG) { Log.d(TAG, "activeDeviceChanged: currVolume=" + currVolume + " savedVolume=" + savedVolume); } if (savedVolume != currVolume) { mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, savedVolume, AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_BLUETOOTH_ABS_VOLUME); } // If absolute volume for the device is supported, set the volume for the device if (mDeviceMap.get(bdaddr)) { int avrcpVolume = systemToAvrcpVolume(savedVolume); Log.e(TAG, "activeDeviceChanged: Updating device volume: avrcpVolume=" + avrcpVolume); mNativeInterface.sendVolumeChanged(avrcpVolume); } } void deviceDisconnected(String bdaddr) { Log.e(TAG, "deviceDisconnected: bdaddr=" + bdaddr); mDeviceMap.remove(bdaddr); } public void dump(StringBuilder sb) { sb.append("Bluetooth Device Volume Map:\n"); Map<String, ?> allKeys = getVolumeMap().getAll(); for (Map.Entry<String, ?> entry : allKeys.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); if (value instanceof Integer) { sb.append(" " + key + " - " + (Integer) value + "\n"); mVolumeMap.put(key, (Integer) value); } } } } Loading
android/app/src/com/android/bluetooth/a2dp/A2dpService.java +14 −0 Original line number Original line Diff line number Diff line Loading @@ -432,6 +432,8 @@ public class A2dpService extends ProfileService { if (device == null) { if (device == null) { // Clear the active device // Clear the active device mActiveDevice = null; mActiveDevice = null; // This needs to happen before we inform the audio manager that the device // disconnected. Please see comment in broadcastActiveDevice() for why. broadcastActiveDevice(null); broadcastActiveDevice(null); if (previousActiveDevice != null) { if (previousActiveDevice != null) { // Make sure the Audio Manager knows the previous Active device is disconnected // Make sure the Audio Manager knows the previous Active device is disconnected Loading Loading @@ -467,6 +469,8 @@ public class A2dpService extends ProfileService { boolean deviceChanged = !Objects.equals(device, mActiveDevice); boolean deviceChanged = !Objects.equals(device, mActiveDevice); mActiveDevice = device; mActiveDevice = device; // This needs to happen before we inform the audio manager that the device // disconnected. Please see comment in broadcastActiveDevice() for why. broadcastActiveDevice(mActiveDevice); broadcastActiveDevice(mActiveDevice); if (deviceChanged) { if (deviceChanged) { // Send an intent with the active device codec config // Send an intent with the active device codec config Loading Loading @@ -786,6 +790,16 @@ public class A2dpService extends ProfileService { Log.d(TAG, "broadcastActiveDevice(" + device + ")"); Log.d(TAG, "broadcastActiveDevice(" + device + ")"); } } // Currently the audio service can only remember the volume for a single device. We send // active device changed intent after informing AVRCP that the device switched so it can // set the stream volume to the new device before A2DP informs the audio service that the // device has changed. This is to avoid the indeterminate volume state that exists when // in the middle of switching devices. if (AvrcpTargetService.get() != null) { AvrcpTargetService.get().volumeDeviceSwitched( device != null ? device.getAddress() : ""); } Intent intent = new Intent(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED); Intent intent = new Intent(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT Loading
android/app/src/com/android/bluetooth/newavrcp/AvrcpTargetService.java +27 −2 Original line number Original line Diff line number Diff line Loading @@ -55,6 +55,7 @@ public class AvrcpTargetService extends ProfileService { private AudioManager mAudioManager; private AudioManager mAudioManager; private AvrcpBroadcastReceiver mReceiver; private AvrcpBroadcastReceiver mReceiver; private AvrcpNativeInterface mNativeInterface; private AvrcpNativeInterface mNativeInterface; private AvrcpVolumeManager mVolumeManager; // Only used to see if the metadata has changed from its previous value // Only used to see if the metadata has changed from its previous value private MediaData mCurrentData; private MediaData mCurrentData; Loading Loading @@ -157,6 +158,8 @@ public class AvrcpTargetService extends ProfileService { mNativeInterface = AvrcpNativeInterface.getInterface(); mNativeInterface = AvrcpNativeInterface.getInterface(); mNativeInterface.init(AvrcpTargetService.this); mNativeInterface.init(AvrcpTargetService.this); mVolumeManager = new AvrcpVolumeManager(this, mAudioManager, mNativeInterface); // Only allow the service to be used once it is initialized // Only allow the service to be used once it is initialized sInstance = this; sInstance = this; Loading Loading @@ -186,12 +189,25 @@ public class AvrcpTargetService extends ProfileService { void deviceConnected(String bdaddr, boolean absoluteVolume) { void deviceConnected(String bdaddr, boolean absoluteVolume) { Log.i(TAG, "deviceConnected: bdaddr=" + bdaddr + " absoluteVolume=" + absoluteVolume); Log.i(TAG, "deviceConnected: bdaddr=" + bdaddr + " absoluteVolume=" + absoluteVolume); mAudioManager.avrcpSupportsAbsoluteVolume(bdaddr, absoluteVolume); mVolumeManager.deviceConnected(bdaddr, absoluteVolume); MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.AVRCP); MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.AVRCP); } } void deviceDisconnected(String bdaddr) { void deviceDisconnected(String bdaddr) { // Do nothing Log.i(TAG, "deviceDisconnected: bdaddr=" + bdaddr); mVolumeManager.deviceDisconnected(bdaddr); } /** * Signal to the service that the current audio out device has changed. The current volume * for the old device is saved and the new device has its volume restored. If there is no * saved volume use the current system volume. */ public void volumeDeviceSwitched(String bdaddr) { if (DEBUG) { Log.d(TAG, "volumeDeviceSwitched: bdaddr=" + bdaddr); } mVolumeManager.volumeDeviceSwitched(bdaddr); } } // TODO (apanicke): Add checks to blacklist Absolute Volume devices if they behave poorly. // TODO (apanicke): Add checks to blacklist Absolute Volume devices if they behave poorly. Loading Loading @@ -291,11 +307,20 @@ public class AvrcpTargetService extends ProfileService { * Dump debugging information to the string builder * Dump debugging information to the string builder */ */ public void dump(StringBuilder sb) { public void dump(StringBuilder sb) { sb.append("\nProfile: AvrcpTargetService:\n"); if (sInstance == null) { sb.append("AvrcpTargetService not running"); return; } if (mMediaPlayerList != null) { if (mMediaPlayerList != null) { mMediaPlayerList.dump(sb); mMediaPlayerList.dump(sb); } else { } else { sb.append("\nMedia Player List is empty\n"); sb.append("\nMedia Player List is empty\n"); } } mVolumeManager.dump(sb); sb.append("\n"); } } private static class AvrcpTargetBinder extends IBluetoothAvrcpTarget.Stub private static class AvrcpTargetBinder extends IBluetoothAvrcpTarget.Stub Loading
android/app/src/com/android/bluetooth/newavrcp/AvrcpVolumeManager.java 0 → 100644 +191 −0 Original line number Original line Diff line number Diff line /* * Copyright 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.bluetooth.avrcp; import android.content.Context; import android.content.SharedPreferences; import android.media.AudioManager; import android.util.Log; import java.util.HashMap; import java.util.Map; class AvrcpVolumeManager { public static final String TAG = "NewAvrcpVolumeManager"; public static final boolean DEBUG = true; // All volumes are stored at system volume values, not AVRCP values public static final String VOLUME_MAP = "bluetooth_volume_map"; public static final String VOLUME_BLACKLIST = "absolute_volume_blacklist"; public static final int AVRCP_MAX_VOL = 127; public static int sDeviceMaxVolume = 0; public static final int STREAM_MUSIC = AudioManager.STREAM_MUSIC; Context mContext; AudioManager mAudioManager; AvrcpNativeInterface mNativeInterface; HashMap<String, Boolean> mDeviceMap = new HashMap<String, Boolean>(); HashMap<String, Integer> mVolumeMap = new HashMap<String, Integer>(); String mCurrentDeviceAddr = ""; boolean mAbsoluteVolumeSupported = false; int avrcpToSystemVolume(int avrcpVolume) { return (int) Math.floor((double) avrcpVolume * sDeviceMaxVolume / AVRCP_MAX_VOL); } int systemToAvrcpVolume(int deviceVolume) { int avrcpVolume = (int) Math.floor((double) deviceVolume * AVRCP_MAX_VOL / sDeviceMaxVolume); if (avrcpVolume > 127) avrcpVolume = 127; return avrcpVolume; } SharedPreferences getVolumeMap() { return mContext.getSharedPreferences(VOLUME_MAP, Context.MODE_PRIVATE); } AvrcpVolumeManager(Context context, AudioManager audioManager, AvrcpNativeInterface nativeInterface) { mContext = context; mAudioManager = audioManager; mNativeInterface = nativeInterface; sDeviceMaxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); // Load the volume map into a hash map since shared preferences are slow Map<String, ?> allKeys = getVolumeMap().getAll(); for (Map.Entry<String, ?> entry : allKeys.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); if (value instanceof Integer) { mVolumeMap.put(key, (Integer) value); } } } int getVolume(String bdaddr, int defaultValue) { if (!mVolumeMap.containsKey(bdaddr)) { Log.w(TAG, "getVolume: Couldn't find volume preference for device: " + bdaddr); return defaultValue; } return mVolumeMap.get(bdaddr); } void storeVolume() { SharedPreferences.Editor pref = getVolumeMap().edit(); int storeVolume = mAudioManager.getStreamVolume(STREAM_MUSIC); Log.i(TAG, "storeVolume: Storing stream volume level for device " + mCurrentDeviceAddr + " : " + storeVolume); mVolumeMap.put(mCurrentDeviceAddr, storeVolume); pref.putInt(mCurrentDeviceAddr, storeVolume); pref.apply(); } void deviceConnected(String bdaddr, boolean absoluteVolume) { if (DEBUG) { Log.d(TAG, "deviceConnected: bdaddr=" + bdaddr + " absoluteVolume=" + absoluteVolume); } mDeviceMap.put(bdaddr.toUpperCase(), absoluteVolume); // AVRCP features lookup has completed after the device became active. Switch to the new // device now. if (bdaddr == mCurrentDeviceAddr) { switchVolumeDevice(bdaddr); } } void volumeDeviceSwitched(String bdaddr) { if (DEBUG) { Log.d(TAG, "activeDeviceChanged: mCurrentDeviceAddr=" + mCurrentDeviceAddr + " bdaddr=" + bdaddr); } if (bdaddr == null || bdaddr.equals(mCurrentDeviceAddr)) { return; } // Store the previous volume if a device was active. if (!mCurrentDeviceAddr.isEmpty()) { storeVolume(); } // Set the current volume device to the new device. mCurrentDeviceAddr = bdaddr; // No new active device. if (bdaddr.isEmpty()) { return; } // A2DP can sometimes connect and set a device to active before AVRCP has determined if the // device supports absolute volume. Defer switching the device until AVRCP returns the // info. if (!mDeviceMap.containsKey(bdaddr)) { Log.w(TAG, "Device isn't connected: " + bdaddr); return; } switchVolumeDevice(bdaddr); } void switchVolumeDevice(String bdaddr) { // Inform the audio manager that the device has changed mAudioManager.avrcpSupportsAbsoluteVolume(bdaddr, mDeviceMap.get(bdaddr)); // Get the current system volume and try to get the preference volume int currVolume = mAudioManager.getStreamVolume(STREAM_MUSIC); int savedVolume = getVolume(bdaddr, currVolume); // If the preference volume isn't equal to the current stream volume then that means // we had a stored preference. if (DEBUG) { Log.d(TAG, "activeDeviceChanged: currVolume=" + currVolume + " savedVolume=" + savedVolume); } if (savedVolume != currVolume) { mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, savedVolume, AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_BLUETOOTH_ABS_VOLUME); } // If absolute volume for the device is supported, set the volume for the device if (mDeviceMap.get(bdaddr)) { int avrcpVolume = systemToAvrcpVolume(savedVolume); Log.e(TAG, "activeDeviceChanged: Updating device volume: avrcpVolume=" + avrcpVolume); mNativeInterface.sendVolumeChanged(avrcpVolume); } } void deviceDisconnected(String bdaddr) { Log.e(TAG, "deviceDisconnected: bdaddr=" + bdaddr); mDeviceMap.remove(bdaddr); } public void dump(StringBuilder sb) { sb.append("Bluetooth Device Volume Map:\n"); Map<String, ?> allKeys = getVolumeMap().getAll(); for (Map.Entry<String, ?> entry : allKeys.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); if (value instanceof Integer) { sb.append(" " + key + " - " + (Integer) value + "\n"); mVolumeMap.put(key, (Integer) value); } } } }