Loading framework/java/android/bluetooth/BluetoothProfileConnector.java +41 −41 Original line number Diff line number Diff line Loading @@ -32,13 +32,13 @@ import android.util.Log; import java.util.Objects; /** * Connector for Bluetooth profile proxies to bind manager service and * profile services * Connector for Bluetooth profile proxies to bind manager service and profile services * * @param <T> The Bluetooth profile interface for this connection. * @hide */ @SuppressLint("AndroidFrameworkBluetoothPermission") public abstract class BluetoothProfileConnector<T> { public abstract class BluetoothProfileConnector<T> extends Handler { private final CloseGuard mCloseGuard = new CloseGuard(); private final int mProfileId; private BluetoothProfile.ServiceListener mServiceListener; Loading @@ -48,6 +48,7 @@ public abstract class BluetoothProfileConnector<T> { private final String mServiceName; private final IBluetoothManager mBluetoothManager; private volatile T mService; private boolean mBound = false; private static final int MESSAGE_SERVICE_CONNECTED = 100; private static final int MESSAGE_SERVICE_DISCONNECTED = 101; Loading @@ -69,26 +70,29 @@ public abstract class BluetoothProfileConnector<T> { public void onServiceConnected(ComponentName className, IBinder service) { logDebug("Proxy object connected"); mService = getServiceInterface(service); mHandler.sendMessage(mHandler.obtainMessage( MESSAGE_SERVICE_CONNECTED)); sendEmptyMessage(MESSAGE_SERVICE_CONNECTED); } @Override public void onServiceDisconnected(ComponentName className) { logDebug("Proxy object disconnected"); T service = mService; doUnbind(); mHandler.sendMessage(mHandler.obtainMessage( MESSAGE_SERVICE_DISCONNECTED)); if (service != null) { sendEmptyMessage(MESSAGE_SERVICE_DISCONNECTED); } } }; /** @hide */ public BluetoothProfileConnector( Looper looper, BluetoothProfile profile, int profileId, String profileName, String serviceName, IBluetoothManager bluetoothManager) { super(looper); mProfileId = profileId; mProfileProxy = profile; mProfileName = profileName; Loading @@ -99,6 +103,7 @@ public abstract class BluetoothProfileConnector<T> { BluetoothProfileConnector( BluetoothProfile profile, int profileId, String profileName, String serviceName) { this( Looper.getMainLooper(), profile, profileId, profileName, Loading @@ -113,30 +118,30 @@ public abstract class BluetoothProfileConnector<T> { doUnbind(); } private boolean doBind() { private void doBind() { synchronized (mConnection) { if (mService == null) { if (!mBound) { logDebug("Binding service for " + mPackageName); mCloseGuard.open("doUnbind"); try { return mBluetoothManager.bindBluetoothProfileService( mBluetoothManager.bindBluetoothProfileService( mProfileId, mServiceName, mConnection); mBound = true; } catch (RemoteException re) { logError("Failed to bind service. " + re); return false; } } } return true; } private void doUnbind() { synchronized (mConnection) { if (mService != null) { if (mBound) { logDebug("Unbinding service for " + mPackageName); mCloseGuard.close(); try { mBluetoothManager.unbindBluetoothProfileService(mProfileId, mConnection); mBound = false; } catch (RemoteException re) { logError("Unable to unbind service: " + re); } finally { Loading Loading @@ -206,18 +211,15 @@ public abstract class BluetoothProfileConnector<T> { Log.e(mProfileName, log); } @SuppressLint("AndroidFrameworkBluetoothPermission") private final Handler mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_SERVICE_CONNECTED: { case MESSAGE_SERVICE_CONNECTED: if (mServiceListener != null) { mServiceListener.onServiceConnected(mProfileId, mProfileProxy); } break; } case MESSAGE_SERVICE_DISCONNECTED: { case MESSAGE_SERVICE_DISCONNECTED: if (mServiceListener != null) { mServiceListener.onServiceDisconnected(mProfileId); } Loading @@ -225,5 +227,3 @@ public abstract class BluetoothProfileConnector<T> { } } } }; } framework/tests/unit/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -23,7 +23,9 @@ android_test { static_libs: [ "androidx.test.ext.truth", "androidx.test.rules", "frameworks-base-testutils", "junit", "mockito-target", "modules-utils-bytesmatcher", ], test_suites: [ Loading framework/tests/unit/src/android/bluetooth/BluetoothProfileConnectorTest.java +82 −18 Original line number Diff line number Diff line Loading @@ -18,16 +18,27 @@ package android.bluetooth; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.times; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verifyZeroInteractions; import android.content.ComponentName; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.test.TestLooper; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InOrder; /** Test cases for {@link BluetoothProfileConnector}. */ @SmallTest Loading @@ -36,6 +47,15 @@ public class BluetoothProfileConnectorTest { static class FakeBluetoothManager extends IBluetoothManager.Default { private IBluetoothStateChangeCallback mStateChangeCallback; private IBluetoothProfileServiceConnection mServiceConnection; private final Handler mHandler; private FakeBluetoothManager(Looper looper) { mHandler = new Handler(looper); } Looper getLooper() { return mHandler.getLooper(); } @Override public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) { Loading Loading @@ -64,14 +84,28 @@ public class BluetoothProfileConnectorTest { int profile, IBluetoothProfileServiceConnection proxy) { if (proxy != mServiceConnection) throw new IllegalStateException(); mHandler.post( () -> { try { proxy.onServiceDisconnected(new ComponentName("pkg", "cls")); } catch (RemoteException e) { throw new RuntimeException(e); } }); mServiceConnection = null; } } private BluetoothProfileConnector createBluetoothProfileConnector( IBluetoothManager bluetoothManager) { FakeBluetoothManager bluetoothManager) { return new BluetoothProfileConnector( null, BluetoothProfile.HEADSET, "Headset", "HeadsetService", bluetoothManager) { bluetoothManager.getLooper(), null, BluetoothProfile.HEADSET, "Headset", "HeadsetService", bluetoothManager) { public IBinder getServiceInterface(IBinder service) { return service; } Loading @@ -80,89 +114,119 @@ public class BluetoothProfileConnectorTest { @Test public void bind_registerServiceConnection() throws RemoteException { FakeBluetoothManager bluetoothManager = new FakeBluetoothManager(); TestLooper looper = new TestLooper(); FakeBluetoothManager bluetoothManager = new FakeBluetoothManager(looper.getLooper()); BluetoothProfileConnector connector = createBluetoothProfileConnector(bluetoothManager); BluetoothProfile.ServiceListener listener = null; BluetoothProfile.ServiceListener listener = mock(BluetoothProfile.ServiceListener.class); connector.connect("test.package", listener); bluetoothManager.mStateChangeCallback.onBluetoothStateChange(true); looper.dispatchAll(); assertThat(bluetoothManager.mServiceConnection).isNotNull(); verifyZeroInteractions(listener); } @Test public void unbind_unregisterServiceConnection() throws RemoteException { FakeBluetoothManager bluetoothManager = new FakeBluetoothManager(); TestLooper looper = new TestLooper(); FakeBluetoothManager bluetoothManager = new FakeBluetoothManager(looper.getLooper()); BluetoothProfileConnector connector = createBluetoothProfileConnector(bluetoothManager); ComponentName componentName = new ComponentName("pkg", "cls"); BluetoothProfile.ServiceListener listener = null; BluetoothProfile.ServiceListener listener = mock(BluetoothProfile.ServiceListener.class); connector.connect("test.package", listener); bluetoothManager.mStateChangeCallback.onBluetoothStateChange(true); bluetoothManager.mServiceConnection.onServiceConnected(componentName, new Binder()); bluetoothManager.mServiceConnection.onServiceDisconnected(componentName); bluetoothManager.mStateChangeCallback.onBluetoothStateChange(false); looper.dispatchAll(); assertThat(bluetoothManager.mServiceConnection).isNull(); InOrder order = inOrder(listener); order.verify(listener).onServiceConnected(anyInt(), any()); order.verify(listener).onServiceDisconnected(anyInt()); order.verifyNoMoreInteractions(); } @Test public void upThenDown_unregisterServiceConnection() throws RemoteException { FakeBluetoothManager bluetoothManager = new FakeBluetoothManager(); TestLooper looper = new TestLooper(); FakeBluetoothManager bluetoothManager = new FakeBluetoothManager(looper.getLooper()); BluetoothProfileConnector connector = createBluetoothProfileConnector(bluetoothManager); BluetoothProfile.ServiceListener listener = null; BluetoothProfile.ServiceListener listener = mock(BluetoothProfile.ServiceListener.class); connector.connect("test.package", listener); bluetoothManager.mStateChangeCallback.onBluetoothStateChange(true); bluetoothManager.mStateChangeCallback.onBluetoothStateChange(false); looper.dispatchAll(); // TODO(b/302092694): Should be isNull assertThat(bluetoothManager.mServiceConnection).isNotNull(); assertThat(bluetoothManager.mServiceConnection).isNull(); verifyZeroInteractions(listener); } @Test public void disconnectAfterConnect_unregisterCallbacks() { FakeBluetoothManager bluetoothManager = new FakeBluetoothManager(); TestLooper looper = new TestLooper(); FakeBluetoothManager bluetoothManager = new FakeBluetoothManager(looper.getLooper()); BluetoothProfileConnector connector = createBluetoothProfileConnector(bluetoothManager); BluetoothProfile.ServiceListener listener = null; BluetoothProfile.ServiceListener listener = mock(BluetoothProfile.ServiceListener.class); connector.connect("test.package", listener); connector.disconnect(); looper.dispatchAll(); assertThat(bluetoothManager.mServiceConnection).isNull(); assertThat(bluetoothManager.mStateChangeCallback).isNull(); InOrder order = inOrder(listener); // TODO(b/309635805): This should not be here order.verify(listener).onServiceDisconnected(anyInt()); order.verifyNoMoreInteractions(); } @Test public void disconnectAfterBind_unregisterCallbacks() throws RemoteException { FakeBluetoothManager bluetoothManager = new FakeBluetoothManager(); TestLooper looper = new TestLooper(); FakeBluetoothManager bluetoothManager = new FakeBluetoothManager(looper.getLooper()); BluetoothProfileConnector connector = createBluetoothProfileConnector(bluetoothManager); BluetoothProfile.ServiceListener listener = null; BluetoothProfile.ServiceListener listener = mock(BluetoothProfile.ServiceListener.class); connector.connect("test.package", listener); bluetoothManager.mStateChangeCallback.onBluetoothStateChange(true); connector.disconnect(); looper.dispatchAll(); // TODO(b/302092694): Should be isNull assertThat(bluetoothManager.mServiceConnection).isNotNull(); assertThat(bluetoothManager.mServiceConnection).isNull(); assertThat(bluetoothManager.mStateChangeCallback).isNull(); InOrder order = inOrder(listener); // TODO(b/309635805): This should not be here order.verify(listener).onServiceDisconnected(anyInt()); order.verifyNoMoreInteractions(); } @Test public void disconnectAfterUnbind_unregisterCallbacks() throws RemoteException { FakeBluetoothManager bluetoothManager = new FakeBluetoothManager(); TestLooper looper = new TestLooper(); FakeBluetoothManager bluetoothManager = new FakeBluetoothManager(looper.getLooper()); BluetoothProfileConnector connector = createBluetoothProfileConnector(bluetoothManager); ComponentName componentName = new ComponentName("pkg", "cls"); BluetoothProfile.ServiceListener listener = null; BluetoothProfile.ServiceListener listener = mock(BluetoothProfile.ServiceListener.class); connector.connect("test.package", listener); bluetoothManager.mStateChangeCallback.onBluetoothStateChange(true); bluetoothManager.mServiceConnection.onServiceConnected(componentName, new Binder()); bluetoothManager.mServiceConnection.onServiceDisconnected(componentName); bluetoothManager.mStateChangeCallback.onBluetoothStateChange(false); looper.dispatchAll(); connector.disconnect(); looper.dispatchAll(); assertThat(bluetoothManager.mServiceConnection).isNull(); assertThat(bluetoothManager.mStateChangeCallback).isNull(); InOrder order = inOrder(listener); order.verify(listener).onServiceConnected(anyInt(), any()); // TODO(b/309635805): Should be only one order.verify(listener, times(2)).onServiceDisconnected(anyInt()); order.verifyNoMoreInteractions(); } } Loading
framework/java/android/bluetooth/BluetoothProfileConnector.java +41 −41 Original line number Diff line number Diff line Loading @@ -32,13 +32,13 @@ import android.util.Log; import java.util.Objects; /** * Connector for Bluetooth profile proxies to bind manager service and * profile services * Connector for Bluetooth profile proxies to bind manager service and profile services * * @param <T> The Bluetooth profile interface for this connection. * @hide */ @SuppressLint("AndroidFrameworkBluetoothPermission") public abstract class BluetoothProfileConnector<T> { public abstract class BluetoothProfileConnector<T> extends Handler { private final CloseGuard mCloseGuard = new CloseGuard(); private final int mProfileId; private BluetoothProfile.ServiceListener mServiceListener; Loading @@ -48,6 +48,7 @@ public abstract class BluetoothProfileConnector<T> { private final String mServiceName; private final IBluetoothManager mBluetoothManager; private volatile T mService; private boolean mBound = false; private static final int MESSAGE_SERVICE_CONNECTED = 100; private static final int MESSAGE_SERVICE_DISCONNECTED = 101; Loading @@ -69,26 +70,29 @@ public abstract class BluetoothProfileConnector<T> { public void onServiceConnected(ComponentName className, IBinder service) { logDebug("Proxy object connected"); mService = getServiceInterface(service); mHandler.sendMessage(mHandler.obtainMessage( MESSAGE_SERVICE_CONNECTED)); sendEmptyMessage(MESSAGE_SERVICE_CONNECTED); } @Override public void onServiceDisconnected(ComponentName className) { logDebug("Proxy object disconnected"); T service = mService; doUnbind(); mHandler.sendMessage(mHandler.obtainMessage( MESSAGE_SERVICE_DISCONNECTED)); if (service != null) { sendEmptyMessage(MESSAGE_SERVICE_DISCONNECTED); } } }; /** @hide */ public BluetoothProfileConnector( Looper looper, BluetoothProfile profile, int profileId, String profileName, String serviceName, IBluetoothManager bluetoothManager) { super(looper); mProfileId = profileId; mProfileProxy = profile; mProfileName = profileName; Loading @@ -99,6 +103,7 @@ public abstract class BluetoothProfileConnector<T> { BluetoothProfileConnector( BluetoothProfile profile, int profileId, String profileName, String serviceName) { this( Looper.getMainLooper(), profile, profileId, profileName, Loading @@ -113,30 +118,30 @@ public abstract class BluetoothProfileConnector<T> { doUnbind(); } private boolean doBind() { private void doBind() { synchronized (mConnection) { if (mService == null) { if (!mBound) { logDebug("Binding service for " + mPackageName); mCloseGuard.open("doUnbind"); try { return mBluetoothManager.bindBluetoothProfileService( mBluetoothManager.bindBluetoothProfileService( mProfileId, mServiceName, mConnection); mBound = true; } catch (RemoteException re) { logError("Failed to bind service. " + re); return false; } } } return true; } private void doUnbind() { synchronized (mConnection) { if (mService != null) { if (mBound) { logDebug("Unbinding service for " + mPackageName); mCloseGuard.close(); try { mBluetoothManager.unbindBluetoothProfileService(mProfileId, mConnection); mBound = false; } catch (RemoteException re) { logError("Unable to unbind service: " + re); } finally { Loading Loading @@ -206,18 +211,15 @@ public abstract class BluetoothProfileConnector<T> { Log.e(mProfileName, log); } @SuppressLint("AndroidFrameworkBluetoothPermission") private final Handler mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { switch (msg.what) { case MESSAGE_SERVICE_CONNECTED: { case MESSAGE_SERVICE_CONNECTED: if (mServiceListener != null) { mServiceListener.onServiceConnected(mProfileId, mProfileProxy); } break; } case MESSAGE_SERVICE_DISCONNECTED: { case MESSAGE_SERVICE_DISCONNECTED: if (mServiceListener != null) { mServiceListener.onServiceDisconnected(mProfileId); } Loading @@ -225,5 +227,3 @@ public abstract class BluetoothProfileConnector<T> { } } } }; }
framework/tests/unit/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -23,7 +23,9 @@ android_test { static_libs: [ "androidx.test.ext.truth", "androidx.test.rules", "frameworks-base-testutils", "junit", "mockito-target", "modules-utils-bytesmatcher", ], test_suites: [ Loading
framework/tests/unit/src/android/bluetooth/BluetoothProfileConnectorTest.java +82 −18 Original line number Diff line number Diff line Loading @@ -18,16 +18,27 @@ package android.bluetooth; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.times; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verifyZeroInteractions; import android.content.ComponentName; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.test.TestLooper; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InOrder; /** Test cases for {@link BluetoothProfileConnector}. */ @SmallTest Loading @@ -36,6 +47,15 @@ public class BluetoothProfileConnectorTest { static class FakeBluetoothManager extends IBluetoothManager.Default { private IBluetoothStateChangeCallback mStateChangeCallback; private IBluetoothProfileServiceConnection mServiceConnection; private final Handler mHandler; private FakeBluetoothManager(Looper looper) { mHandler = new Handler(looper); } Looper getLooper() { return mHandler.getLooper(); } @Override public void registerStateChangeCallback(IBluetoothStateChangeCallback callback) { Loading Loading @@ -64,14 +84,28 @@ public class BluetoothProfileConnectorTest { int profile, IBluetoothProfileServiceConnection proxy) { if (proxy != mServiceConnection) throw new IllegalStateException(); mHandler.post( () -> { try { proxy.onServiceDisconnected(new ComponentName("pkg", "cls")); } catch (RemoteException e) { throw new RuntimeException(e); } }); mServiceConnection = null; } } private BluetoothProfileConnector createBluetoothProfileConnector( IBluetoothManager bluetoothManager) { FakeBluetoothManager bluetoothManager) { return new BluetoothProfileConnector( null, BluetoothProfile.HEADSET, "Headset", "HeadsetService", bluetoothManager) { bluetoothManager.getLooper(), null, BluetoothProfile.HEADSET, "Headset", "HeadsetService", bluetoothManager) { public IBinder getServiceInterface(IBinder service) { return service; } Loading @@ -80,89 +114,119 @@ public class BluetoothProfileConnectorTest { @Test public void bind_registerServiceConnection() throws RemoteException { FakeBluetoothManager bluetoothManager = new FakeBluetoothManager(); TestLooper looper = new TestLooper(); FakeBluetoothManager bluetoothManager = new FakeBluetoothManager(looper.getLooper()); BluetoothProfileConnector connector = createBluetoothProfileConnector(bluetoothManager); BluetoothProfile.ServiceListener listener = null; BluetoothProfile.ServiceListener listener = mock(BluetoothProfile.ServiceListener.class); connector.connect("test.package", listener); bluetoothManager.mStateChangeCallback.onBluetoothStateChange(true); looper.dispatchAll(); assertThat(bluetoothManager.mServiceConnection).isNotNull(); verifyZeroInteractions(listener); } @Test public void unbind_unregisterServiceConnection() throws RemoteException { FakeBluetoothManager bluetoothManager = new FakeBluetoothManager(); TestLooper looper = new TestLooper(); FakeBluetoothManager bluetoothManager = new FakeBluetoothManager(looper.getLooper()); BluetoothProfileConnector connector = createBluetoothProfileConnector(bluetoothManager); ComponentName componentName = new ComponentName("pkg", "cls"); BluetoothProfile.ServiceListener listener = null; BluetoothProfile.ServiceListener listener = mock(BluetoothProfile.ServiceListener.class); connector.connect("test.package", listener); bluetoothManager.mStateChangeCallback.onBluetoothStateChange(true); bluetoothManager.mServiceConnection.onServiceConnected(componentName, new Binder()); bluetoothManager.mServiceConnection.onServiceDisconnected(componentName); bluetoothManager.mStateChangeCallback.onBluetoothStateChange(false); looper.dispatchAll(); assertThat(bluetoothManager.mServiceConnection).isNull(); InOrder order = inOrder(listener); order.verify(listener).onServiceConnected(anyInt(), any()); order.verify(listener).onServiceDisconnected(anyInt()); order.verifyNoMoreInteractions(); } @Test public void upThenDown_unregisterServiceConnection() throws RemoteException { FakeBluetoothManager bluetoothManager = new FakeBluetoothManager(); TestLooper looper = new TestLooper(); FakeBluetoothManager bluetoothManager = new FakeBluetoothManager(looper.getLooper()); BluetoothProfileConnector connector = createBluetoothProfileConnector(bluetoothManager); BluetoothProfile.ServiceListener listener = null; BluetoothProfile.ServiceListener listener = mock(BluetoothProfile.ServiceListener.class); connector.connect("test.package", listener); bluetoothManager.mStateChangeCallback.onBluetoothStateChange(true); bluetoothManager.mStateChangeCallback.onBluetoothStateChange(false); looper.dispatchAll(); // TODO(b/302092694): Should be isNull assertThat(bluetoothManager.mServiceConnection).isNotNull(); assertThat(bluetoothManager.mServiceConnection).isNull(); verifyZeroInteractions(listener); } @Test public void disconnectAfterConnect_unregisterCallbacks() { FakeBluetoothManager bluetoothManager = new FakeBluetoothManager(); TestLooper looper = new TestLooper(); FakeBluetoothManager bluetoothManager = new FakeBluetoothManager(looper.getLooper()); BluetoothProfileConnector connector = createBluetoothProfileConnector(bluetoothManager); BluetoothProfile.ServiceListener listener = null; BluetoothProfile.ServiceListener listener = mock(BluetoothProfile.ServiceListener.class); connector.connect("test.package", listener); connector.disconnect(); looper.dispatchAll(); assertThat(bluetoothManager.mServiceConnection).isNull(); assertThat(bluetoothManager.mStateChangeCallback).isNull(); InOrder order = inOrder(listener); // TODO(b/309635805): This should not be here order.verify(listener).onServiceDisconnected(anyInt()); order.verifyNoMoreInteractions(); } @Test public void disconnectAfterBind_unregisterCallbacks() throws RemoteException { FakeBluetoothManager bluetoothManager = new FakeBluetoothManager(); TestLooper looper = new TestLooper(); FakeBluetoothManager bluetoothManager = new FakeBluetoothManager(looper.getLooper()); BluetoothProfileConnector connector = createBluetoothProfileConnector(bluetoothManager); BluetoothProfile.ServiceListener listener = null; BluetoothProfile.ServiceListener listener = mock(BluetoothProfile.ServiceListener.class); connector.connect("test.package", listener); bluetoothManager.mStateChangeCallback.onBluetoothStateChange(true); connector.disconnect(); looper.dispatchAll(); // TODO(b/302092694): Should be isNull assertThat(bluetoothManager.mServiceConnection).isNotNull(); assertThat(bluetoothManager.mServiceConnection).isNull(); assertThat(bluetoothManager.mStateChangeCallback).isNull(); InOrder order = inOrder(listener); // TODO(b/309635805): This should not be here order.verify(listener).onServiceDisconnected(anyInt()); order.verifyNoMoreInteractions(); } @Test public void disconnectAfterUnbind_unregisterCallbacks() throws RemoteException { FakeBluetoothManager bluetoothManager = new FakeBluetoothManager(); TestLooper looper = new TestLooper(); FakeBluetoothManager bluetoothManager = new FakeBluetoothManager(looper.getLooper()); BluetoothProfileConnector connector = createBluetoothProfileConnector(bluetoothManager); ComponentName componentName = new ComponentName("pkg", "cls"); BluetoothProfile.ServiceListener listener = null; BluetoothProfile.ServiceListener listener = mock(BluetoothProfile.ServiceListener.class); connector.connect("test.package", listener); bluetoothManager.mStateChangeCallback.onBluetoothStateChange(true); bluetoothManager.mServiceConnection.onServiceConnected(componentName, new Binder()); bluetoothManager.mServiceConnection.onServiceDisconnected(componentName); bluetoothManager.mStateChangeCallback.onBluetoothStateChange(false); looper.dispatchAll(); connector.disconnect(); looper.dispatchAll(); assertThat(bluetoothManager.mServiceConnection).isNull(); assertThat(bluetoothManager.mStateChangeCallback).isNull(); InOrder order = inOrder(listener); order.verify(listener).onServiceConnected(anyInt(), any()); // TODO(b/309635805): Should be only one order.verify(listener, times(2)).onServiceDisconnected(anyInt()); order.verifyNoMoreInteractions(); } }