Loading android/app/src/com/android/bluetooth/a2dp/A2dpService.java +48 −17 Original line number Diff line number Diff line Loading @@ -30,7 +30,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.media.AudioManager; import android.os.HandlerThread; import android.os.ParcelUuid; import android.provider.Settings; import android.support.annotation.GuardedBy; import android.support.annotation.VisibleForTesting; Loading Loading @@ -58,7 +57,10 @@ public class A2dpService extends ProfileService { private static final boolean DBG = true; private static final String TAG = "A2dpService"; private static A2dpService sA2dpService; private BluetoothAdapter mAdapter; private AdapterService mAdapterService; private HandlerThread mStateMachinesThread; private Avrcp mAvrcp; Loading @@ -84,14 +86,6 @@ public class A2dpService extends ProfileService { mA2dpNativeInterface = A2dpNativeInterface.getInstance(); } private static A2dpService sA2dpService; static final ParcelUuid[] A2DP_SOURCE_UUID = { BluetoothUuid.AudioSource }; static final ParcelUuid[] A2DP_SOURCE_SINK_UUIDS = { BluetoothUuid.AudioSource, BluetoothUuid.AudioSink }; @Override protected IProfileServiceBinder initBinder() { return new BluetoothA2dpBinder(this); Loading @@ -103,9 +97,9 @@ public class A2dpService extends ProfileService { Log.d(TAG, "start()"); } AdapterService adapterService = Objects.requireNonNull(AdapterService.getAdapterService(), mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(), "AdapterService cannot be null when A2dpService starts"); mMaxConnectedAudioDevices = adapterService.getMaxConnectedAudioDevices(); mMaxConnectedAudioDevices = mAdapterService.getMaxConnectedAudioDevices(); if (DBG) { Log.d(TAG, "Max connected audio devices set to " + mMaxConnectedAudioDevices); } Loading Loading @@ -215,9 +209,8 @@ public class A2dpService extends ProfileService { if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) { return false; } ParcelUuid[] featureUuids = device.getUuids(); if ((BluetoothUuid.containsAnyUuid(featureUuids, A2DP_SOURCE_UUID)) && !(BluetoothUuid.containsAllUuids(featureUuids, A2DP_SOURCE_SINK_UUIDS))) { if (!BluetoothUuid.isUuidPresent(mAdapterService.getRemoteUuids(device), BluetoothUuid.AudioSink)) { Log.e(TAG, "Cannot connect to " + device + " : Remote does not have A2DP Sink UUID"); return false; } Loading Loading @@ -292,14 +285,52 @@ public class A2dpService extends ProfileService { return (connected < mMaxConnectedAudioDevices); } /** * Check whether can connect to a peer device. * The check considers a number of factors during the evaluation. * * @param device the peer device to connect to * @return true if connection is allowed, otherwise false */ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) public boolean okToConnect(BluetoothDevice device) { // Check if this is an incoming connection in Quiet mode. if (mAdapterService.isQuietModeEnabled()) { Log.e(TAG, "okToConnect: cannot connect to " + device + " : quiet mode enabled"); return false; } // Check if too many devices if (!canConnectToDevice(device)) { Log.e(TAG, "okToConnect: cannot connect to " + device + " : too many connected devices"); return false; } // Check priority and accept or reject the connection int priority = getPriority(device); int bondState = mAdapterService.getBondState(device); // If priority is undefined, it is likely that our SDP has not completed and peer is // initiating the connection. Allow the connection only if the device is bonded or bonding. if ((priority == BluetoothProfile.PRIORITY_UNDEFINED) && (bondState == BluetoothDevice.BOND_NONE)) { Log.e(TAG, "okToConnect: cannot connect to " + device + " : priority=" + priority + " bondState=" + bondState); return false; } if (priority <= BluetoothProfile.PRIORITY_OFF) { Log.e(TAG, "okToConnect: cannot connect to " + device + " : priority=" + priority); return false; } return true; } List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); List<BluetoothDevice> devices = new ArrayList<>(); Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); synchronized (mStateMachines) { for (BluetoothDevice device : bondedDevices) { ParcelUuid[] featureUuids = device.getUuids(); if (!BluetoothUuid.isUuidPresent(featureUuids, BluetoothUuid.AudioSink)) { if (!BluetoothUuid.isUuidPresent(mAdapterService.getRemoteUuids(device), BluetoothUuid.AudioSink)) { continue; } int connectionState = BluetoothProfile.STATE_DISCONNECTED; Loading Loading @@ -659,7 +690,7 @@ public class A2dpService extends ProfileService { if (DBG) { Log.d(TAG, "Creating a new state machine for " + device); } sm = A2dpStateMachine.make(device, this, this, mA2dpNativeInterface, sm = A2dpStateMachine.make(device, this, mA2dpNativeInterface, mStateMachinesThread.getLooper()); mStateMachines.put(device, sm); return sm; Loading android/app/src/com/android/bluetooth/a2dp/A2dpStateMachine.java +17 −44 Original line number Diff line number Diff line Loading @@ -50,14 +50,12 @@ import android.bluetooth.BluetoothCodecConfig; import android.bluetooth.BluetoothCodecStatus; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.Intent; import android.os.Looper; import android.os.Message; import android.support.annotation.VisibleForTesting; import android.util.Log; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.ProfileService; import com.android.internal.util.State; import com.android.internal.util.StateMachine; Loading Loading @@ -88,18 +86,18 @@ final class A2dpStateMachine extends StateMachine { private int mConnectionState = BluetoothProfile.STATE_DISCONNECTED; private int mLastConnectionState = -1; private A2dpService mService; private A2dpService mA2dpService; private A2dpNativeInterface mA2dpNativeInterface; private final BluetoothDevice mDevice; private boolean mIsPlaying = false; private BluetoothCodecStatus mCodecStatus; A2dpStateMachine(BluetoothDevice device, A2dpService svc, Context context, A2dpStateMachine(BluetoothDevice device, A2dpService a2dpService, A2dpNativeInterface a2dpNativeInterface, Looper looper) { super(TAG, looper); mDevice = device; mService = svc; mA2dpService = a2dpService; mA2dpNativeInterface = a2dpNativeInterface; mDisconnected = new Disconnected(); Loading @@ -115,14 +113,12 @@ final class A2dpStateMachine extends StateMachine { setInitialState(mDisconnected); } static A2dpStateMachine make(BluetoothDevice device, A2dpService svc, Context context, A2dpNativeInterface a2dpNativeInterface, Looper looper) { static A2dpStateMachine make(BluetoothDevice device, A2dpService a2dpService, A2dpNativeInterface a2dpNativeInterface, Looper looper) { if (DBG) { Log.d(TAG, "make for device " + device); } A2dpStateMachine a2dpSm = new A2dpStateMachine(device, svc, context, a2dpNativeInterface, A2dpStateMachine a2dpSm = new A2dpStateMachine(device, a2dpService, a2dpNativeInterface, looper); a2dpSm.start(); return a2dpSm; Loading Loading @@ -159,7 +155,7 @@ final class A2dpStateMachine extends StateMachine { if (mIsPlaying) { Log.i(TAG, "Disconnected: stopped playing: " + mDevice); mIsPlaying = false; mService.setAvrcpAudioState(BluetoothA2dp.STATE_NOT_PLAYING); mA2dpService.setAvrcpAudioState(BluetoothA2dp.STATE_NOT_PLAYING); broadcastAudioState(BluetoothA2dp.STATE_NOT_PLAYING, BluetoothA2dp.STATE_PLAYING); } Loading Loading @@ -189,7 +185,7 @@ final class A2dpStateMachine extends StateMachine { Log.e(TAG, "Disconnected: error connecting to " + mDevice); break; } if (okToConnect(mDevice)) { if (mA2dpService.okToConnect(mDevice)) { transitionTo(mConnecting); sendMessageDelayed(CONNECT_TIMEOUT, sConnectTimeoutMs); } else { Loading Loading @@ -233,7 +229,7 @@ final class A2dpStateMachine extends StateMachine { Log.w(TAG, "Ignore A2DP DISCONNECTED event: " + mDevice); break; case A2dpStackEvent.CONNECTION_STATE_CONNECTING: if (okToConnect(mDevice)) { if (mA2dpService.okToConnect(mDevice)) { Log.i(TAG, "Incoming A2DP Connecting request accepted: " + mDevice); transitionTo(mConnecting); } else { Loading @@ -244,7 +240,7 @@ final class A2dpStateMachine extends StateMachine { break; case A2dpStackEvent.CONNECTION_STATE_CONNECTED: Log.w(TAG, "A2DP Connected from Disconnected state: " + mDevice); if (okToConnect(mDevice)) { if (mA2dpService.okToConnect(mDevice)) { Log.i(TAG, "Incoming A2DP Connected request accepted: " + mDevice); transitionTo(mConnected); } else { Loading Loading @@ -438,7 +434,7 @@ final class A2dpStateMachine extends StateMachine { transitionTo(mDisconnected); break; case A2dpStackEvent.CONNECTION_STATE_CONNECTED: if (okToConnect(mDevice)) { if (mA2dpService.okToConnect(mDevice)) { Log.w(TAG, "Disconnecting interrupted: device is connected: " + mDevice); transitionTo(mConnected); } else { Loading @@ -448,7 +444,7 @@ final class A2dpStateMachine extends StateMachine { } break; case A2dpStackEvent.CONNECTION_STATE_CONNECTING: if (okToConnect(mDevice)) { if (mA2dpService.okToConnect(mDevice)) { Log.i(TAG, "Disconnecting interrupted: try to reconnect: " + mDevice); transitionTo(mConnecting); } else { Loading Loading @@ -570,7 +566,7 @@ final class A2dpStateMachine extends StateMachine { if (!mIsPlaying) { Log.i(TAG, "Connected: started playing: " + mDevice); mIsPlaying = true; mService.setAvrcpAudioState(BluetoothA2dp.STATE_PLAYING); mA2dpService.setAvrcpAudioState(BluetoothA2dp.STATE_PLAYING); broadcastAudioState(BluetoothA2dp.STATE_PLAYING, BluetoothA2dp.STATE_NOT_PLAYING); } Loading @@ -582,7 +578,7 @@ final class A2dpStateMachine extends StateMachine { if (mIsPlaying) { Log.i(TAG, "Connected: stopped playing: " + mDevice); mIsPlaying = false; mService.setAvrcpAudioState(BluetoothA2dp.STATE_NOT_PLAYING); mA2dpService.setAvrcpAudioState(BluetoothA2dp.STATE_NOT_PLAYING); broadcastAudioState(BluetoothA2dp.STATE_NOT_PLAYING, BluetoothA2dp.STATE_PLAYING); } Loading Loading @@ -646,30 +642,7 @@ final class A2dpStateMachine extends StateMachine { boolean sameAudioFeedingParameters = newCodecStatus.getCodecConfig().sameAudioFeedingParameters(prevCodecConfig); mService.codecConfigUpdated(mDevice, mCodecStatus, sameAudioFeedingParameters); } boolean okToConnect(BluetoothDevice device) { AdapterService adapterService = AdapterService.getAdapterService(); int priority = mService.getPriority(device); // Check if this is an incoming connection in Quiet mode. if ((adapterService == null) || adapterService.isQuietModeEnabled()) { return false; } // Check if too many devices if (!mService.canConnectToDevice(device)) { Log.e(TAG, "Cannot connect to " + device + " : too many connected devices"); return false; } // Check priority and accept or reject the connection. If priority is undefined // it is likely that our SDP has not completed and peer is initiating the // connection. Allow this connection, provided the device is bonded. if ((BluetoothProfile.PRIORITY_OFF < priority) || ( (BluetoothProfile.PRIORITY_UNDEFINED == priority) && (device.getBondState() != BluetoothDevice.BOND_NONE))) { return true; } return false; mA2dpService.codecConfigUpdated(mDevice, mCodecStatus, sameAudioFeedingParameters); } // This method does not check for error conditon (newState == prevState) Loading @@ -685,7 +658,7 @@ final class A2dpStateMachine extends StateMachine { intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); mA2dpService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); } private void broadcastAudioState(int newState, int prevState) { Loading @@ -700,7 +673,7 @@ final class A2dpStateMachine extends StateMachine { intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mService.sendBroadcast(intent, A2dpService.BLUETOOTH_PERM); mA2dpService.sendBroadcast(intent, A2dpService.BLUETOOTH_PERM); } private static String messageWhatToString(int what) { Loading android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java +22 −0 Original line number Diff line number Diff line Loading @@ -22,11 +22,13 @@ import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothUuid; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Looper; import android.os.ParcelUuid; import android.support.test.InstrumentationRegistry; import android.support.test.filters.MediumTest; import android.support.test.rule.ServiceTestRule; Loading Loading @@ -100,6 +102,8 @@ public class A2dpServiceTest { // Get a device for testing mTestDevice = mAdapter.getRemoteDevice("00:01:02:03:04:05"); mA2dpService.setPriority(mTestDevice, BluetoothProfile.PRIORITY_UNDEFINED); doReturn(new ParcelUuid[]{BluetoothUuid.AudioSink}).when(mAdapterService) .getRemoteUuids(any(BluetoothDevice.class)); } @After Loading Loading @@ -196,6 +200,24 @@ public class A2dpServiceTest { mA2dpService.getPriority(mTestDevice)); } /** * Test that an outgoing connection to device that does not have A2DP Sink UUID is rejected */ @Test public void testOutgoingConnectMissingAudioSinkUuid() { // Update the device priority so okToConnect() returns true mA2dpService.setPriority(mTestDevice, BluetoothProfile.PRIORITY_ON); doReturn(true).when(mA2dpNativeInterface).connectA2dp(any(BluetoothDevice.class)); doReturn(true).when(mA2dpNativeInterface).disconnectA2dp(any(BluetoothDevice.class)); // Return AudioSource UUID instead of AudioSink doReturn(new ParcelUuid[]{BluetoothUuid.AudioSource}).when(mAdapterService) .getRemoteUuids(any(BluetoothDevice.class)); // Send a connect request Assert.assertFalse("Connect expected to fail", mA2dpService.connect(mTestDevice)); } /** * Test that an outgoing connection times out */ Loading android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpStateMachineTest.java +2 −12 Original line number Diff line number Diff line Loading @@ -76,8 +76,7 @@ public class A2dpStateMachineTest { mHandlerThread = new HandlerThread("A2dpStateMachineTestHandlerThread"); mHandlerThread.start(); mA2dpStateMachine = new A2dpStateMachine(mTestDevice, mA2dpService, mTargetContext, mA2dpNativeInterface, mHandlerThread.getLooper()); mA2dpNativeInterface, mHandlerThread.getLooper()); // Override the timeout value to speed up the test A2dpStateMachine.sConnectTimeoutMs = 1000; // 1s mA2dpStateMachine.start(); Loading Loading @@ -108,16 +107,7 @@ public class A2dpStateMachineTest { * @param allow if true, connection is allowed */ private void allowConnection(boolean allow) { if (allow) { // Update the device priority so okToConnect() returns true doReturn(BluetoothProfile.PRIORITY_ON).when(mA2dpService) .getPriority(any(BluetoothDevice.class)); } else { // Update the device priority so okToConnect() returns false doReturn(BluetoothProfile.PRIORITY_OFF).when(mA2dpService) .getPriority(any(BluetoothDevice.class)); } doReturn(true).when(mA2dpService).canConnectToDevice(any(BluetoothDevice.class)); doReturn(allow).when(mA2dpService).okToConnect(any(BluetoothDevice.class)); } /** Loading Loading
android/app/src/com/android/bluetooth/a2dp/A2dpService.java +48 −17 Original line number Diff line number Diff line Loading @@ -30,7 +30,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.media.AudioManager; import android.os.HandlerThread; import android.os.ParcelUuid; import android.provider.Settings; import android.support.annotation.GuardedBy; import android.support.annotation.VisibleForTesting; Loading Loading @@ -58,7 +57,10 @@ public class A2dpService extends ProfileService { private static final boolean DBG = true; private static final String TAG = "A2dpService"; private static A2dpService sA2dpService; private BluetoothAdapter mAdapter; private AdapterService mAdapterService; private HandlerThread mStateMachinesThread; private Avrcp mAvrcp; Loading @@ -84,14 +86,6 @@ public class A2dpService extends ProfileService { mA2dpNativeInterface = A2dpNativeInterface.getInstance(); } private static A2dpService sA2dpService; static final ParcelUuid[] A2DP_SOURCE_UUID = { BluetoothUuid.AudioSource }; static final ParcelUuid[] A2DP_SOURCE_SINK_UUIDS = { BluetoothUuid.AudioSource, BluetoothUuid.AudioSink }; @Override protected IProfileServiceBinder initBinder() { return new BluetoothA2dpBinder(this); Loading @@ -103,9 +97,9 @@ public class A2dpService extends ProfileService { Log.d(TAG, "start()"); } AdapterService adapterService = Objects.requireNonNull(AdapterService.getAdapterService(), mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(), "AdapterService cannot be null when A2dpService starts"); mMaxConnectedAudioDevices = adapterService.getMaxConnectedAudioDevices(); mMaxConnectedAudioDevices = mAdapterService.getMaxConnectedAudioDevices(); if (DBG) { Log.d(TAG, "Max connected audio devices set to " + mMaxConnectedAudioDevices); } Loading Loading @@ -215,9 +209,8 @@ public class A2dpService extends ProfileService { if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) { return false; } ParcelUuid[] featureUuids = device.getUuids(); if ((BluetoothUuid.containsAnyUuid(featureUuids, A2DP_SOURCE_UUID)) && !(BluetoothUuid.containsAllUuids(featureUuids, A2DP_SOURCE_SINK_UUIDS))) { if (!BluetoothUuid.isUuidPresent(mAdapterService.getRemoteUuids(device), BluetoothUuid.AudioSink)) { Log.e(TAG, "Cannot connect to " + device + " : Remote does not have A2DP Sink UUID"); return false; } Loading Loading @@ -292,14 +285,52 @@ public class A2dpService extends ProfileService { return (connected < mMaxConnectedAudioDevices); } /** * Check whether can connect to a peer device. * The check considers a number of factors during the evaluation. * * @param device the peer device to connect to * @return true if connection is allowed, otherwise false */ @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) public boolean okToConnect(BluetoothDevice device) { // Check if this is an incoming connection in Quiet mode. if (mAdapterService.isQuietModeEnabled()) { Log.e(TAG, "okToConnect: cannot connect to " + device + " : quiet mode enabled"); return false; } // Check if too many devices if (!canConnectToDevice(device)) { Log.e(TAG, "okToConnect: cannot connect to " + device + " : too many connected devices"); return false; } // Check priority and accept or reject the connection int priority = getPriority(device); int bondState = mAdapterService.getBondState(device); // If priority is undefined, it is likely that our SDP has not completed and peer is // initiating the connection. Allow the connection only if the device is bonded or bonding. if ((priority == BluetoothProfile.PRIORITY_UNDEFINED) && (bondState == BluetoothDevice.BOND_NONE)) { Log.e(TAG, "okToConnect: cannot connect to " + device + " : priority=" + priority + " bondState=" + bondState); return false; } if (priority <= BluetoothProfile.PRIORITY_OFF) { Log.e(TAG, "okToConnect: cannot connect to " + device + " : priority=" + priority); return false; } return true; } List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); List<BluetoothDevice> devices = new ArrayList<>(); Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); synchronized (mStateMachines) { for (BluetoothDevice device : bondedDevices) { ParcelUuid[] featureUuids = device.getUuids(); if (!BluetoothUuid.isUuidPresent(featureUuids, BluetoothUuid.AudioSink)) { if (!BluetoothUuid.isUuidPresent(mAdapterService.getRemoteUuids(device), BluetoothUuid.AudioSink)) { continue; } int connectionState = BluetoothProfile.STATE_DISCONNECTED; Loading Loading @@ -659,7 +690,7 @@ public class A2dpService extends ProfileService { if (DBG) { Log.d(TAG, "Creating a new state machine for " + device); } sm = A2dpStateMachine.make(device, this, this, mA2dpNativeInterface, sm = A2dpStateMachine.make(device, this, mA2dpNativeInterface, mStateMachinesThread.getLooper()); mStateMachines.put(device, sm); return sm; Loading
android/app/src/com/android/bluetooth/a2dp/A2dpStateMachine.java +17 −44 Original line number Diff line number Diff line Loading @@ -50,14 +50,12 @@ import android.bluetooth.BluetoothCodecConfig; import android.bluetooth.BluetoothCodecStatus; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.Intent; import android.os.Looper; import android.os.Message; import android.support.annotation.VisibleForTesting; import android.util.Log; import com.android.bluetooth.btservice.AdapterService; import com.android.bluetooth.btservice.ProfileService; import com.android.internal.util.State; import com.android.internal.util.StateMachine; Loading Loading @@ -88,18 +86,18 @@ final class A2dpStateMachine extends StateMachine { private int mConnectionState = BluetoothProfile.STATE_DISCONNECTED; private int mLastConnectionState = -1; private A2dpService mService; private A2dpService mA2dpService; private A2dpNativeInterface mA2dpNativeInterface; private final BluetoothDevice mDevice; private boolean mIsPlaying = false; private BluetoothCodecStatus mCodecStatus; A2dpStateMachine(BluetoothDevice device, A2dpService svc, Context context, A2dpStateMachine(BluetoothDevice device, A2dpService a2dpService, A2dpNativeInterface a2dpNativeInterface, Looper looper) { super(TAG, looper); mDevice = device; mService = svc; mA2dpService = a2dpService; mA2dpNativeInterface = a2dpNativeInterface; mDisconnected = new Disconnected(); Loading @@ -115,14 +113,12 @@ final class A2dpStateMachine extends StateMachine { setInitialState(mDisconnected); } static A2dpStateMachine make(BluetoothDevice device, A2dpService svc, Context context, A2dpNativeInterface a2dpNativeInterface, Looper looper) { static A2dpStateMachine make(BluetoothDevice device, A2dpService a2dpService, A2dpNativeInterface a2dpNativeInterface, Looper looper) { if (DBG) { Log.d(TAG, "make for device " + device); } A2dpStateMachine a2dpSm = new A2dpStateMachine(device, svc, context, a2dpNativeInterface, A2dpStateMachine a2dpSm = new A2dpStateMachine(device, a2dpService, a2dpNativeInterface, looper); a2dpSm.start(); return a2dpSm; Loading Loading @@ -159,7 +155,7 @@ final class A2dpStateMachine extends StateMachine { if (mIsPlaying) { Log.i(TAG, "Disconnected: stopped playing: " + mDevice); mIsPlaying = false; mService.setAvrcpAudioState(BluetoothA2dp.STATE_NOT_PLAYING); mA2dpService.setAvrcpAudioState(BluetoothA2dp.STATE_NOT_PLAYING); broadcastAudioState(BluetoothA2dp.STATE_NOT_PLAYING, BluetoothA2dp.STATE_PLAYING); } Loading Loading @@ -189,7 +185,7 @@ final class A2dpStateMachine extends StateMachine { Log.e(TAG, "Disconnected: error connecting to " + mDevice); break; } if (okToConnect(mDevice)) { if (mA2dpService.okToConnect(mDevice)) { transitionTo(mConnecting); sendMessageDelayed(CONNECT_TIMEOUT, sConnectTimeoutMs); } else { Loading Loading @@ -233,7 +229,7 @@ final class A2dpStateMachine extends StateMachine { Log.w(TAG, "Ignore A2DP DISCONNECTED event: " + mDevice); break; case A2dpStackEvent.CONNECTION_STATE_CONNECTING: if (okToConnect(mDevice)) { if (mA2dpService.okToConnect(mDevice)) { Log.i(TAG, "Incoming A2DP Connecting request accepted: " + mDevice); transitionTo(mConnecting); } else { Loading @@ -244,7 +240,7 @@ final class A2dpStateMachine extends StateMachine { break; case A2dpStackEvent.CONNECTION_STATE_CONNECTED: Log.w(TAG, "A2DP Connected from Disconnected state: " + mDevice); if (okToConnect(mDevice)) { if (mA2dpService.okToConnect(mDevice)) { Log.i(TAG, "Incoming A2DP Connected request accepted: " + mDevice); transitionTo(mConnected); } else { Loading Loading @@ -438,7 +434,7 @@ final class A2dpStateMachine extends StateMachine { transitionTo(mDisconnected); break; case A2dpStackEvent.CONNECTION_STATE_CONNECTED: if (okToConnect(mDevice)) { if (mA2dpService.okToConnect(mDevice)) { Log.w(TAG, "Disconnecting interrupted: device is connected: " + mDevice); transitionTo(mConnected); } else { Loading @@ -448,7 +444,7 @@ final class A2dpStateMachine extends StateMachine { } break; case A2dpStackEvent.CONNECTION_STATE_CONNECTING: if (okToConnect(mDevice)) { if (mA2dpService.okToConnect(mDevice)) { Log.i(TAG, "Disconnecting interrupted: try to reconnect: " + mDevice); transitionTo(mConnecting); } else { Loading Loading @@ -570,7 +566,7 @@ final class A2dpStateMachine extends StateMachine { if (!mIsPlaying) { Log.i(TAG, "Connected: started playing: " + mDevice); mIsPlaying = true; mService.setAvrcpAudioState(BluetoothA2dp.STATE_PLAYING); mA2dpService.setAvrcpAudioState(BluetoothA2dp.STATE_PLAYING); broadcastAudioState(BluetoothA2dp.STATE_PLAYING, BluetoothA2dp.STATE_NOT_PLAYING); } Loading @@ -582,7 +578,7 @@ final class A2dpStateMachine extends StateMachine { if (mIsPlaying) { Log.i(TAG, "Connected: stopped playing: " + mDevice); mIsPlaying = false; mService.setAvrcpAudioState(BluetoothA2dp.STATE_NOT_PLAYING); mA2dpService.setAvrcpAudioState(BluetoothA2dp.STATE_NOT_PLAYING); broadcastAudioState(BluetoothA2dp.STATE_NOT_PLAYING, BluetoothA2dp.STATE_PLAYING); } Loading Loading @@ -646,30 +642,7 @@ final class A2dpStateMachine extends StateMachine { boolean sameAudioFeedingParameters = newCodecStatus.getCodecConfig().sameAudioFeedingParameters(prevCodecConfig); mService.codecConfigUpdated(mDevice, mCodecStatus, sameAudioFeedingParameters); } boolean okToConnect(BluetoothDevice device) { AdapterService adapterService = AdapterService.getAdapterService(); int priority = mService.getPriority(device); // Check if this is an incoming connection in Quiet mode. if ((adapterService == null) || adapterService.isQuietModeEnabled()) { return false; } // Check if too many devices if (!mService.canConnectToDevice(device)) { Log.e(TAG, "Cannot connect to " + device + " : too many connected devices"); return false; } // Check priority and accept or reject the connection. If priority is undefined // it is likely that our SDP has not completed and peer is initiating the // connection. Allow this connection, provided the device is bonded. if ((BluetoothProfile.PRIORITY_OFF < priority) || ( (BluetoothProfile.PRIORITY_UNDEFINED == priority) && (device.getBondState() != BluetoothDevice.BOND_NONE))) { return true; } return false; mA2dpService.codecConfigUpdated(mDevice, mCodecStatus, sameAudioFeedingParameters); } // This method does not check for error conditon (newState == prevState) Loading @@ -685,7 +658,7 @@ final class A2dpStateMachine extends StateMachine { intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); mService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); mA2dpService.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); } private void broadcastAudioState(int newState, int prevState) { Loading @@ -700,7 +673,7 @@ final class A2dpStateMachine extends StateMachine { intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mService.sendBroadcast(intent, A2dpService.BLUETOOTH_PERM); mA2dpService.sendBroadcast(intent, A2dpService.BLUETOOTH_PERM); } private static String messageWhatToString(int what) { Loading
android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpServiceTest.java +22 −0 Original line number Diff line number Diff line Loading @@ -22,11 +22,13 @@ import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothUuid; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Looper; import android.os.ParcelUuid; import android.support.test.InstrumentationRegistry; import android.support.test.filters.MediumTest; import android.support.test.rule.ServiceTestRule; Loading Loading @@ -100,6 +102,8 @@ public class A2dpServiceTest { // Get a device for testing mTestDevice = mAdapter.getRemoteDevice("00:01:02:03:04:05"); mA2dpService.setPriority(mTestDevice, BluetoothProfile.PRIORITY_UNDEFINED); doReturn(new ParcelUuid[]{BluetoothUuid.AudioSink}).when(mAdapterService) .getRemoteUuids(any(BluetoothDevice.class)); } @After Loading Loading @@ -196,6 +200,24 @@ public class A2dpServiceTest { mA2dpService.getPriority(mTestDevice)); } /** * Test that an outgoing connection to device that does not have A2DP Sink UUID is rejected */ @Test public void testOutgoingConnectMissingAudioSinkUuid() { // Update the device priority so okToConnect() returns true mA2dpService.setPriority(mTestDevice, BluetoothProfile.PRIORITY_ON); doReturn(true).when(mA2dpNativeInterface).connectA2dp(any(BluetoothDevice.class)); doReturn(true).when(mA2dpNativeInterface).disconnectA2dp(any(BluetoothDevice.class)); // Return AudioSource UUID instead of AudioSink doReturn(new ParcelUuid[]{BluetoothUuid.AudioSource}).when(mAdapterService) .getRemoteUuids(any(BluetoothDevice.class)); // Send a connect request Assert.assertFalse("Connect expected to fail", mA2dpService.connect(mTestDevice)); } /** * Test that an outgoing connection times out */ Loading
android/app/tests/unit/src/com/android/bluetooth/a2dp/A2dpStateMachineTest.java +2 −12 Original line number Diff line number Diff line Loading @@ -76,8 +76,7 @@ public class A2dpStateMachineTest { mHandlerThread = new HandlerThread("A2dpStateMachineTestHandlerThread"); mHandlerThread.start(); mA2dpStateMachine = new A2dpStateMachine(mTestDevice, mA2dpService, mTargetContext, mA2dpNativeInterface, mHandlerThread.getLooper()); mA2dpNativeInterface, mHandlerThread.getLooper()); // Override the timeout value to speed up the test A2dpStateMachine.sConnectTimeoutMs = 1000; // 1s mA2dpStateMachine.start(); Loading Loading @@ -108,16 +107,7 @@ public class A2dpStateMachineTest { * @param allow if true, connection is allowed */ private void allowConnection(boolean allow) { if (allow) { // Update the device priority so okToConnect() returns true doReturn(BluetoothProfile.PRIORITY_ON).when(mA2dpService) .getPriority(any(BluetoothDevice.class)); } else { // Update the device priority so okToConnect() returns false doReturn(BluetoothProfile.PRIORITY_OFF).when(mA2dpService) .getPriority(any(BluetoothDevice.class)); } doReturn(true).when(mA2dpService).canConnectToDevice(any(BluetoothDevice.class)); doReturn(allow).when(mA2dpService).okToConnect(any(BluetoothDevice.class)); } /** Loading