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

Commit 0eebef4a authored by Alexey Patosin's avatar Alexey Patosin
Browse files

Use legacy BluetoothHeadsetClientCall for wearables instead of HfpClientCall.

Test: atest BluetoothInstrumentationTests
Bug: 300924083
Change-Id: I96c9c747868d65cd8df741f1a431c88f719062f4
parent 2f3656c4
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -263,7 +263,11 @@ public class HeadsetClientService extends ProfileService {
        }
    };

    private static BluetoothHeadsetClientCall toLegacyCall(HfpClientCall call) {
    /**
     * Convert {@code HfpClientCall} to legacy {@code BluetoothHeadsetClientCall} still used by some
     * clients.
     */
    static BluetoothHeadsetClientCall toLegacyCall(HfpClientCall call) {
        if (call == null) return null;
        return new BluetoothHeadsetClientCall(call.getDevice(), call.getId(), call.getUUID(),
                call.getState(), call.getNumber(), call.isMultiParty(), call.isOutgoing(),
+13 −2
Original line number Diff line number Diff line
@@ -35,6 +35,9 @@ package com.android.bluetooth.hfpclient;

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.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
@@ -389,7 +392,15 @@ public class HeadsetClientStateMachine extends StateMachine {
        logD("sendCallChangedIntent " + c);
        Intent intent = new Intent(BluetoothHeadsetClient.ACTION_CALL_CHANGED);
        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);

        if (mService.getPackageManager().hasSystemFeature(FEATURE_WATCH)) {
            logD("Send legacy call");
            intent.putExtra(
                    BluetoothHeadsetClient.EXTRA_CALL, HeadsetClientService.toLegacyCall(c));
        } else {
            intent.putExtra(BluetoothHeadsetClient.EXTRA_CALL, c);
        }

        Utils.sendBroadcast(mService, intent, BLUETOOTH_CONNECT,
                Utils.getTempAllowlistBroadcastOptions());
        HfpClientConnectionService.onCallChanged(c.getDevice(), c);
@@ -872,7 +883,7 @@ public class HeadsetClientStateMachine extends StateMachine {
    HeadsetClientStateMachine(HeadsetClientService context, HeadsetService headsetService,
                              Looper looper, NativeInterface nativeInterface) {
        super(TAG, looper);
        mService = context;
        mService = requireNonNull(context);
        mNativeInterface = nativeInterface;
        mAudioManager = mService.getAudioManager();
        mHeadsetService = headsetService;
+63 −1
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.bluetooth.hfpclient;

import static android.content.pm.PackageManager.FEATURE_WATCH;

import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;

import static com.android.bluetooth.hfpclient.HeadsetClientStateMachine.AT_OK;
@@ -32,11 +34,13 @@ import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothAssignedNumbers;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadsetClient;
import android.bluetooth.BluetoothHeadsetClientCall;
import android.bluetooth.BluetoothSinkAudioPolicy;
import android.bluetooth.BluetoothStatusCodes;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.media.AudioManager;
import android.os.Bundle;
@@ -93,7 +97,7 @@ public class HeadsetClientStateMachineTest {
    @Mock private HeadsetClientService mHeadsetClientService;
    @Mock private AudioManager mAudioManager;
    @Mock private RemoteDevices mRemoteDevices;

    @Mock private PackageManager mPackageManager;
    @Mock private NativeInterface mNativeInterface;

    private static final int STANDARD_WAIT_MILLIS = 1000;
@@ -114,6 +118,8 @@ public class HeadsetClientStateMachineTest {
        when(mHeadsetClientService.getAudioManager()).thenReturn(
                mAudioManager);
        when(mHeadsetClientService.getResources()).thenReturn(mMockHfpResources);
        when(mHeadsetClientService.getPackageManager()).thenReturn(mPackageManager);
        when(mPackageManager.hasSystemFeature(FEATURE_WATCH)).thenReturn(false);
        when(mMockHfpResources.getBoolean(R.bool.hfp_clcc_poll_during_call)).thenReturn(true);
        when(mMockHfpResources.getInteger(R.integer.hfp_clcc_poll_interval_during_call))
                .thenReturn(2000);
@@ -462,7 +468,63 @@ public class HeadsetClientStateMachineTest {
                intentArgument.getValue().getIntExtra(BluetoothHeadsetClient.EXTRA_IN_BAND_RING,
                        -1));
        Assert.assertEquals(false, mHeadsetClientStateMachine.getInBandRing());
    }

    /** Test that wearables use {@code BluetoothHeadsetClientCall} in intent. */
    @Test
    public void testWearablesUseBluetoothHeadsetClientCallInIntent() {
        // Specify the watch form factor when package manager is asked
        when(mPackageManager.hasSystemFeature(FEATURE_WATCH)).thenReturn(true);

        // Skip over the Android AT commands to test this code path
        doReturn(false).when(mNativeInterface).sendAndroidAt(anyObject(), anyString());

        // Return true for connection policy to allow connections
        when(mHeadsetClientService.getConnectionPolicy(any(BluetoothDevice.class)))
                .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);

        // Send an incoming connection event
        StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
        event.device = mTestDevice;
        event.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_CONNECTED;
        mHeadsetClientStateMachine.sendMessage(StackEvent.STACK_EVENT, event);

        // Send a message to trigger service level connection using the required ECS feature
        event = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
        event.device = mTestDevice;
        event.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_SLC_CONNECTED;
        event.valueInt2 = HeadsetClientHalConstants.PEER_FEAT_ECS;
        mHeadsetClientStateMachine.sendMessage(StackEvent.STACK_EVENT, event);

        // Dial a phone call, which will fail as @{code dial} method is not specified in @{code
        // mNativeInterface} mock and trigger a call state changed broadcast
        mHeadsetClientStateMachine.sendMessage(
                HeadsetClientStateMachine.DIAL_NUMBER,
                new HfpClientCall(
                        mTestDevice,
                        0,
                        HfpClientCall.CALL_STATE_WAITING,
                        "1",
                        false,
                        false,
                        false));

        // Wait for processing
        TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());

        // Verify the broadcast
        ArgumentCaptor<Intent> intentArgument = ArgumentCaptor.forClass(Intent.class);
        verify(mHeadsetClientService, times(1))
                .sendBroadcast(intentArgument.capture(), anyString(), any(Bundle.class));

        // Verify that the parcelable extra has a legacy {@code BluetoothHeadsetClientCall} type for
        // wearables.
        Assert.assertThat(
                intentArgument.getValue().getParcelableExtra(BluetoothHeadsetClient.EXTRA_CALL),
                IsInstanceOf.instanceOf(BluetoothHeadsetClientCall.class));

        // To satisfy the @After verification
        verify(mHeadsetService).updateInbandRinging(eq(mTestDevice), eq(true));
    }

    /* Utility function to simulate HfpClient is connected. */