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

Commit 4b6617f3 authored by Ugo Yu's avatar Ugo Yu
Browse files

Fix NPE in setActiveDevice if HFP audio is not allowed

Fix a null pointer expection that if a connected device is set to active
but audio is not allowed. If the previous active device is null, the
HeadsetService tries to send the null device to the native interface and
causes crash.

Tag: #stability
Bug: 201357034
Test: atest HeadsetServiceTest
Change-Id: I49f0b1cb690a7903a5b04813df4943382914866e
parent b4d17e1c
Loading
Loading
Loading
Loading
+41 −24
Original line number Diff line number Diff line
@@ -1136,36 +1136,45 @@ public class HeadsetService extends ProfileService {
    }

    /**
     * Set the active device.
     *
     * @param device the active device
     * @return true on success, otherwise false
     * Remove the active device
     */
    public boolean setActiveDevice(BluetoothDevice device) {
        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
        Log.i(TAG, "setActiveDevice: device=" + device + ", " + Utils.getUidPidString());
    private void removeActiveDevice() {
        synchronized (mStateMachines) {
            if (device == null) {
            // Clear the active device
            if (mVoiceRecognitionStarted) {
                if (!stopVoiceRecognition(mActiveDevice)) {
                        Log.w(TAG, "setActiveDevice: fail to stopVoiceRecognition from "
                    Log.w(TAG, "removeActiveDevice: fail to stopVoiceRecognition from "
                            + mActiveDevice);
                }
            }
            if (mVirtualCallStarted) {
                if (!stopScoUsingVirtualVoiceCall()) {
                        Log.w(TAG, "setActiveDevice: fail to stopScoUsingVirtualVoiceCall from "
                    Log.w(TAG, "removeActiveDevice: fail to stopScoUsingVirtualVoiceCall from "
                            + mActiveDevice);
                }
            }
            if (getAudioState(mActiveDevice) != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
                if (!disconnectAudio(mActiveDevice)) {
                        Log.w(TAG, "setActiveDevice: disconnectAudio failed on " + mActiveDevice);
                    Log.w(TAG, "removeActiveDevice: disconnectAudio failed on " + mActiveDevice);
                }
            }
            mActiveDevice = null;
            broadcastActiveDevice(null);
        }
    }

    /**
     * Set the active device.
     *
     * @param device the active device
     * @return true on success, otherwise false
     */
    public boolean setActiveDevice(BluetoothDevice device) {
        enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission");
        Log.i(TAG, "setActiveDevice: device=" + device + ", " + Utils.getUidPidString());
        synchronized (mStateMachines) {
            if (device == null) {
                removeActiveDevice();
                return true;
            }
            if (device.equals(mActiveDevice)) {
@@ -1187,8 +1196,12 @@ public class HeadsetService extends ProfileService {
                if (!disconnectAudio(previousActiveDevice)) {
                    Log.e(TAG, "setActiveDevice: fail to disconnectAudio from "
                            + previousActiveDevice);
                    if (previousActiveDevice == null) {
                        removeActiveDevice();
                    } else {
                        mActiveDevice = previousActiveDevice;
                        mNativeInterface.setActiveDevice(previousActiveDevice);
                    }
                    return false;
                }
                broadcastActiveDevice(mActiveDevice);
@@ -1196,8 +1209,12 @@ public class HeadsetService extends ProfileService {
                broadcastActiveDevice(mActiveDevice);
                if (!connectAudio(mActiveDevice)) {
                    Log.e(TAG, "setActiveDevice: fail to connectAudio to " + mActiveDevice);
                    if (previousActiveDevice == null) {
                        removeActiveDevice();
                    } else {
                        mActiveDevice = previousActiveDevice;
                        mNativeInterface.setActiveDevice(previousActiveDevice);
                    }
                    return false;
                }
            } else {
+25 −0
Original line number Diff line number Diff line
@@ -900,6 +900,31 @@ public class HeadsetServiceTest {
        Assert.assertEquals(mCurrentDevice, mHeadsetService.getActiveDevice());
    }

    /**
     * Test that whether active device been removed after enable silence mode
     */
    @Test
    public void testSetActiveDevice_AudioNotAllowed() {
        when(mDatabaseManager.getProfileConnectionPolicy(any(BluetoothDevice.class),
                eq(BluetoothProfile.HEADSET)))
                .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
        mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0);
        mHeadsetService.setForceScoAudio(false);

        Assert.assertTrue(mHeadsetService.connect(mCurrentDevice));
        when(mStateMachines.get(mCurrentDevice).getDevice()).thenReturn(mCurrentDevice);
        when(mStateMachines.get(mCurrentDevice).getConnectionState()).thenReturn(
                BluetoothProfile.STATE_CONNECTED);

        Assert.assertTrue(mHeadsetService.setActiveDevice(null));
        when(mSystemInterface.isInCall()).thenReturn(true);
        mHeadsetService.setAudioRouteAllowed(false);

        // Test that active device should not be changed if audio is not allowed
        Assert.assertFalse(mHeadsetService.setActiveDevice(mCurrentDevice));
        Assert.assertEquals(null, mHeadsetService.getActiveDevice());
    }

    /*
     *  Helper function to test okToAcceptConnection() method
     *