Add tests for BluetoothHapClientBinder

Bug: 237467631
Tag: #refactor
Test: atest HapClientTest
Change-Id: If94ee2cb1552b4dd9005ee9b6f65a71de7e9132e
diff --git a/android/app/src/com/android/bluetooth/hap/HapClientService.java b/android/app/src/com/android/bluetooth/hap/HapClientService.java
index c5c5672..52aaaaf 100644
--- a/android/app/src/com/android/bluetooth/hap/HapClientService.java
+++ b/android/app/src/com/android/bluetooth/hap/HapClientService.java
@@ -1209,6 +1209,8 @@
     @VisibleForTesting
     static class BluetoothHapClientBinder extends IBluetoothHapClient.Stub
             implements IProfileServiceBinder {
+        @VisibleForTesting
+        boolean mIsTesting = false;
         private HapClientService mService;
 
         BluetoothHapClientBinder(HapClientService svc) {
@@ -1216,6 +1218,9 @@
         }
 
         private HapClientService getService(AttributionSource source) {
+            if (mIsTesting) {
+                return mService;
+            }
             if (!Utils.checkCallerIsSystemOrActiveUser(TAG)
                     || !Utils.checkServiceAvailable(mService, TAG)
                     || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) {
diff --git a/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientTest.java b/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientTest.java
index d4f3e46..1e82632 100644
--- a/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientTest.java
+++ b/android/app/tests/unit/src/com/android/bluetooth/hap/HapClientTest.java
@@ -17,9 +17,26 @@
 
 package com.android.bluetooth.hap;
 
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
-import android.bluetooth.*;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHapClient;
+import android.bluetooth.BluetoothHapPresetInfo;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothStatusCodes;
+import android.bluetooth.BluetoothUuid;
+import android.bluetooth.IBluetoothHapClientCallback;
+import android.content.AttributionSource;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -28,32 +45,32 @@
 import android.os.Looper;
 import android.os.ParcelUuid;
 import android.os.RemoteException;
-import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.MediumTest;
 import androidx.test.rule.ServiceTestRule;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.bluetooth.R;
 import com.android.bluetooth.TestUtils;
 import com.android.bluetooth.Utils;
 import com.android.bluetooth.btservice.AdapterService;
 import com.android.bluetooth.btservice.ServiceFactory;
 import com.android.bluetooth.btservice.storage.DatabaseManager;
 import com.android.bluetooth.csip.CsipSetCoordinatorService;
+import com.android.bluetooth.x.com.android.modules.utils.SynchronousResultReceiver;
 
 import org.junit.After;
 import org.junit.Assert;
-import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
+import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -77,6 +94,8 @@
     private BluetoothDevice mDevice3;
     private Context mTargetContext;
     private HapClientService mService;
+    private HapClientService.BluetoothHapClientBinder mServiceBinder;
+    private AttributionSource mAttributionSource;
     private HasIntentReceiver mHasIntentReceiver;
     private HashMap<BluetoothDevice, LinkedBlockingQueue<Intent>> mIntentQueue;
 
@@ -116,11 +135,14 @@
         doReturn(true, false).when(mAdapterService).isStartedProfile(anyString());
 
         mAdapter = BluetoothAdapter.getDefaultAdapter();
+        mAttributionSource = mAdapter.getAttributionSource();
 
         startService();
         mService.mHapClientNativeInterface = mNativeInterface;
         mService.mFactory = mServiceFactory;
         doReturn(mCsipService).when(mServiceFactory).getCsipSetCoordinatorService();
+        mServiceBinder = (HapClientService.BluetoothHapClientBinder) mService.initBinder();
+        mServiceBinder.mIsTesting = true;
 
         // Set up the State Changed receiver
         IntentFilter filter = new IntentFilter();
@@ -260,7 +282,7 @@
      * Test get/set policy for BluetoothDevice
      */
     @Test
-    public void testGetSetPolicy() {
+    public void testGetSetPolicy() throws Exception {
         when(mDatabaseManager
                 .getProfileConnectionPolicy(mDevice, BluetoothProfile.HAP_CLIENT))
                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
@@ -278,9 +300,14 @@
         when(mDatabaseManager
                 .getProfileConnectionPolicy(mDevice, BluetoothProfile.HAP_CLIENT))
                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
+        // call getConnectionPolicy via binder
+        final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get();
+        int defaultRecvValue = -1000;
+        mServiceBinder.getConnectionPolicy(mDevice, mAttributionSource, recv);
+        int policy = recv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS))
+                .getValue(defaultRecvValue);
         Assert.assertEquals("Setting device policy to POLICY_ALLOWED",
-                BluetoothProfile.CONNECTION_POLICY_ALLOWED,
-                mService.getConnectionPolicy(mDevice));
+                BluetoothProfile.CONNECTION_POLICY_ALLOWED, policy);
     }
 
     /**
@@ -398,7 +425,7 @@
      * Test that an outgoing connection times out
      */
     @Test
-    public void testOutgoingConnectTimeout() {
+    public void testOutgoingConnectTimeout() throws Exception {
         // Update the device policy so okToConnect() returns true
         when(mDatabaseManager
                 .getProfileConnectionPolicy(mDevice, BluetoothProfile.HAP_CLIENT))
@@ -415,19 +442,22 @@
         Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
                 mService.getConnectionState(mDevice));
 
-        // Verify the connection state broadcast, and that we are in Disconnected state
+        // Verify the connection state broadcast, and that we are in Disconnected state via binder
         verifyConnectionStateIntent(HapClientStateMachine.sConnectTimeoutMs * 2,
-                mDevice, BluetoothProfile.STATE_DISCONNECTED,
-                BluetoothProfile.STATE_CONNECTING);
-        Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED,
-                mService.getConnectionState(mDevice));
+                mDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTING);
+        final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get();
+        int defaultRecvValue = -1000;
+        mServiceBinder.getConnectionState(mDevice, mAttributionSource, recv);
+        int state = recv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS))
+                .getValue(defaultRecvValue);
+        Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, state);
     }
 
     /**
      * Test that an outgoing connection to two device that have HAS UUID is successful
      */
     @Test
-    public void testConnectTwo() {
+    public void testConnectTwo() throws Exception {
         doReturn(new ParcelUuid[]{BluetoothUuid.HAS}).when(mAdapterService)
                 .getRemoteUuids(any(BluetoothDevice.class));
 
@@ -438,7 +468,12 @@
         BluetoothDevice Device2 = TestUtils.getTestDevice(mAdapter, 1);
         testConnectingDevice(Device2);
 
-        List<BluetoothDevice> devices = mService.getConnectedDevices();
+        // indirect call of mService.getConnectedDevices to test BluetoothHearingAidBinder
+        final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+                SynchronousResultReceiver.get();
+        mServiceBinder.getConnectedDevices(mAttributionSource, recv);
+        List<BluetoothDevice> devices = recv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS))
+                .getValue(null);
         Assert.assertTrue(devices.contains(mDevice));
         Assert.assertTrue(devices.contains(Device2));
         Assert.assertNotEquals(mDevice, Device2);
@@ -450,14 +485,14 @@
     @Test
     public void testCallsForNotConnectedDevice() {
         Assert.assertEquals(BluetoothHapClient.PRESET_INDEX_UNAVAILABLE,
-                        mService.getActivePresetIndex(mDevice));
+                mService.getActivePresetIndex(mDevice));
     }
 
     /**
      * Test getting HAS coordinated sets.
      */
     @Test
-    public void testGetHapGroupCoordinatedOps() {
+    public void testGetHapGroupCoordinatedOps() throws Exception {
         doReturn(new ParcelUuid[]{BluetoothUuid.HAS}).when(mAdapterService)
                 .getRemoteUuids(any(BluetoothDevice.class));
         testConnectingDevice(mDevice);
@@ -482,7 +517,12 @@
         Assert.assertEquals(3, mService.getHapGroup(mDevice3));
 
         /* Third one has no coordinated operations support but is part of the group */
-        Assert.assertEquals(2, mService.getHapGroup(mDevice2));
+        final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get();
+        int defaultRecvValue = -1000;
+        mServiceBinder.getHapGroup(mDevice2, mAttributionSource, recv);
+        int hapGroup = recv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS))
+                .getValue(defaultRecvValue);
+        Assert.assertEquals(2, hapGroup);
     }
 
     /**
@@ -505,7 +545,7 @@
             throw e.rethrowFromSystemServer();
         }
 
-        mService.selectPreset(mDevice, 0x01);
+        mServiceBinder.selectPreset(mDevice, 0x01, mAttributionSource);
         verify(mNativeInterface, times(1))
                 .selectActivePreset(eq(mDevice), eq(0x01));
     }
@@ -531,7 +571,7 @@
             throw e.rethrowFromSystemServer();
         }
 
-        mService.selectPresetForGroup(0x03, 0x01);
+        mServiceBinder.selectPresetForGroup(0x03, 0x01, mAttributionSource);
         verify(mNativeInterface, times(1))
                 .groupSelectActivePreset(eq(0x03), eq(0x01));
     }
@@ -546,7 +586,7 @@
         testConnectingDevice(mDevice);
 
         // Verify Native Interface call
-        mService.switchToNextPreset(mDevice);
+        mServiceBinder.switchToNextPreset(mDevice, mAttributionSource);
         verify(mNativeInterface, times(1))
                 .nextActivePreset(eq(mDevice));
     }
@@ -563,7 +603,7 @@
         mNativeInterface.onFeaturesUpdate(getByteAddress(mDevice3), flags);
 
         // Verify Native Interface call
-        mService.switchToNextPresetForGroup(0x03);
+        mServiceBinder.switchToNextPresetForGroup(0x03, mAttributionSource);
         verify(mNativeInterface, times(1)).groupNextActivePreset(eq(0x03));
     }
 
@@ -577,7 +617,7 @@
         testConnectingDevice(mDevice);
 
         // Verify Native Interface call
-        mService.switchToPreviousPreset(mDevice);
+        mServiceBinder.switchToPreviousPreset(mDevice, mAttributionSource);
         verify(mNativeInterface, times(1))
                 .previousActivePreset(eq(mDevice));
     }
@@ -596,7 +636,7 @@
         mNativeInterface.onFeaturesUpdate(getByteAddress(mDevice), flags);
 
         // Verify Native Interface call
-        mService.switchToPreviousPresetForGroup(0x02);
+        mServiceBinder.switchToPreviousPresetForGroup(0x02, mAttributionSource);
         verify(mNativeInterface, times(1)).groupPreviousActivePreset(eq(0x02));
     }
 
@@ -604,26 +644,45 @@
      * Test that getActivePresetIndex returns cached value.
      */
     @Test
-    public void testGetActivePresetIndex() {
+    public void testGetActivePresetIndex() throws Exception {
         doReturn(new ParcelUuid[]{BluetoothUuid.HAS}).when(mAdapterService)
                 .getRemoteUuids(any(BluetoothDevice.class));
         testConnectingDevice(mDevice);
         testOnPresetSelected(mDevice, 0x01);
 
-        // Verify cached value
-        Assert.assertEquals(0x01, mService.getActivePresetIndex(mDevice));
+        // Verify cached value via binder
+        final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get();
+        int defaultRecvValue = -1000;
+        mServiceBinder.getActivePresetIndex(mDevice, mAttributionSource, recv);
+        int presetIndex = recv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS))
+                .getValue(defaultRecvValue);
+        Assert.assertEquals(0x01, presetIndex);
     }
 
     /**
      * Test that getActivePresetInfo returns cached value for valid parameters.
      */
     @Test
-    public void testGetActivePresetInfo() {
+    public void testGetPresetInfoAndActivePresetInfo() throws Exception {
         doReturn(new ParcelUuid[]{BluetoothUuid.HAS}).when(mAdapterService)
                 .getRemoteUuids(any(BluetoothDevice.class));
         testConnectingDevice(mDevice2);
 
         // Check when active preset is not known yet
+        final SynchronousResultReceiver<List<BluetoothHapPresetInfo>> presetListRecv =
+                SynchronousResultReceiver.get();
+        mServiceBinder.getAllPresetInfo(mDevice2, mAttributionSource, presetListRecv);
+        List<BluetoothHapPresetInfo> presetList = presetListRecv.awaitResultNoInterrupt(
+                Duration.ofMillis(TIMEOUT_MS)).getValue(null);
+
+        final SynchronousResultReceiver<BluetoothHapPresetInfo> presetRecv =
+                SynchronousResultReceiver.get();
+        mServiceBinder.getPresetInfo(mDevice2, 0x01, mAttributionSource, presetRecv);
+        BluetoothHapPresetInfo presetInfo = presetRecv.awaitResultNoInterrupt(
+                Duration.ofMillis(TIMEOUT_MS)).getValue(null);
+        Assert.assertTrue(presetList.contains(presetInfo));
+        Assert.assertEquals(0x01, presetInfo.getIndex());
+
         Assert.assertEquals(BluetoothHapClient.PRESET_INDEX_UNAVAILABLE,
                 mService.getActivePresetIndex(mDevice2));
         Assert.assertEquals(null, mService.getActivePresetInfo(mDevice2));
@@ -633,9 +692,13 @@
 
         // Check when active preset is known
         Assert.assertEquals(0x01, mService.getActivePresetIndex(mDevice2));
-        BluetoothHapPresetInfo info = mService.getActivePresetInfo(mDevice2);
+        final SynchronousResultReceiver<BluetoothHapPresetInfo> recv =
+                SynchronousResultReceiver.get();
+        mServiceBinder.getActivePresetInfo(mDevice2, mAttributionSource, recv);
+        BluetoothHapPresetInfo info = recv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS))
+                .getValue(null);
         Assert.assertNotNull(info);
-        Assert.assertEquals(0x01, info.getIndex());
+        Assert.assertEquals("One", info.getName());
     }
 
     /**
@@ -647,7 +710,7 @@
                 .getRemoteUuids(any(BluetoothDevice.class));
         testConnectingDevice(mDevice);
 
-        mService.setPresetName(mDevice, 0x00, "ExamplePresetName");
+        mServiceBinder.setPresetName(mDevice, 0x00, "ExamplePresetName", mAttributionSource);
         verify(mNativeInterface, times(0))
                 .setPresetName(eq(mDevice), eq(0x00), eq("ExamplePresetName"));
         try {
@@ -678,7 +741,8 @@
         int flags = 0x21;
         mNativeInterface.onFeaturesUpdate(getByteAddress(mDevice), flags);
 
-        mService.setPresetNameForGroup(test_group, 0x00, "ExamplePresetName");
+        mServiceBinder.setPresetNameForGroup(
+                test_group, 0x00, "ExamplePresetName", mAttributionSource);
         try {
             verify(mCallback, after(TIMEOUT_MS).times(1)).onSetPresetNameForGroupFailed(eq(test_group),
                     eq(BluetoothStatusCodes.ERROR_HAP_INVALID_PRESET_INDEX));
@@ -937,6 +1001,57 @@
         }
     }
 
+    @Test
+    public void testServiceBinderGetDevicesMatchingConnectionStates() throws Exception {
+        final SynchronousResultReceiver<List<BluetoothDevice>> recv =
+                SynchronousResultReceiver.get();
+        mServiceBinder.getDevicesMatchingConnectionStates(null, mAttributionSource, recv);
+        List<BluetoothDevice> devices = recv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS))
+                .getValue(null);
+        Assert.assertEquals(0, devices.size());
+    }
+
+    @Test
+    public void testServiceBinderSetConnectionPolicy() throws Exception {
+        final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get();
+        boolean defaultRecvValue = false;
+        mServiceBinder.setConnectionPolicy(
+                mDevice, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, mAttributionSource, recv);
+        Assert.assertTrue(recv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS))
+                .getValue(defaultRecvValue));
+        verify(mDatabaseManager).setProfileConnectionPolicy(
+                mDevice, BluetoothProfile.HAP_CLIENT, BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
+    }
+
+    @Test
+    public void testServiceBinderGetFeatures() throws Exception {
+        final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get();
+        int defaultRecvValue = -1000;
+        mServiceBinder.getFeatures(mDevice, mAttributionSource, recv);
+        int features = recv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS))
+                .getValue(defaultRecvValue);
+        Assert.assertEquals(0x00, features);
+    }
+
+    @Test
+    public void testServiceBinderRegisterUnregisterCallback() throws Exception {
+        IBluetoothHapClientCallback callback = Mockito.mock(IBluetoothHapClientCallback.class);
+        Binder binder = Mockito.mock(Binder.class);
+        when(callback.asBinder()).thenReturn(binder);
+
+        int size = mService.mCallbacks.getRegisteredCallbackCount();
+        SynchronousResultReceiver<Void> recv = SynchronousResultReceiver.get();
+        mServiceBinder.registerCallback(callback, mAttributionSource, recv);
+        recv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS)).getValue(null);
+        Assert.assertEquals(size + 1, mService.mCallbacks.getRegisteredCallbackCount());
+
+        recv = SynchronousResultReceiver.get();
+        mServiceBinder.unregisterCallback(callback, mAttributionSource, recv);
+        recv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS)).getValue(null);
+        Assert.assertEquals(size, mService.mCallbacks.getRegisteredCallbackCount());
+
+    }
+
     /**
      * Helper function to test device connecting
      */