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

Commit 546e5e25 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "HFPClient: Support HF Indicators"

parents 3df1e20f 544b22d7
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -146,6 +146,11 @@ public final class HeadsetClientHalConstants {
    static final int PEER_FEAT_EXTERR = 0x00000100;
    // Codec Negotiation
    static final int PEER_FEAT_CODEC = 0x00000200;
    // HFP 1.7 features
    // HF Indicators
    static final int PEER_FEAT_HF_IND = 0x00000400;
    // ESCO S4 link setting
    static final int PEER_FEAT_ESCO_S4 = 0x00000800;

    // AG's 3WC features masks
    // match up with masks in bt_hf_client.h
@@ -171,6 +176,7 @@ public final class HeadsetClientHalConstants {

    static final int HANDSFREECLIENT_AT_CMD_NREC = 15;
    static final int HANDSFREECLIENT_AT_CMD_VENDOR_SPECIFIC_CMD = 16;
    static final int HANDSFREECLIENT_AT_CMD_BIEV = 17;

    // Flag to check for local NREC support
    static final boolean HANDSFREECLIENT_NREC_SUPPORTED = true;
+44 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.os.BatteryManager;
import android.os.Bundle;
import android.os.HandlerThread;
import android.os.Message;
@@ -62,6 +63,8 @@ public class HeadsetClientService extends ProfileService {
    private HeadsetClientStateMachineFactory mSmFactory = null;
    private DatabaseManager mDatabaseManager;
    private AudioManager mAudioManager = null;
    private BatteryManager mBatteryManager = null;
    private int mLastBatteryLevel = -1;
    // Maxinum number of devices we can try connecting to in one session
    private static final int MAX_STATE_MACHINES_POSSIBLE = 100;

@@ -89,6 +92,8 @@ public class HeadsetClientService extends ProfileService {
        mNativeInterface = NativeInterface.getInstance();
        mNativeInterface.initialize();

        mBatteryManager = getSystemService(BatteryManager.class);

        mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        if (mAudioManager == null) {
            Log.e(TAG, "AudioManager service doesn't exist?");
@@ -101,6 +106,7 @@ public class HeadsetClientService extends ProfileService {
        mStateMachineMap.clear();

        IntentFilter filter = new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION);
        filter.addAction(Intent.ACTION_BATTERY_CHANGED);
        registerReceiver(mBroadcastReceiver, filter);

        // Start the HfpClientConnectionService to create connection with telecom when HFP
@@ -183,6 +189,29 @@ public class HeadsetClientService extends ProfileService {
                        }
                    }
                }
            } else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
                int batteryIndicatorID = 2;
                int batteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);

                if (batteryLevel == mLastBatteryLevel) {
                    return;
                }
                mLastBatteryLevel = batteryLevel;

                if (DBG) {
                    Log.d(TAG,
                            "Send battery level update BIEV(2," + batteryLevel + ") command");
                }

                synchronized (this) {
                    for (HeadsetClientStateMachine sm : mStateMachineMap.values()) {
                        if (sm != null) {
                            sm.sendMessage(HeadsetClientStateMachine.SEND_BIEV,
                                    batteryIndicatorID,
                                    batteryLevel);
                        }
                    }
                }
            }
        }
    };
@@ -1016,4 +1045,19 @@ public class HeadsetClientService extends ProfileService {
    protected AudioManager getAudioManager() {
        return mAudioManager;
    }

    protected void updateBatteryLevel() {
        int batteryLevel = mBatteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
        int batteryIndicatorID = 2;

        synchronized (this) {
            for (HeadsetClientStateMachine sm : mStateMachineMap.values()) {
                if (sm != null) {
                    sm.sendMessage(HeadsetClientStateMachine.SEND_BIEV,
                            batteryIndicatorID,
                            batteryLevel);
                }
            }
        }
    }
}
+19 −0
Original line number Diff line number Diff line
@@ -105,6 +105,7 @@ public class HeadsetClientStateMachine extends StateMachine {
    public static final int EXPLICIT_CALL_TRANSFER = 18;
    public static final int DISABLE_NREC = 20;
    public static final int SEND_VENDOR_AT_COMMAND = 21;
    public static final int SEND_BIEV = 22;

    // internal actions
    private static final int QUERY_CURRENT_CALLS = 50;
@@ -291,6 +292,8 @@ public class HeadsetClientStateMachine extends StateMachine {
                return "DISABLE_NREC";
            case SEND_VENDOR_AT_COMMAND:
                return "SEND_VENDOR_AT_COMMAND";
            case SEND_BIEV:
                return "SEND_BIEV";
            case QUERY_CURRENT_CALLS:
                return "QUERY_CURRENT_CALLS";
            case QUERY_OPERATOR_NAME:
@@ -1151,6 +1154,7 @@ public class HeadsetClientStateMachine extends StateMachine {
            logD("Enter Connected: " + getCurrentMessage().what);
            mAudioWbs = false;
            mCommandedSpeakerVolume = -1;

            if (mPrevState == mConnecting) {
                broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
                        BluetoothProfile.STATE_CONNECTING);
@@ -1161,6 +1165,7 @@ public class HeadsetClientStateMachine extends StateMachine {
                Log.e(TAG, "Connected: Illegal state transition from " + prevStateName
                        + " to Connecting, mCurrentDevice=" + mCurrentDevice);
            }
            mService.updateBatteryLevel();
        }

        @Override
@@ -1239,6 +1244,20 @@ public class HeadsetClientStateMachine extends StateMachine {
                    break;
                }

                case SEND_BIEV: {
                    if ((mPeerFeatures & HeadsetClientHalConstants.PEER_FEAT_HF_IND)
                            == HeadsetClientHalConstants.PEER_FEAT_HF_IND) {
                        int indicatorID = message.arg1;
                        int value = message.arg2;
                        mNativeInterface.sendATCmd(getByteAddress(mCurrentDevice),
                                HeadsetClientHalConstants.HANDSFREECLIENT_AT_CMD_BIEV,
                                indicatorID,
                                value,
                                null);
                    }
                    break;
                }

                // Called only for Mute/Un-mute - Mic volume change is not allowed.
                case SET_MIC_VOLUME:
                    break;
+47 −0
Original line number Diff line number Diff line
@@ -16,10 +16,17 @@

package com.android.bluetooth.hfpclient;

import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.Intent;
import android.os.BatteryManager;

import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
@@ -48,9 +55,12 @@ public class HeadsetClientServiceTest {
    private BluetoothAdapter mAdapter = null;
    private Context mTargetContext;

    private static final int STANDARD_WAIT_MILLIS = 1000;

    @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();

    @Mock private AdapterService mAdapterService;
    @Mock private HeadsetClientStateMachine mStateMachine;

    @Mock private DatabaseManager mDatabaseManager;

@@ -86,4 +96,41 @@ public class HeadsetClientServiceTest {
    public void testInitialize() {
        Assert.assertNotNull(HeadsetClientService.getHeadsetClientService());
    }

    @Test
    public void testSendBIEVtoStateMachineWhenBatteryChanged() {
        // Put mock state machine
        BluetoothDevice device =
                BluetoothAdapter.getDefaultAdapter().getRemoteDevice("00:01:02:03:04:05");
        mService.getStateMachineMap().put(device, mStateMachine);

        // Send battery changed intent
        Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
        intent.putExtra(BatteryManager.EXTRA_LEVEL, 50);
        mService.sendBroadcast(intent);

        // Expect send BIEV to state machine
        verify(mStateMachine, timeout(STANDARD_WAIT_MILLIS).times(1))
                .sendMessage(
                    eq(HeadsetClientStateMachine.SEND_BIEV),
                    eq(2),
                    anyInt());
    }

    @Test
    public void testUpdateBatteryLevel() {
        // Put mock state machine
        BluetoothDevice device =
                BluetoothAdapter.getDefaultAdapter().getRemoteDevice("00:01:02:03:04:05");
        mService.getStateMachineMap().put(device, mStateMachine);

        mService.updateBatteryLevel();

        // Expect send BIEV to state machine
        verify(mStateMachine, timeout(STANDARD_WAIT_MILLIS).times(1))
                .sendMessage(
                    eq(HeadsetClientStateMachine.SEND_BIEV),
                    eq(2),
                    anyInt());
    }
}
+51 −0
Original line number Diff line number Diff line
@@ -368,6 +368,7 @@ public class HeadsetClientStateMachineTest {
        StackEvent slcEvent = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
        slcEvent.valueInt = HeadsetClientHalConstants.CONNECTION_STATE_SLC_CONNECTED;
        slcEvent.valueInt2 = HeadsetClientHalConstants.PEER_FEAT_ECS;
        slcEvent.valueInt2 |= HeadsetClientHalConstants.PEER_FEAT_HF_IND;
        slcEvent.device = mTestDevice;
        mHeadsetClientStateMachine.sendMessage(StackEvent.STACK_EVENT, slcEvent);
        ArgumentCaptor<Intent> intentArgument = ArgumentCaptor.forClass(Intent.class);
@@ -622,4 +623,54 @@ public class HeadsetClientStateMachineTest {
        Assert.assertEquals(expectedState, state);
        return expectedBroadcastIndex + 1;
    }

    /**
     * Test send BIEV command
     */
    @MediumTest
    @Test
    public void testSendBIEVCommand() {
        // Setup connection state machine to be in connected state
        when(mHeadsetClientService.getConnectionPolicy(any(BluetoothDevice.class))).thenReturn(
                BluetoothProfile.CONNECTION_POLICY_ALLOWED);
        int expectedBroadcastIndex = 1;
        expectedBroadcastIndex = setUpHfpClientConnection(expectedBroadcastIndex);
        expectedBroadcastIndex = setUpServiceLevelConnection(expectedBroadcastIndex);

        int indicator_id = 2;
        int indicator_value = 50;

        Message msg = mHeadsetClientStateMachine.obtainMessage(HeadsetClientStateMachine.SEND_BIEV);
        msg.arg1 = indicator_id;
        msg.arg2 = indicator_value;

        mHeadsetClientStateMachine.sendMessage(msg);

        verify(mNativeInterface, timeout(STANDARD_WAIT_MILLIS).times(1))
                .sendATCmd(
                        Utils.getBytesFromAddress(mTestDevice.getAddress()),
                        HeadsetClientHalConstants.HANDSFREECLIENT_AT_CMD_BIEV,
                        indicator_id,
                        indicator_value,
                        null);
    }

    /**
     * Test state machine shall try to send AT+BIEV command to AG
     * to update an init battery level.
     */
    @MediumTest
    @Test
    public void testSendBatteryUpdateIndicatorWhenConnect() {
        // Setup connection state machine to be in connected state
        when(mHeadsetClientService.getConnectionPolicy(any(BluetoothDevice.class))).thenReturn(
                BluetoothProfile.CONNECTION_POLICY_ALLOWED);
        int expectedBroadcastIndex = 1;

        expectedBroadcastIndex = setUpHfpClientConnection(expectedBroadcastIndex);
        expectedBroadcastIndex = setUpServiceLevelConnection(expectedBroadcastIndex);

        verify(mHeadsetClientService, timeout(STANDARD_WAIT_MILLIS).times(1))
                .updateBatteryLevel();
    }
}