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

Commit 26922b3f authored by Alexey Patosin's avatar Alexey Patosin Committed by Gerrit Code Review
Browse files

Merge "Use legacy BluetoothHeadsetClientCall for wearables instead of HfpClientCall." into main

parents 4fa3df5a 0eebef4a
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. */