Loading android/app/src/com/android/bluetooth/mapclient/MceStateMachine.java +4 −1 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ package com.android.bluetooth.mapclient; import static android.Manifest.permission.BLUETOOTH_CONNECT; import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; import static android.Manifest.permission.RECEIVE_SMS; import android.app.Activity; Loading Loading @@ -239,7 +240,9 @@ class MceStateMachine extends StateMachine { intent.putExtra(BluetoothProfile.EXTRA_STATE, state); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mService.sendBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempAllowlistBroadcastOptions()); mService.sendBroadcastMultiplePermissions(intent, new String[] {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED}, Utils.getTempAllowlistBroadcastOptions()); } public synchronized int getState() { Loading android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientStateMachineTest.java +24 −24 Original line number Diff line number Diff line Loading @@ -177,8 +177,8 @@ public class MapClientStateMachineTest { // to MapClientService to change // state from STATE_CONNECTING to STATE_DISCONNECTED verify(mMockMapClientService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcast( mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcastMultiplePermissions( mIntentArgument.capture(), any(String[].class), any(Bundle.class)); Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, mMceStateMachine.getState()); } Loading @@ -198,8 +198,8 @@ public class MapClientStateMachineTest { // to MapClientService to change // state from STATE_CONNECTING to STATE_CONNECTED verify(mMockMapClientService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcast( mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcastMultiplePermissions( mIntentArgument.capture(), any(String[].class), any(Bundle.class)); Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mMceStateMachine.getState()); } Loading @@ -220,16 +220,16 @@ public class MapClientStateMachineTest { // to MapClientService to change // state from STATE_CONNECTING to STATE_CONNECTED verify(mMockMapClientService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcast( mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcastMultiplePermissions( mIntentArgument.capture(), any(String[].class), any(Bundle.class)); Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mMceStateMachine.getState()); msg = Message.obtain(mHandler, MceStateMachine.MSG_MAS_DISCONNECTED); mMceStateMachine.sendMessage(msg); verify(mMockMapClientService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(4)).sendBroadcast( mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(4)).sendBroadcastMultiplePermissions( mIntentArgument.capture(), any(String[].class), any(Bundle.class)); Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, mMceStateMachine.getState()); Loading @@ -249,8 +249,8 @@ public class MapClientStateMachineTest { // to MapClientService to change // state from STATE_CONNECTING to STATE_CONNECTED verify(mMockMapClientService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcast( mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcastMultiplePermissions( mIntentArgument.capture(), any(String[].class), any(Bundle.class)); Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mMceStateMachine.getState()); Loading @@ -273,8 +273,8 @@ public class MapClientStateMachineTest { // to MapClientService to change // state from STATE_CONNECTING to STATE_CONNECTED verify(mMockMapClientService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcast( mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcastMultiplePermissions( mIntentArgument.capture(), any(String[].class), any(Bundle.class)); Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mMceStateMachine.getState()); Assert.assertTrue( Loading @@ -299,15 +299,15 @@ public class MapClientStateMachineTest { // to MapClientService to change // state from STATE_CONNECTING to STATE_CONNECTED verify(mMockMapClientService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcast( mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcastMultiplePermissions( mIntentArgument.capture(), any(String[].class), any(Bundle.class)); Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mMceStateMachine.getState()); mMceStateMachine.disconnect(); verify(mMockMapClientService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(4)).sendBroadcast( mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(4)).sendBroadcastMultiplePermissions( mIntentArgument.capture(), any(String[].class), any(Bundle.class)); Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, mMceStateMachine.getState()); } Loading @@ -325,21 +325,21 @@ public class MapClientStateMachineTest { // to MapClientService to change // state from STATE_CONNECTING to STATE_CONNECTED verify(mMockMapClientService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcast( mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcastMultiplePermissions( mIntentArgument.capture(), any(String[].class), any(Bundle.class)); Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mMceStateMachine.getState()); mMceStateMachine.disconnect(); verify(mMockMapClientService, after(DISCONNECT_TIMEOUT / 2).times(3)).sendBroadcast( mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), after(DISCONNECT_TIMEOUT / 2).times(3)).sendBroadcastMultiplePermissions( mIntentArgument.capture(), any(String[].class), any(Bundle.class)); Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTING, mMceStateMachine.getState()); verify(mMockMapClientService, timeout(DISCONNECT_TIMEOUT).times(4)).sendBroadcast( mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), timeout(DISCONNECT_TIMEOUT).times(4)).sendBroadcastMultiplePermissions( mIntentArgument.capture(), any(String[].class), any(Bundle.class)); Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, mMceStateMachine.getState()); } Loading Loading @@ -400,8 +400,8 @@ public class MapClientStateMachineTest { private void setupSdpRecordReceipt() { // Perform first part of MAP connection logic. verify(mMockMapClientService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendBroadcast( mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendBroadcastMultiplePermissions( mIntentArgument.capture(), any(String[].class), any(Bundle.class)); Assert.assertEquals(BluetoothProfile.STATE_CONNECTING, mMceStateMachine.getState()); Loading framework/java/android/bluetooth/BluetoothMapClient.java +83 −15 Original line number Diff line number Diff line Loading @@ -18,12 +18,12 @@ package android.bluetooth; import static android.bluetooth.BluetoothUtils.getSyncTimeout; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.app.PendingIntent; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; Loading @@ -34,6 +34,7 @@ import android.net.Uri; import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.CloseGuard; import android.util.Log; import com.android.modules.utils.SynchronousResultReceiver; Loading @@ -49,15 +50,37 @@ import java.util.concurrent.TimeoutException; * @hide */ @SystemApi public final class BluetoothMapClient implements BluetoothProfile { public final class BluetoothMapClient implements BluetoothProfile, AutoCloseable { private static final String TAG = "BluetoothMapClient"; private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE); /** @hide */ private final CloseGuard mCloseGuard; /** * Intent used to broadcast the change in connection state of the MAP Client profile. * * <p>This intent will have 3 extras: * <ul> * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li> * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> * </ul> * * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. * * @hide */ @SystemApi @SuppressLint("ActionValue") @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, }) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED"; Loading Loading @@ -197,6 +220,16 @@ public final class BluetoothMapClient implements BluetoothProfile { mAdapter = adapter; mAttributionSource = adapter.getAttributionSource(); mProfileConnector.connect(context, listener); mCloseGuard = new CloseGuard(); mCloseGuard.open("close"); } /** @hide */ protected void finalize() { if (mCloseGuard != null) { mCloseGuard.warnIfOpen(); } close(); } /** Loading @@ -208,6 +241,9 @@ public final class BluetoothMapClient implements BluetoothProfile { */ public void close() { mProfileConnector.disconnect(); if (mCloseGuard != null) { mCloseGuard.close(); } } private IBluetoothMapClient getService() { Loading Loading @@ -309,10 +345,14 @@ public final class BluetoothMapClient implements BluetoothProfile { * @return list of connected devices * @hide */ @SystemApi @Override @RequiresBluetoothConnectPermission @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List<BluetoothDevice> getConnectedDevices() { @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, }) public @NonNull List<BluetoothDevice> getConnectedDevices() { if (DBG) Log.d(TAG, "getConnectedDevices()"); final IBluetoothMapClient service = getService(); final List<BluetoothDevice> defaultValue = new ArrayList<>(); Loading @@ -327,7 +367,10 @@ public final class BluetoothMapClient implements BluetoothProfile { return Attributable.setAttributionSource( recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), mAttributionSource); } catch (RemoteException | TimeoutException e) { } catch (RemoteException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); throw e.rethrowFromSystemServer(); } catch (TimeoutException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } } Loading @@ -337,13 +380,19 @@ public final class BluetoothMapClient implements BluetoothProfile { /** * Get the list of devices matching specified states. Currently at most one. * * @param states The connection states to match for. * @return list of matching devices * @hide */ @SystemApi @Override @RequiresBluetoothConnectPermission @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, }) public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[] states) { if (DBG) Log.d(TAG, "getDevicesMatchingStates()"); final IBluetoothMapClient service = getService(); final List<BluetoothDevice> defaultValue = new ArrayList<>(); Loading @@ -358,7 +407,10 @@ public final class BluetoothMapClient implements BluetoothProfile { return Attributable.setAttributionSource( recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), mAttributionSource); } catch (RemoteException | TimeoutException e) { } catch (RemoteException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); throw e.rethrowFromSystemServer(); } catch (TimeoutException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } } Loading @@ -368,13 +420,18 @@ public final class BluetoothMapClient implements BluetoothProfile { /** * Get connection state of device * * @param device The remote device whose connection state is to be ascertained. * @return device connection state * @hide */ @SystemApi @Override @RequiresBluetoothConnectPermission @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(BluetoothDevice device) { @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, }) public @BtProfileState int getConnectionState(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "getConnectionState(" + device + ")"); final IBluetoothMapClient service = getService(); final int defaultValue = BluetoothProfile.STATE_DISCONNECTED; Loading @@ -386,7 +443,10 @@ public final class BluetoothMapClient implements BluetoothProfile { final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver<>(); service.getConnectionState(device, mAttributionSource, recv); return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); } catch (RemoteException | TimeoutException e) { } catch (RemoteException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); throw e.rethrowFromSystemServer(); } catch (TimeoutException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } } Loading Loading @@ -426,6 +486,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * @return true if connectionPolicy is set, false on error * @hide */ @SystemApi @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, Loading @@ -446,7 +507,10 @@ public final class BluetoothMapClient implements BluetoothProfile { final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv); return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); } catch (RemoteException | TimeoutException e) { } catch (RemoteException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); throw e.rethrowFromSystemServer(); } catch (TimeoutException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } } Loading Loading @@ -484,6 +548,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * @return connection policy of the device * @hide */ @SystemApi @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, Loading @@ -501,7 +566,10 @@ public final class BluetoothMapClient implements BluetoothProfile { final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); service.getConnectionPolicy(device, mAttributionSource, recv); return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); } catch (RemoteException | TimeoutException e) { } catch (RemoteException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); throw e.rethrowFromSystemServer(); } catch (TimeoutException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } } Loading Loading
android/app/src/com/android/bluetooth/mapclient/MceStateMachine.java +4 −1 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ package com.android.bluetooth.mapclient; import static android.Manifest.permission.BLUETOOTH_CONNECT; import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; import static android.Manifest.permission.RECEIVE_SMS; import android.app.Activity; Loading Loading @@ -239,7 +240,9 @@ class MceStateMachine extends StateMachine { intent.putExtra(BluetoothProfile.EXTRA_STATE, state); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mService.sendBroadcast(intent, BLUETOOTH_CONNECT, Utils.getTempAllowlistBroadcastOptions()); mService.sendBroadcastMultiplePermissions(intent, new String[] {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED}, Utils.getTempAllowlistBroadcastOptions()); } public synchronized int getState() { Loading
android/app/tests/unit/src/com/android/bluetooth/mapclient/MapClientStateMachineTest.java +24 −24 Original line number Diff line number Diff line Loading @@ -177,8 +177,8 @@ public class MapClientStateMachineTest { // to MapClientService to change // state from STATE_CONNECTING to STATE_DISCONNECTED verify(mMockMapClientService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcast( mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcastMultiplePermissions( mIntentArgument.capture(), any(String[].class), any(Bundle.class)); Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, mMceStateMachine.getState()); } Loading @@ -198,8 +198,8 @@ public class MapClientStateMachineTest { // to MapClientService to change // state from STATE_CONNECTING to STATE_CONNECTED verify(mMockMapClientService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcast( mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcastMultiplePermissions( mIntentArgument.capture(), any(String[].class), any(Bundle.class)); Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mMceStateMachine.getState()); } Loading @@ -220,16 +220,16 @@ public class MapClientStateMachineTest { // to MapClientService to change // state from STATE_CONNECTING to STATE_CONNECTED verify(mMockMapClientService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcast( mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcastMultiplePermissions( mIntentArgument.capture(), any(String[].class), any(Bundle.class)); Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mMceStateMachine.getState()); msg = Message.obtain(mHandler, MceStateMachine.MSG_MAS_DISCONNECTED); mMceStateMachine.sendMessage(msg); verify(mMockMapClientService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(4)).sendBroadcast( mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(4)).sendBroadcastMultiplePermissions( mIntentArgument.capture(), any(String[].class), any(Bundle.class)); Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, mMceStateMachine.getState()); Loading @@ -249,8 +249,8 @@ public class MapClientStateMachineTest { // to MapClientService to change // state from STATE_CONNECTING to STATE_CONNECTED verify(mMockMapClientService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcast( mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcastMultiplePermissions( mIntentArgument.capture(), any(String[].class), any(Bundle.class)); Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mMceStateMachine.getState()); Loading @@ -273,8 +273,8 @@ public class MapClientStateMachineTest { // to MapClientService to change // state from STATE_CONNECTING to STATE_CONNECTED verify(mMockMapClientService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcast( mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcastMultiplePermissions( mIntentArgument.capture(), any(String[].class), any(Bundle.class)); Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mMceStateMachine.getState()); Assert.assertTrue( Loading @@ -299,15 +299,15 @@ public class MapClientStateMachineTest { // to MapClientService to change // state from STATE_CONNECTING to STATE_CONNECTED verify(mMockMapClientService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcast( mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcastMultiplePermissions( mIntentArgument.capture(), any(String[].class), any(Bundle.class)); Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mMceStateMachine.getState()); mMceStateMachine.disconnect(); verify(mMockMapClientService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(4)).sendBroadcast( mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(4)).sendBroadcastMultiplePermissions( mIntentArgument.capture(), any(String[].class), any(Bundle.class)); Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, mMceStateMachine.getState()); } Loading @@ -325,21 +325,21 @@ public class MapClientStateMachineTest { // to MapClientService to change // state from STATE_CONNECTING to STATE_CONNECTED verify(mMockMapClientService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcast( mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcastMultiplePermissions( mIntentArgument.capture(), any(String[].class), any(Bundle.class)); Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mMceStateMachine.getState()); mMceStateMachine.disconnect(); verify(mMockMapClientService, after(DISCONNECT_TIMEOUT / 2).times(3)).sendBroadcast( mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), after(DISCONNECT_TIMEOUT / 2).times(3)).sendBroadcastMultiplePermissions( mIntentArgument.capture(), any(String[].class), any(Bundle.class)); Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTING, mMceStateMachine.getState()); verify(mMockMapClientService, timeout(DISCONNECT_TIMEOUT).times(4)).sendBroadcast( mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), timeout(DISCONNECT_TIMEOUT).times(4)).sendBroadcastMultiplePermissions( mIntentArgument.capture(), any(String[].class), any(Bundle.class)); Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, mMceStateMachine.getState()); } Loading Loading @@ -400,8 +400,8 @@ public class MapClientStateMachineTest { private void setupSdpRecordReceipt() { // Perform first part of MAP connection logic. verify(mMockMapClientService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendBroadcast( mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendBroadcastMultiplePermissions( mIntentArgument.capture(), any(String[].class), any(Bundle.class)); Assert.assertEquals(BluetoothProfile.STATE_CONNECTING, mMceStateMachine.getState()); Loading
framework/java/android/bluetooth/BluetoothMapClient.java +83 −15 Original line number Diff line number Diff line Loading @@ -18,12 +18,12 @@ package android.bluetooth; import static android.bluetooth.BluetoothUtils.getSyncTimeout; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.app.PendingIntent; import android.bluetooth.annotations.RequiresBluetoothConnectPermission; Loading @@ -34,6 +34,7 @@ import android.net.Uri; import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.CloseGuard; import android.util.Log; import com.android.modules.utils.SynchronousResultReceiver; Loading @@ -49,15 +50,37 @@ import java.util.concurrent.TimeoutException; * @hide */ @SystemApi public final class BluetoothMapClient implements BluetoothProfile { public final class BluetoothMapClient implements BluetoothProfile, AutoCloseable { private static final String TAG = "BluetoothMapClient"; private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE); /** @hide */ private final CloseGuard mCloseGuard; /** * Intent used to broadcast the change in connection state of the MAP Client profile. * * <p>This intent will have 3 extras: * <ul> * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li> * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> * </ul> * * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. * * @hide */ @SystemApi @SuppressLint("ActionValue") @RequiresBluetoothConnectPermission @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, }) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED"; Loading Loading @@ -197,6 +220,16 @@ public final class BluetoothMapClient implements BluetoothProfile { mAdapter = adapter; mAttributionSource = adapter.getAttributionSource(); mProfileConnector.connect(context, listener); mCloseGuard = new CloseGuard(); mCloseGuard.open("close"); } /** @hide */ protected void finalize() { if (mCloseGuard != null) { mCloseGuard.warnIfOpen(); } close(); } /** Loading @@ -208,6 +241,9 @@ public final class BluetoothMapClient implements BluetoothProfile { */ public void close() { mProfileConnector.disconnect(); if (mCloseGuard != null) { mCloseGuard.close(); } } private IBluetoothMapClient getService() { Loading Loading @@ -309,10 +345,14 @@ public final class BluetoothMapClient implements BluetoothProfile { * @return list of connected devices * @hide */ @SystemApi @Override @RequiresBluetoothConnectPermission @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List<BluetoothDevice> getConnectedDevices() { @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, }) public @NonNull List<BluetoothDevice> getConnectedDevices() { if (DBG) Log.d(TAG, "getConnectedDevices()"); final IBluetoothMapClient service = getService(); final List<BluetoothDevice> defaultValue = new ArrayList<>(); Loading @@ -327,7 +367,10 @@ public final class BluetoothMapClient implements BluetoothProfile { return Attributable.setAttributionSource( recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), mAttributionSource); } catch (RemoteException | TimeoutException e) { } catch (RemoteException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); throw e.rethrowFromSystemServer(); } catch (TimeoutException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } } Loading @@ -337,13 +380,19 @@ public final class BluetoothMapClient implements BluetoothProfile { /** * Get the list of devices matching specified states. Currently at most one. * * @param states The connection states to match for. * @return list of matching devices * @hide */ @SystemApi @Override @RequiresBluetoothConnectPermission @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, }) public @NonNull List<BluetoothDevice> getDevicesMatchingConnectionStates(@NonNull int[] states) { if (DBG) Log.d(TAG, "getDevicesMatchingStates()"); final IBluetoothMapClient service = getService(); final List<BluetoothDevice> defaultValue = new ArrayList<>(); Loading @@ -358,7 +407,10 @@ public final class BluetoothMapClient implements BluetoothProfile { return Attributable.setAttributionSource( recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue), mAttributionSource); } catch (RemoteException | TimeoutException e) { } catch (RemoteException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); throw e.rethrowFromSystemServer(); } catch (TimeoutException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } } Loading @@ -368,13 +420,18 @@ public final class BluetoothMapClient implements BluetoothProfile { /** * Get connection state of device * * @param device The remote device whose connection state is to be ascertained. * @return device connection state * @hide */ @SystemApi @Override @RequiresBluetoothConnectPermission @RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT) public int getConnectionState(BluetoothDevice device) { @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, }) public @BtProfileState int getConnectionState(@NonNull BluetoothDevice device) { if (DBG) Log.d(TAG, "getConnectionState(" + device + ")"); final IBluetoothMapClient service = getService(); final int defaultValue = BluetoothProfile.STATE_DISCONNECTED; Loading @@ -386,7 +443,10 @@ public final class BluetoothMapClient implements BluetoothProfile { final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver<>(); service.getConnectionState(device, mAttributionSource, recv); return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); } catch (RemoteException | TimeoutException e) { } catch (RemoteException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); throw e.rethrowFromSystemServer(); } catch (TimeoutException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } } Loading Loading @@ -426,6 +486,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * @return true if connectionPolicy is set, false on error * @hide */ @SystemApi @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, Loading @@ -446,7 +507,10 @@ public final class BluetoothMapClient implements BluetoothProfile { final SynchronousResultReceiver<Boolean> recv = new SynchronousResultReceiver(); service.setConnectionPolicy(device, connectionPolicy, mAttributionSource, recv); return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); } catch (RemoteException | TimeoutException e) { } catch (RemoteException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); throw e.rethrowFromSystemServer(); } catch (TimeoutException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } } Loading Loading @@ -484,6 +548,7 @@ public final class BluetoothMapClient implements BluetoothProfile { * @return connection policy of the device * @hide */ @SystemApi @RequiresBluetoothConnectPermission @RequiresPermission(allOf = { android.Manifest.permission.BLUETOOTH_CONNECT, Loading @@ -501,7 +566,10 @@ public final class BluetoothMapClient implements BluetoothProfile { final SynchronousResultReceiver<Integer> recv = new SynchronousResultReceiver(); service.getConnectionPolicy(device, mAttributionSource, recv); return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue); } catch (RemoteException | TimeoutException e) { } catch (RemoteException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); throw e.rethrowFromSystemServer(); } catch (TimeoutException e) { Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable())); } } Loading