diff options
Diffstat (limited to 'src')
14 files changed, 174 insertions, 218 deletions
diff --git a/src/com/android/bluetooth/a2dp/A2dpCodecConfig.java b/src/com/android/bluetooth/a2dp/A2dpCodecConfig.java index 4de89dfb..5da084f4 100644 --- a/src/com/android/bluetooth/a2dp/A2dpCodecConfig.java +++ b/src/com/android/bluetooth/a2dp/A2dpCodecConfig.java @@ -67,8 +67,16 @@ class A2dpCodecConfig { // Set the mandatory codec's priority to default, and remove the rest for (int i = 0; i < codecConfigArray.length; i++) { BluetoothCodecConfig codecConfig = codecConfigArray[i]; - if (!codecConfig.isMandatoryCodec()) { + if (codecConfig == null || !codecConfig.isMandatoryCodec()) { codecConfigArray[i] = null; + } else { + /* Rebuild SBC selectable codec with Dual Channel (SBC HD audio) */ + codecConfigArray[i] = new BluetoothCodecConfig( + BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, mA2dpSourceCodecPrioritySbc, + BluetoothCodecConfig.SAMPLE_RATE_NONE, + BluetoothCodecConfig.BITS_PER_SAMPLE_NONE, + BluetoothCodecConfig.CHANNEL_MODE_DUAL_CHANNEL, 0 /* codecSpecific1 */, + 0 /* codecSpecific2 */, 0 /* codecSpecific3 */, 0 /* codecSpecific4 */); } } @@ -83,7 +91,7 @@ class A2dpCodecConfig { // Set the mandatory codec's priority to highest, and remove the rest for (int i = 0; i < codecConfigArray.length; i++) { BluetoothCodecConfig codecConfig = codecConfigArray[i]; - if (codecConfig.isMandatoryCodec()) { + if (codecConfig != null && codecConfig.isMandatoryCodec()) { codecConfig.setCodecPriority(BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST); } else { codecConfigArray[i] = null; diff --git a/src/com/android/bluetooth/a2dp/A2dpService.java b/src/com/android/bluetooth/a2dp/A2dpService.java index 16188899..0fc50088 100644 --- a/src/com/android/bluetooth/a2dp/A2dpService.java +++ b/src/com/android/bluetooth/a2dp/A2dpService.java @@ -511,7 +511,14 @@ public class A2dpService extends ProfileService { } // Make sure the Audio Manager knows the previous Active device is disconnected, // and the new Active device is connected. + // Also, mute and unmute the output during the switch to avoid audio glitches. + boolean wasMuted = false; if (previousActiveDevice != null) { + if (!mAudioManager.isStreamMute(AudioManager.STREAM_MUSIC)) { + mAudioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, + AudioManager.ADJUST_MUTE, 0); + wasMuted = true; + } mAudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( previousActiveDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.A2DP, true, -1); @@ -533,6 +540,10 @@ public class A2dpService extends ProfileService { // change, so the Audio Service can reset accordingly the audio // feeding parameters in the Audio HAL to the Bluetooth stack. mAudioManager.handleBluetoothA2dpDeviceConfigChange(mActiveDevice); + if (wasMuted) { + mAudioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, + AudioManager.ADJUST_UNMUTE, 0); + } } } return true; @@ -922,7 +933,10 @@ public class A2dpService extends ProfileService { BluetoothCodecStatus codecStatus = sm.getCodecStatus(); if (codecStatus != null) { for (BluetoothCodecConfig config : codecStatus.getCodecsSelectableCapabilities()) { - if (!config.isMandatoryCodec()) { + boolean isMandatoryCodecWithDualChannel = (config.isMandatoryCodec() + && (config.getChannelMode() & config.CHANNEL_MODE_DUAL_CHANNEL) + == config.CHANNEL_MODE_DUAL_CHANNEL); + if (config != null && !config.isMandatoryCodec() || isMandatoryCodecWithDualChannel) { supportsOptional = true; break; } @@ -930,11 +944,11 @@ public class A2dpService extends ProfileService { } } if (previousSupport == BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN - || supportsOptional != (previousSupport - == BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED)) { + || previousSupport == BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED) { setSupportsOptionalCodecs(device, supportsOptional); } - if (supportsOptional) { + if (supportsOptional + || previousSupport == BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED) { int enabled = getOptionalCodecsEnabled(device); if (enabled == BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) { enableOptionalCodecs(device); diff --git a/src/com/android/bluetooth/btservice/Config.java b/src/com/android/bluetooth/btservice/Config.java index ae65863f..8a9c0a1f 100644 --- a/src/com/android/bluetooth/btservice/Config.java +++ b/src/com/android/bluetooth/btservice/Config.java @@ -21,6 +21,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; import android.provider.Settings; +import android.util.FeatureFlagUtils; import android.util.Log; import com.android.bluetooth.R; @@ -117,6 +118,13 @@ public class Config { ArrayList<Class> profiles = new ArrayList<>(PROFILE_SERVICES_AND_FLAGS.length); for (ProfileConfig config : PROFILE_SERVICES_AND_FLAGS) { boolean supported = resources.getBoolean(config.mSupported); + + if (!supported && (config.mClass == HearingAidService.class) && FeatureFlagUtils + .isEnabled(ctx, FeatureFlagUtils.HEARING_AID_SETTINGS)) { + Log.v(TAG, "Feature Flag enables support for HearingAidService"); + supported = true; + } + if (supported && !isProfileDisabled(ctx, config.mMask)) { Log.v(TAG, "Adding " + config.mClass.getSimpleName()); profiles.add(config.mClass); diff --git a/src/com/android/bluetooth/hearingaid/HearingAidNativeInterface.java b/src/com/android/bluetooth/hearingaid/HearingAidNativeInterface.java index 26036595..e735a881 100644 --- a/src/com/android/bluetooth/hearingaid/HearingAidNativeInterface.java +++ b/src/com/android/bluetooth/hearingaid/HearingAidNativeInterface.java @@ -105,6 +105,28 @@ public class HearingAidNativeInterface { } /** + * Add a hearing aid device to white list. + * + * @param device the remote device + * @return true on success, otherwise false. + */ + @VisibleForTesting (otherwise = VisibleForTesting.PACKAGE_PRIVATE) + public boolean addToWhiteList(BluetoothDevice device) { + return addToWhiteListNative(getByteAddress(device)); + } + + /** + * Remove a hearing aid device from white list. + * + * @param device the remote device + * @return true on success, otherwise false. + */ + @VisibleForTesting (otherwise = VisibleForTesting.PACKAGE_PRIVATE) + public boolean removeFromWhiteList(BluetoothDevice device) { + return removeFromWhiteListNative(getByteAddress(device)); + } + + /** * Sets the HearingAid volume * @param volume */ @@ -168,5 +190,7 @@ public class HearingAidNativeInterface { private native void cleanupNative(); private native boolean connectHearingAidNative(byte[] address); private native boolean disconnectHearingAidNative(byte[] address); + private native boolean addToWhiteListNative(byte[] address); + private native boolean removeFromWhiteListNative(byte[] address); private native void setVolumeNative(int volume); } diff --git a/src/com/android/bluetooth/hearingaid/HearingAidService.java b/src/com/android/bluetooth/hearingaid/HearingAidService.java index 7f2ed89b..704a3d59 100644 --- a/src/com/android/bluetooth/hearingaid/HearingAidService.java +++ b/src/com/android/bluetooth/hearingaid/HearingAidService.java @@ -56,6 +56,10 @@ public class HearingAidService extends ProfileService { // Upper limit of all HearingAid devices: Bonded or Connected private static final int MAX_HEARING_AID_STATE_MACHINES = 10; private static HearingAidService sHearingAidService; + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + static int sConnectTimeoutForEachSideMs = 8000; + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + static int sCheckWhitelistTimeoutMs = 16000; private AdapterService mAdapterService; private HandlerThread mStateMachinesThread; @@ -243,14 +247,6 @@ public class HearingAidService extends ProfileService { } } - synchronized (mStateMachines) { - HearingAidStateMachine smConnect = getOrCreateStateMachine(device); - if (smConnect == null) { - Log.e(TAG, "Cannot connect to " + device + " : no state machine"); - } - smConnect.sendMessage(HearingAidStateMachine.CONNECT); - } - for (BluetoothDevice storedDevice : mDeviceHiSyncIdMap.keySet()) { if (device.equals(storedDevice)) { continue; @@ -263,14 +259,27 @@ public class HearingAidService extends ProfileService { Log.e(TAG, "Ignored connect request for " + device + " : no state machine"); continue; } - sm.sendMessage(HearingAidStateMachine.CONNECT); - } - if (hiSyncId == BluetoothHearingAid.HI_SYNC_ID_INVALID - && !device.equals(storedDevice)) { - break; + sm.sendMessage(HearingAidStateMachine.CONNECT, + sConnectTimeoutForEachSideMs); + sm.sendMessageDelayed(HearingAidStateMachine.CHECK_WHITELIST_CONNECTION, + sCheckWhitelistTimeoutMs); } + break; } } + + synchronized (mStateMachines) { + HearingAidStateMachine smConnect = getOrCreateStateMachine(device); + if (smConnect == null) { + Log.e(TAG, "Cannot connect to " + device + " : no state machine"); + } else { + smConnect.sendMessage(HearingAidStateMachine.CONNECT, + sConnectTimeoutForEachSideMs * 2); + smConnect.sendMessageDelayed(HearingAidStateMachine.CHECK_WHITELIST_CONNECTION, + sCheckWhitelistTimeoutMs); + } + } + return true; } @@ -399,6 +408,16 @@ public class HearingAidService extends ProfileService { } } + /** + * Get the HiSyncIdMap for testing + * + * @return mDeviceHiSyncIdMap + */ + @VisibleForTesting + Map<BluetoothDevice, Long> getHiSyncIdMap() { + return mDeviceHiSyncIdMap; + } + int getConnectionState(BluetoothDevice device) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); synchronized (mStateMachines) { @@ -602,19 +621,24 @@ public class HearingAidService extends ProfileService { if (DBG) { Log.d(TAG, "Set Hearing Aid audio to disconnected"); } - mAudioManager.setHearingAidDeviceConnectionState(mPreviousAudioDevice, - BluetoothProfile.STATE_DISCONNECTED); + boolean suppressNoisyIntent = + (getConnectionState(mPreviousAudioDevice) == BluetoothProfile.STATE_CONNECTED); + mAudioManager.setBluetoothHearingAidDeviceConnectionState( + mPreviousAudioDevice, BluetoothProfile.STATE_DISCONNECTED, + suppressNoisyIntent, 0); mPreviousAudioDevice = null; } else { if (DBG) { Log.d(TAG, "Set Hearing Aid audio to connected"); } if (mPreviousAudioDevice != null) { - mAudioManager.setHearingAidDeviceConnectionState(mPreviousAudioDevice, - BluetoothProfile.STATE_DISCONNECTED); + mAudioManager.setBluetoothHearingAidDeviceConnectionState( + mPreviousAudioDevice, BluetoothProfile.STATE_DISCONNECTED, + true, 0); } - mAudioManager.setHearingAidDeviceConnectionState(device, - BluetoothProfile.STATE_CONNECTED); + mAudioManager.setBluetoothHearingAidDeviceConnectionState( + device, BluetoothProfile.STATE_CONNECTED, + true, 0); mPreviousAudioDevice = device; } } @@ -652,6 +676,7 @@ public class HearingAidService extends ProfileService { if (bondState != BluetoothDevice.BOND_NONE) { return; } + mDeviceHiSyncIdMap.remove(device); synchronized (mStateMachines) { HearingAidStateMachine sm = mStateMachines.get(device); if (sm == null) { diff --git a/src/com/android/bluetooth/hearingaid/HearingAidStateMachine.java b/src/com/android/bluetooth/hearingaid/HearingAidStateMachine.java index d02e1021..3e0d617a 100644 --- a/src/com/android/bluetooth/hearingaid/HearingAidStateMachine.java +++ b/src/com/android/bluetooth/hearingaid/HearingAidStateMachine.java @@ -69,13 +69,15 @@ final class HearingAidStateMachine extends StateMachine { static final int CONNECT = 1; static final int DISCONNECT = 2; + static final int CHECK_WHITELIST_CONNECTION = 3; @VisibleForTesting static final int STACK_EVENT = 101; private static final int CONNECT_TIMEOUT = 201; - // NOTE: the value is not "final" - it is modified in the unit tests - @VisibleForTesting - static int sConnectTimeoutMs = 30000; // 30s + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + static int sConnectTimeoutMs = 16000; + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + static int sDisconnectTimeoutMs = 16000; private Disconnected mDisconnected; private Connecting mConnecting; @@ -172,6 +174,12 @@ final class HearingAidStateMachine extends StateMachine { case DISCONNECT: Log.w(TAG, "Disconnected: DISCONNECT ignored: " + mDevice); break; + case CHECK_WHITELIST_CONNECTION: + if (mService.getConnectedDevices().isEmpty()) { + log("No device connected, remove this device from white list"); + mNativeInterface.removeFromWhiteList(mDevice); + } + break; case STACK_EVENT: HearingAidStackEvent event = (HearingAidStackEvent) message.obj; if (DBG) { @@ -238,7 +246,9 @@ final class HearingAidStateMachine extends StateMachine { public void enter() { Log.i(TAG, "Enter Connecting(" + mDevice + "): " + messageWhatToString(getCurrentMessage().what)); - sendMessageDelayed(CONNECT_TIMEOUT, sConnectTimeoutMs); + int timeout = getCurrentMessage().arg1 != 0 + ? getCurrentMessage().arg1 : sConnectTimeoutMs; + sendMessageDelayed(CONNECT_TIMEOUT, timeout); mConnectionState = BluetoothProfile.STATE_CONNECTING; broadcastConnectionState(mConnectionState, mLastConnectionState); } @@ -261,14 +271,13 @@ final class HearingAidStateMachine extends StateMachine { deferMessage(message); break; case CONNECT_TIMEOUT: - Log.w(TAG, "Connecting connection timeout: " + mDevice); + Log.w(TAG, "Connecting connection timeout: " + mDevice + ". Try whitelist"); mNativeInterface.disconnectHearingAid(mDevice); - HearingAidStackEvent disconnectEvent = - new HearingAidStackEvent( - HearingAidStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); - disconnectEvent.device = mDevice; - disconnectEvent.valueInt1 = HearingAidStackEvent.CONNECTION_STATE_DISCONNECTED; - sendMessage(STACK_EVENT, disconnectEvent); + mNativeInterface.addToWhiteList(mDevice); + transitionTo(mDisconnected); + break; + case CHECK_WHITELIST_CONNECTION: + deferMessage(message); break; case DISCONNECT: log("Connecting: connection canceled to " + mDevice); @@ -325,7 +334,7 @@ final class HearingAidStateMachine extends StateMachine { public void enter() { Log.i(TAG, "Enter Disconnecting(" + mDevice + "): " + messageWhatToString(getCurrentMessage().what)); - sendMessageDelayed(CONNECT_TIMEOUT, sConnectTimeoutMs); + sendMessageDelayed(CONNECT_TIMEOUT, sDisconnectTimeoutMs); mConnectionState = BluetoothProfile.STATE_DISCONNECTING; broadcastConnectionState(mConnectionState, mLastConnectionState); } diff --git a/src/com/android/bluetooth/hfp/HeadsetPhoneState.java b/src/com/android/bluetooth/hfp/HeadsetPhoneState.java index a0914384..bcf123cd 100644 --- a/src/com/android/bluetooth/hfp/HeadsetPhoneState.java +++ b/src/com/android/bluetooth/hfp/HeadsetPhoneState.java @@ -323,134 +323,18 @@ public class HeadsetPhoneState { @Override public void onSignalStrengthsChanged(SignalStrength signalStrength) { - int prevSignal = mCindSignal; if (mCindService == HeadsetHalConstants.NETWORK_STATE_NOT_AVAILABLE) { mCindSignal = 0; - } else if (signalStrength.isGsm()) { - mCindSignal = signalStrength.getLteLevel(); - if (mCindSignal == SignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN) { - mCindSignal = gsmAsuToSignal(signalStrength); - } else { - // SignalStrength#getLteLevel returns the scale from 0-4 - // Bluetooth signal scales at 0-5 - // Let's match up the larger side - mCindSignal++; - } } else { - mCindSignal = cdmaDbmEcioToSignal(signalStrength); + mCindSignal = signalStrength.getLevel() + 1; } - - // network signal strength is scaled to BT 1-5 levels. + // +CIND "signal" indicator is always between 0 to 5 + mCindSignal = Integer.max(Integer.min(mCindSignal, 5), 0); // This results in a lot of duplicate messages, hence this check if (prevSignal != mCindSignal) { sendDeviceStateChanged(); } } - - /* convert [0,31] ASU signal strength to the [0,5] expected by - * bluetooth devices. Scale is similar to status bar policy - */ - private int gsmAsuToSignal(SignalStrength signalStrength) { - int asu = signalStrength.getGsmSignalStrength(); - if (asu == 99) { - return 0; - } else if (asu >= 16) { - return 5; - } else if (asu >= 8) { - return 4; - } else if (asu >= 4) { - return 3; - } else if (asu >= 2) { - return 2; - } else if (asu >= 1) { - return 1; - } else { - return 0; - } - } - - /** - * Convert the cdma / evdo db levels to appropriate icon level. - * The scale is similar to the one used in status bar policy. - * - * @param signalStrength signal strength level - * @return the icon level for remote device - */ - private int cdmaDbmEcioToSignal(SignalStrength signalStrength) { - int levelDbm = 0; - int levelEcio = 0; - int cdmaIconLevel = 0; - int evdoIconLevel = 0; - int cdmaDbm = signalStrength.getCdmaDbm(); - int cdmaEcio = signalStrength.getCdmaEcio(); - - if (cdmaDbm >= -75) { - levelDbm = 4; - } else if (cdmaDbm >= -85) { - levelDbm = 3; - } else if (cdmaDbm >= -95) { - levelDbm = 2; - } else if (cdmaDbm >= -100) { - levelDbm = 1; - } else { - levelDbm = 0; - } - - // Ec/Io are in dB*10 - if (cdmaEcio >= -90) { - levelEcio = 4; - } else if (cdmaEcio >= -110) { - levelEcio = 3; - } else if (cdmaEcio >= -130) { - levelEcio = 2; - } else if (cdmaEcio >= -150) { - levelEcio = 1; - } else { - levelEcio = 0; - } - - cdmaIconLevel = (levelDbm < levelEcio) ? levelDbm : levelEcio; - - // STOPSHIP: Change back to getRilVoiceRadioTechnology - if (mServiceState != null && ( - mServiceState.getRadioTechnology() == ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0 - || mServiceState.getRadioTechnology() - == ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A)) { - int evdoEcio = signalStrength.getEvdoEcio(); - int evdoSnr = signalStrength.getEvdoSnr(); - int levelEvdoEcio = 0; - int levelEvdoSnr = 0; - - // Ec/Io are in dB*10 - if (evdoEcio >= -650) { - levelEvdoEcio = 4; - } else if (evdoEcio >= -750) { - levelEvdoEcio = 3; - } else if (evdoEcio >= -900) { - levelEvdoEcio = 2; - } else if (evdoEcio >= -1050) { - levelEvdoEcio = 1; - } else { - levelEvdoEcio = 0; - } - - if (evdoSnr > 7) { - levelEvdoSnr = 4; - } else if (evdoSnr > 5) { - levelEvdoSnr = 3; - } else if (evdoSnr > 3) { - levelEvdoSnr = 2; - } else if (evdoSnr > 1) { - levelEvdoSnr = 1; - } else { - levelEvdoSnr = 0; - } - - evdoIconLevel = (levelEvdoEcio < levelEvdoSnr) ? levelEvdoEcio : levelEvdoSnr; - } - // TODO(): There is a bug open regarding what should be sent. - return (cdmaIconLevel > evdoIconLevel) ? cdmaIconLevel : evdoIconLevel; - } } } diff --git a/src/com/android/bluetooth/hfp/HeadsetService.java b/src/com/android/bluetooth/hfp/HeadsetService.java index e7a7d639..6d16a6a2 100644 --- a/src/com/android/bluetooth/hfp/HeadsetService.java +++ b/src/com/android/bluetooth/hfp/HeadsetService.java @@ -1486,13 +1486,13 @@ public class HeadsetService extends ProfileService { } } mStateMachinesThread.getThreadHandler().post(() -> { - boolean shouldCallAudioBeActiveBefore = shouldCallAudioBeActive(); + boolean isCallIdleBefore = mSystemInterface.isCallIdle(); mSystemInterface.getHeadsetPhoneState().setNumActiveCall(numActive); mSystemInterface.getHeadsetPhoneState().setNumHeldCall(numHeld); mSystemInterface.getHeadsetPhoneState().setCallState(callState); // Suspend A2DP when call about is about to become active if (callState != HeadsetHalConstants.CALL_STATE_DISCONNECTED - && shouldCallAudioBeActive() && !shouldCallAudioBeActiveBefore) { + && !mSystemInterface.isCallIdle() && isCallIdleBefore) { mSystemInterface.getAudioManager().setParameters("A2dpSuspended=true"); } }); @@ -1501,7 +1501,7 @@ public class HeadsetService extends ProfileService { new HeadsetCallState(numActive, numHeld, callState, number, type))); mStateMachinesThread.getThreadHandler().post(() -> { if (callState == HeadsetHalConstants.CALL_STATE_IDLE - && !shouldCallAudioBeActive() && !isAudioOn()) { + && mSystemInterface.isCallIdle() && !isAudioOn()) { // Resume A2DP when call ended and SCO is not connected mSystemInterface.getAudioManager().setParameters("A2dpSuspended=false"); } diff --git a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java index b1d2cfc7..d25d9edf 100644 --- a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java +++ b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java @@ -1201,7 +1201,8 @@ public class HeadsetStateMachine extends StateMachine { // Set active device to current active SCO device when the current active device // is different from mCurrentDevice. This is to accommodate active device state // mis-match between native and Java. - if (!mDevice.equals(mHeadsetService.getActiveDevice())) { + if (!mDevice.equals(mHeadsetService.getActiveDevice()) + && !hasDeferredMessages(DISCONNECT_AUDIO)) { mHeadsetService.setActiveDevice(mDevice); } setAudioParameters(); diff --git a/src/com/android/bluetooth/map/BluetoothMapService.java b/src/com/android/bluetooth/map/BluetoothMapService.java index c00b39ba..bfb1d3fc 100644 --- a/src/com/android/bluetooth/map/BluetoothMapService.java +++ b/src/com/android/bluetooth/map/BluetoothMapService.java @@ -348,9 +348,7 @@ public class BluetoothMapService extends ProfileService { updateMasInstancesHandler(); break; case START_LISTENER: - if (mAdapter.isEnabled()) { - startSocketListeners(msg.arg1); - } + startSocketListeners(msg.arg1); break; case MSG_MAS_CONNECT: onConnectHandler(msg.arg1); @@ -606,7 +604,6 @@ public class BluetoothMapService extends ProfileService { IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY); - filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); filter.addAction(BluetoothDevice.ACTION_SDP_RECORD); filter.addAction(ACTION_SHOW_MAPS_SETTINGS); @@ -988,30 +985,11 @@ public class BluetoothMapService extends ProfileService { private class MapBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - if (DEBUG) { - Log.d(TAG, "onReceive"); - } String action = intent.getAction(); if (DEBUG) { Log.d(TAG, "onReceive: " + action); } - if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { - int state = - intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); - if (state == BluetoothAdapter.STATE_TURNING_OFF) { - if (DEBUG) { - Log.d(TAG, "STATE_TURNING_OFF"); - } - sendShutdownMessage(); - } else if (state == BluetoothAdapter.STATE_ON) { - if (DEBUG) { - Log.d(TAG, "STATE_ON"); - } - // start ServerSocket listener threads - sendStartListenerMessage(-1); - } - - } else if (action.equals(USER_CONFIRM_TIMEOUT_ACTION)) { + if (action.equals(USER_CONFIRM_TIMEOUT_ACTION)) { if (DEBUG) { Log.d(TAG, "USER_CONFIRM_TIMEOUT ACTION Received."); } diff --git a/src/com/android/bluetooth/newavrcp/AvrcpTargetService.java b/src/com/android/bluetooth/newavrcp/AvrcpTargetService.java index c7029ce1..6a1d357d 100644 --- a/src/com/android/bluetooth/newavrcp/AvrcpTargetService.java +++ b/src/com/android/bluetooth/newavrcp/AvrcpTargetService.java @@ -65,6 +65,8 @@ public class AvrcpTargetService extends ProfileService { MediaPlayerList.FolderUpdateCallback { @Override public void run(MediaData data) { + if (mNativeInterface == null) return; + boolean metadata = !Objects.equals(mCurrentData.metadata, data.metadata); boolean state = !MediaPlayerWrapper.playstateEquals(mCurrentData.state, data.state); boolean queue = !Objects.equals(mCurrentData.queue, data.queue); @@ -81,6 +83,8 @@ public class AvrcpTargetService extends ProfileService { @Override public void run(boolean availablePlayers, boolean addressedPlayers, boolean uids) { + if (mNativeInterface == null) return; + mNativeInterface.sendFolderUpdate(availablePlayers, addressedPlayers, uids); } } @@ -140,17 +144,17 @@ public class AvrcpTargetService extends ProfileService { Log.i(TAG, "Starting the AVRCP Target Service"); mCurrentData = new MediaData(null, null, null); - mReceiver = new AvrcpBroadcastReceiver(); - IntentFilter filter = new IntentFilter(); - filter.addAction(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED); - registerReceiver(mReceiver, filter); - if (!SystemProperties.getBoolean(AVRCP_ENABLE_PROPERTY, true)) { Log.w(TAG, "Skipping initialization of the new AVRCP Target Service"); sInstance = null; return true; } + mReceiver = new AvrcpBroadcastReceiver(); + IntentFilter filter = new IntentFilter(); + filter.addAction(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED); + registerReceiver(mReceiver, filter); + mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); sDeviceMaxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); diff --git a/src/com/android/bluetooth/newavrcp/AvrcpVolumeManager.java b/src/com/android/bluetooth/newavrcp/AvrcpVolumeManager.java index 4b87fac6..2e2ce420 100644 --- a/src/com/android/bluetooth/newavrcp/AvrcpVolumeManager.java +++ b/src/com/android/bluetooth/newavrcp/AvrcpVolumeManager.java @@ -36,11 +36,12 @@ class AvrcpVolumeManager extends AudioDeviceCallback { 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; + private static final String VOLUME_MAP = "bluetooth_volume_map"; + private static final String VOLUME_BLACKLIST = "absolute_volume_blacklist"; + private static final int AVRCP_MAX_VOL = 127; + private static final int STREAM_MUSIC = AudioManager.STREAM_MUSIC; + private static int sDeviceMaxVolume = 0; + private static int sNewDeviceVolume = 0; Context mContext; AudioManager mAudioManager; @@ -72,10 +73,9 @@ class AvrcpVolumeManager extends AudioDeviceCallback { mAudioManager.avrcpSupportsAbsoluteVolume(device.getAddress(), mDeviceMap.get(device)); // Get the current system volume and try to get the preference volume - int currVolume = mAudioManager.getStreamVolume(STREAM_MUSIC); - int savedVolume = getVolume(device, currVolume); + int savedVolume = getVolume(device, sNewDeviceVolume); - d("switchVolumeDevice: currVolume=" + currVolume + " savedVolume=" + savedVolume); + d("switchVolumeDevice: savedVolume=" + savedVolume); // If absolute volume for the device is supported, set the volume for the device if (mDeviceMap.get(device)) { @@ -91,6 +91,7 @@ class AvrcpVolumeManager extends AudioDeviceCallback { mAudioManager = audioManager; mNativeInterface = nativeInterface; sDeviceMaxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + sNewDeviceVolume = sDeviceMaxVolume / 2; mAudioManager.registerAudioDeviceCallback(this, null); @@ -194,7 +195,7 @@ class AvrcpVolumeManager extends AudioDeviceCallback { mCurrentDevice = device; } - void deviceDisconnected(@NonNull BluetoothDevice device) { + synchronized void deviceDisconnected(@NonNull BluetoothDevice device) { d("deviceDisconnected: device=" + device); mDeviceMap.remove(device); } diff --git a/src/com/android/bluetooth/newavrcp/MediaPlayerList.java b/src/com/android/bluetooth/newavrcp/MediaPlayerList.java index 1a10979c..74ea0393 100644 --- a/src/com/android/bluetooth/newavrcp/MediaPlayerList.java +++ b/src/com/android/bluetooth/newavrcp/MediaPlayerList.java @@ -152,7 +152,7 @@ public class MediaPlayerList { for (BrowsedPlayerWrapper wrapper : players) { // Generate new id and add the browsable player if (!mMediaPlayerIds.containsKey(wrapper.getPackageName())) { - mMediaPlayerIds.put(wrapper.getPackageName(), mMediaPlayerIds.size() + 1); + mMediaPlayerIds.put(wrapper.getPackageName(), getFreeMediaPlayerId()); } d("Adding Browser Wrapper for " + wrapper.getPackageName() + " with id " @@ -206,6 +206,12 @@ public class MediaPlayerList { return BLUETOOTH_PLAYER_ID; } + int getFreeMediaPlayerId() { + int id = 0; + while (mMediaPlayerIds.containsValue(++id)) {} + return id; + } + MediaPlayerWrapper getActivePlayer() { return mMediaPlayers.get(mActivePlayerId); } @@ -405,7 +411,7 @@ public class MediaPlayerList { // that key. String packageName = controller.getPackageName(); if (!mMediaPlayerIds.containsKey(packageName)) { - mMediaPlayerIds.put(packageName, mMediaPlayerIds.size() + 1); + mMediaPlayerIds.put(packageName, getFreeMediaPlayerId()); } int playerId = mMediaPlayerIds.get(packageName); diff --git a/src/com/android/bluetooth/newavrcp/MediaPlayerWrapper.java b/src/com/android/bluetooth/newavrcp/MediaPlayerWrapper.java index e0b591ad..256e7714 100644 --- a/src/com/android/bluetooth/newavrcp/MediaPlayerWrapper.java +++ b/src/com/android/bluetooth/newavrcp/MediaPlayerWrapper.java @@ -53,7 +53,6 @@ class MediaPlayerWrapper { private final Object mCallbackLock = new Object(); private Callback mRegisteredCallback = null; - protected MediaPlayerWrapper() { mCurrentData = new MediaData(null, null, null); } @@ -122,16 +121,6 @@ class MediaPlayerWrapper { } Metadata getCurrentMetadata() { - // Try to use the now playing list if the information exists. - if (getActiveQueueID() != -1) { - for (Metadata data : getCurrentQueue()) { - if (data.mediaId.equals(Util.NOW_PLAYING_PREFIX + getActiveQueueID())) { - d("getCurrentMetadata: Using playlist data: " + data.toString()); - return data.clone(); - } - } - } - return Util.toMetadata(getMetadata()); } @@ -240,7 +229,7 @@ class MediaPlayerWrapper { getPlaybackState(), Util.toMetadataList(getQueue())); - mControllerCallbacks = new MediaControllerListener(mLooper); + mControllerCallbacks = new MediaControllerListener(mMediaController, mLooper); } /** @@ -260,14 +249,16 @@ class MediaPlayerWrapper { void updateMediaController(MediaController newController) { if (newController == mMediaController) return; + mMediaController = newController; + synchronized (mCallbackLock) { if (mRegisteredCallback == null || mControllerCallbacks == null) { + d("Controller for " + mPackageName + " maybe is not activated."); return; } } mControllerCallbacks.cleanup(); - mMediaController = newController; // Update the current data since it could be different on the new controller for the player mCurrentData = new MediaData( @@ -275,7 +266,7 @@ class MediaPlayerWrapper { getPlaybackState(), Util.toMetadataList(getQueue())); - mControllerCallbacks = new MediaControllerListener(mLooper); + mControllerCallbacks = new MediaControllerListener(mMediaController, mLooper); d("Controller for " + mPackageName + " was updated."); } @@ -295,7 +286,7 @@ class MediaPlayerWrapper { synchronized (mCallbackLock) { if (mRegisteredCallback == null) { Log.e(TAG, mPackageName - + "Trying to send an update with no registered callback"); + + ": Trying to send an update with no registered callback"); return; } @@ -340,20 +331,23 @@ class MediaPlayerWrapper { class MediaControllerListener extends MediaController.Callback { private final Object mTimeoutHandlerLock = new Object(); private Handler mTimeoutHandler; + private MediaController mController; - MediaControllerListener(Looper newLooper) { + MediaControllerListener(MediaController controller, Looper newLooper) { synchronized (mTimeoutHandlerLock) { mTimeoutHandler = new TimeoutHandler(newLooper); + mController = controller; // Register the callbacks to execute on the same thread as the timeout thread. This // prevents a race condition where a timeout happens at the same time as an update. - mMediaController.registerCallback(this, mTimeoutHandler); + mController.registerCallback(this, mTimeoutHandler); } } void cleanup() { synchronized (mTimeoutHandlerLock) { - mMediaController.unregisterCallback(this); + mController.unregisterCallback(this); + mController = null; mTimeoutHandler.removeMessages(TimeoutHandler.MSG_TIMEOUT); mTimeoutHandler = null; } |
