Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7be6ce9d authored by William Escande's avatar William Escande
Browse files

HeadsetClientService: inject adapterService

Bug: 344658662
Test: atest HeadsetClientServiceTest
Test: atest HeadsetClientStateMachineTest
Flag: Exempt no-op refactor
Change-Id: I89ceb98f04e860ed0c96775846c246acae812ef8
parent db360885
Loading
Loading
Loading
Loading
+14 −20
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import static android.Manifest.permission.BLUETOOTH_CONNECT;
import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
import static android.content.pm.PackageManager.FEATURE_WATCH;

import static java.util.Objects.requireNonNull;

import android.annotation.RequiresPermission;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadsetClient;
@@ -74,8 +76,9 @@ public class HeadsetClientService extends ProfileService {
    private NativeInterface mNativeInterface = null;
    private HandlerThread mSmThread = null;
    private HeadsetClientStateMachineFactory mSmFactory = null;
    private DatabaseManager mDatabaseManager;
    private AudioManager mAudioManager = null;
    private final AdapterService mAdapterService;
    private final DatabaseManager mDatabaseManager;
    private final AudioManager mAudioManager;
    private BatteryManager mBatteryManager = null;
    private int mLastBatteryLevel = -1;
    // Maximum number of devices we can try connecting to in one session
@@ -85,8 +88,11 @@ public class HeadsetClientService extends ProfileService {

    public static final String HFP_CLIENT_STOP_TAG = "hfp_client_stop_tag";

    public HeadsetClientService(Context ctx) {
        super(ctx);
    public HeadsetClientService(AdapterService adapterService) {
        super(requireNonNull(adapterService));
        mAdapterService = adapterService;
        mDatabaseManager = requireNonNull(adapterService.getDatabase());
        mAudioManager = requireNonNull(getSystemService(AudioManager.class));
    }

    public static boolean isEnabled() {
@@ -106,24 +112,14 @@ public class HeadsetClientService extends ProfileService {
                throw new IllegalStateException("start() called twice");
            }

            mDatabaseManager =
                    Objects.requireNonNull(
                            AdapterService.getAdapterService().getDatabase(),
                            "DatabaseManager cannot be null when HeadsetClientService starts");

            // Setup the JNI service
            mNativeInterface = NativeInterface.getInstance();
            mNativeInterface.initialize();

            mBatteryManager = getSystemService(BatteryManager.class);

            mAudioManager = getSystemService(AudioManager.class);
            if (mAudioManager == null) {
                Log.e(TAG, "AudioManager service doesn't exist?");
            } else {
            // start AudioManager in a known state
            mAudioManager.setHfpEnabled(false);
            }

            mSmFactory = new HeadsetClientStateMachineFactory();
            synchronized (mStateMachineMap) {
@@ -1265,7 +1261,7 @@ public class HeadsetClientService extends ProfileService {

            // Allocate a new SM
            Log.d(TAG, "Creating a new state machine");
            sm = mSmFactory.make(this, mSmThread, mNativeInterface);
            sm = mSmFactory.make(mAdapterService, this, mSmThread, mNativeInterface);
            mStateMachineMap.put(device, sm);
            return sm;
        }
@@ -1295,9 +1291,7 @@ public class HeadsetClientService extends ProfileService {
    }

    void handleBatteryLevelChanged(BluetoothDevice device, int batteryLevel) {
        AdapterService.getAdapterService()
                .getRemoteDevices()
                .handleAgBatteryLevelChanged(device, batteryLevel);
        mAdapterService.getRemoteDevices().handleAgBatteryLevelChanged(device, batteryLevel);
    }

    @Override
+11 −14
Original line number Diff line number Diff line
@@ -134,6 +134,7 @@ public class HeadsetClientStateMachine extends StateMachine {
    private final AudioOn mAudioOn;
    private State mPrevState;

    private final AdapterService mAdapterService;
    private final HeadsetClientService mService;
    private final HeadsetService mHeadsetService;

@@ -878,11 +879,13 @@ public class HeadsetClientStateMachine extends StateMachine {
    }

    HeadsetClientStateMachine(
            AdapterService adapterService,
            HeadsetClientService context,
            HeadsetService headsetService,
            Looper looper,
            NativeInterface nativeInterface) {
        super(TAG, looper);
        mAdapterService = requireNonNull(adapterService);
        mService = requireNonNull(context);
        mNativeInterface = nativeInterface;
        mAudioManager = mService.getAudioManager();
@@ -949,6 +952,7 @@ public class HeadsetClientStateMachine extends StateMachine {
    }

    static HeadsetClientStateMachine make(
            AdapterService adapterService,
            HeadsetClientService context,
            HeadsetService headsetService,
            Looper looper,
@@ -956,17 +960,12 @@ public class HeadsetClientStateMachine extends StateMachine {
        Log.d(TAG, "make");
        HeadsetClientStateMachine hfcsm =
                new HeadsetClientStateMachine(
                        context, headsetService,
                        looper, nativeInterface);
                        adapterService, context, headsetService, looper, nativeInterface);
        hfcsm.start();
        return hfcsm;
    }

    synchronized void routeHfpAudio(boolean enable) {
        if (mAudioManager == null) {
            error("AudioManager is null!");
            return;
        }
        debug("hfp_enable=" + enable);
        if (enable && !sAudioIsRouted) {
            mAudioManager.setHfpEnabled(true);
@@ -1162,7 +1161,7 @@ public class HeadsetClientStateMachine extends StateMachine {
                                "Incoming AG rejected. connectionPolicy="
                                        + mService.getConnectionPolicy(device)
                                        + " bondState="
                                        + AdapterService.getAdapterService().getBondState(device));
                                        + mAdapterService.getBondState(device));
                        // reject the connection and stay in Disconnected state
                        // itself
                        mNativeInterface.disconnect(device);
@@ -2125,10 +2124,10 @@ public class HeadsetClientStateMachine extends StateMachine {

        BluetoothStatsLog.write(
                BluetoothStatsLog.BLUETOOTH_SCO_CONNECTION_STATE_CHANGED,
                AdapterService.getAdapterService().obfuscateAddress(device),
                mAdapterService.obfuscateAddress(device),
                getConnectionStateFromAudioState(newState),
                sco_codec,
                AdapterService.getAdapterService().getMetricId(device));
                mAdapterService.getMetricId(device));
        Intent intent = new Intent(BluetoothHeadsetClient.ACTION_AUDIO_STATE_CHANGED);
        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
        intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
@@ -2266,15 +2265,14 @@ public class HeadsetClientStateMachine extends StateMachine {

    List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
        List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>();
        AdapterService adapterService = AdapterService.getAdapterService();
        final BluetoothDevice[] bondedDevices = adapterService.getBondedDevices();
        final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices();
        if (bondedDevices == null) {
            return deviceList;
        }
        int connectionState;
        synchronized (this) {
            for (BluetoothDevice device : bondedDevices) {
                final ParcelUuid[] featureUuids = adapterService.getRemoteUuids(device);
                final ParcelUuid[] featureUuids = mAdapterService.getRemoteUuids(device);
                if (!Utils.arrayContains(featureUuids, BluetoothUuid.HFP_AG)) {
                    continue;
                }
@@ -2299,8 +2297,7 @@ public class HeadsetClientStateMachine extends StateMachine {
        // connection. Allow this connection, provided the device is bonded
        if ((BluetoothProfile.CONNECTION_POLICY_FORBIDDEN < connectionPolicy)
                || ((BluetoothProfile.CONNECTION_POLICY_UNKNOWN == connectionPolicy)
                        && (AdapterService.getAdapterService().getBondState(device)
                                != BluetoothDevice.BOND_NONE))) {
                        && (mAdapterService.getBondState(device) != BluetoothDevice.BOND_NONE))) {
            ret = true;
        }
        return ret;
+10 −2
Original line number Diff line number Diff line
@@ -18,14 +18,22 @@ package com.android.bluetooth.hfpclient;

import android.os.HandlerThread;

import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.hfp.HeadsetService;

// Factory so that StateMachine objected can be mocked
public class HeadsetClientStateMachineFactory {
    /** Factory method to create state machine for headset client */
    public HeadsetClientStateMachine make(
            HeadsetClientService context, HandlerThread t, NativeInterface nativeInterface) {
            AdapterService adapterService,
            HeadsetClientService context,
            HandlerThread t,
            NativeInterface nativeInterface) {
        return HeadsetClientStateMachine.make(
                context, HeadsetService.getHeadsetService(), t.getLooper(), nativeInterface);
                adapterService,
                context,
                HeadsetService.getHeadsetService(),
                t.getLooper(),
                nativeInterface);
    }
}
+16 −26
Original line number Diff line number Diff line
@@ -32,9 +32,9 @@ import android.bluetooth.BluetoothSinkAudioPolicy;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.media.AudioManager;
import android.os.BatteryManager;

import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;

@@ -61,9 +61,6 @@ import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
public class HeadsetClientServiceTest {
    private HeadsetClientService mService = null;
    private BluetoothAdapter mAdapter = null;
    private Context mTargetContext;
    private boolean mIsAdapterServiceSet;
    private boolean mIsHeadsetClientServiceStarted;

    private static final int STANDARD_WAIT_MILLIS = 1000;
@@ -72,17 +69,20 @@ public class HeadsetClientServiceTest {
    @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();

    @Mock private AdapterService mAdapterService;
    private AudioManager mMockAudioManager;
    @Mock private HeadsetClientStateMachine mStateMachine;
    @Mock private NativeInterface mNativeInterface;
    @Mock private DatabaseManager mDatabaseManager;
    @Mock private RemoteDevices mRemoteDevices;

    <T> T mockGetSystemService(String serviceName, Class<T> serviceClass) {
        return TestUtils.mockGetSystemService(mAdapterService, serviceName, serviceClass);
    }

    @Before
    public void setUp() throws Exception {
        mTargetContext = InstrumentationRegistry.getTargetContext();

        TestUtils.setAdapterService(mAdapterService);
        mIsAdapterServiceSet = true;
        mMockAudioManager = mockGetSystemService(Context.AUDIO_SERVICE, AudioManager.class);
        mockGetSystemService(Context.BATTERY_SERVICE, BatteryManager.class);
        doReturn(mDatabaseManager).when(mAdapterService).getDatabase();
        doReturn(mRemoteDevices).when(mAdapterService).getRemoteDevices();
        NativeInterface.setInstance(mNativeInterface);
@@ -92,9 +92,6 @@ public class HeadsetClientServiceTest {
    public void tearDown() throws Exception {
        NativeInterface.setInstance(null);
        stopServiceIfStarted();
        if (mIsAdapterServiceSet) {
            TestUtils.clearAdapterService(mAdapterService);
        }
    }

    @Test
@@ -170,52 +167,45 @@ public class HeadsetClientServiceTest {

    @Test
    public void testHfpClientConnectionServiceStarted() throws Exception {
        Context context = Mockito.mock(Context.class);
        PackageManager packageManager = Mockito.mock(PackageManager.class);

        doReturn(false).when(packageManager).hasSystemFeature(FEATURE_WATCH);
        doReturn(packageManager).when(context).getPackageManager();
        doReturn(packageManager).when(mAdapterService).getPackageManager();

        HeadsetClientService service = new HeadsetClientService(context);
        HeadsetClientService service = new HeadsetClientService(mAdapterService);
        service.start();

        verify(context).startService(any(Intent.class));
        verify(mAdapterService).startService(any(Intent.class));

        service.stop();
    }

    @Test
    public void testHfpClientConnectionServiceNotStarted_wearable() throws Exception {
        Context context = Mockito.mock(Context.class);
        PackageManager packageManager = Mockito.mock(PackageManager.class);

        doReturn(true).when(packageManager).hasSystemFeature(FEATURE_WATCH);
        doReturn(packageManager).when(context).getPackageManager();
        doReturn(packageManager).when(mAdapterService).getPackageManager();

        HeadsetClientService service = new HeadsetClientService(context);
        HeadsetClientService service = new HeadsetClientService(mAdapterService);
        service.start();

        verify(context, never()).startService(any(Intent.class));
        verify(mAdapterService, never()).startService(any(Intent.class));

        service.stop();
    }

    private void startService() throws Exception {
        // At this point the service should have started so check NOT null
        mService = new HeadsetClientService(mTargetContext);
        mService = new HeadsetClientService(mAdapterService);
        mService.start();
        mService.setAvailable(true);
        // Try getting the Bluetooth adapter
        mAdapter = BluetoothAdapter.getDefaultAdapter();
        Assert.assertNotNull(mAdapter);
        mIsHeadsetClientServiceStarted = true;
    }

    private void stopServiceIfStarted() throws Exception {
        if (mIsHeadsetClientServiceStarted) {
            mService.stop();
            mService = HeadsetClientService.getHeadsetClientService();
            Assert.assertNull(mService);
            Assert.assertNull(HeadsetClientService.getHeadsetClientService());
        }
    }
}
+5 −4
Original line number Diff line number Diff line
@@ -129,7 +129,6 @@ public class HeadsetClientStateMachineTest {
        when(mMockHfpResources.getInteger(R.integer.hfp_clcc_poll_interval_during_call))
                .thenReturn(2000);

        TestUtils.setAdapterService(mAdapterService);
        doReturn(mRemoteDevices).when(mAdapterService).getRemoteDevices();
        doReturn(true).when(mNativeInterface).sendAndroidAt(anyObject(), anyString());

@@ -144,6 +143,7 @@ public class HeadsetClientStateMachineTest {
        // Manage looper execution in main test thread explicitly to guarantee timing consistency
        mHeadsetClientStateMachine =
                new TestHeadsetClientStateMachine(
                        mAdapterService,
                        mHeadsetClientService,
                        mHeadsetService,
                        mHandlerThread.getLooper(),
@@ -159,7 +159,6 @@ public class HeadsetClientStateMachineTest {
        mHeadsetClientStateMachine.doQuit();
        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
        mHandlerThread.quit();
        TestUtils.clearAdapterService(mAdapterService);
        verifyNoMoreInteractions(mHeadsetService);
    }

@@ -177,6 +176,7 @@ public class HeadsetClientStateMachineTest {
        if (mHeadsetClientStateMachine == null) {
            mHeadsetClientStateMachine =
                    new TestHeadsetClientStateMachine(
                            mAdapterService,
                            mHeadsetClientService,
                            mHeadsetService,
                            mHandlerThread.getLooper(),
@@ -725,7 +725,7 @@ public class HeadsetClientStateMachineTest {
            mHeadsetClientStateMachine.sendMessage(StackEvent.STACK_EVENT, unknownEvt);
            TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());

            // receive CMD_RESULT OK after the Anroid AT command from remote
            // receive CMD_RESULT OK after the Android AT command from remote
            StackEvent cmdResEvt = new StackEvent(StackEvent.EVENT_TYPE_CMD_RESULT);
            cmdResEvt.valueInt = StackEvent.CMD_RESULT_TYPE_OK;
            cmdResEvt.device = mTestDevice;
@@ -1837,11 +1837,12 @@ public class HeadsetClientStateMachineTest {
        boolean mForceSetAudioPolicyProperty = false;

        TestHeadsetClientStateMachine(
                AdapterService adapterService,
                HeadsetClientService context,
                HeadsetService headsetService,
                Looper looper,
                NativeInterface nativeInterface) {
            super(context, headsetService, looper, nativeInterface);
            super(adapterService, context, headsetService, looper, nativeInterface);
        }

        public boolean doesSuperHaveDeferredMessages(int what) {