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

Commit 8ba47f67 authored by Android Build Coastguard Worker's avatar Android Build Coastguard Worker
Browse files

Snap for 12072601 from fd36b1ae to 24Q4-release

Change-Id: I1df2760bdc4f41ef9378660b02a1993c08576124
parents 6dc17609 fd36b1ae
Loading
Loading
Loading
Loading
+17 −5
Original line number Diff line number Diff line
@@ -1076,16 +1076,19 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac
        BluetoothDevice a2dpFallbackDevice = null;
        if (a2dpService != null) {
            a2dpFallbackDevice = a2dpService.getFallbackDevice();
            Log.d(TAG, "a2dpFallbackDevice: " + a2dpFallbackDevice);
        }

        HeadsetService headsetService = mFactory.getHeadsetService();
        BluetoothDevice headsetFallbackDevice = null;
        if (headsetService != null) {
            headsetFallbackDevice = headsetService.getFallbackDevice();
            Log.d(TAG, "headsetFallbackDevice: " + headsetFallbackDevice);
        }

        List<BluetoothDevice> connectedDevices = new ArrayList<>();
        connectedDevices.addAll(mLeAudioConnectedDevices);
        Log.d(TAG, "Audio mode: " + mAudioManager.getMode());
        switch (mAudioManager.getMode()) {
            case AudioManager.MODE_NORMAL:
                if (a2dpFallbackDevice != null) {
@@ -1107,15 +1110,20 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac
            Log.d(TAG, "No fallback devices are found");
            return false;
        }
        Log.d(TAG, "Most recently connected device: " + device);
        if (mAudioManager.getMode() == AudioManager.MODE_NORMAL) {
            if (Objects.equals(a2dpFallbackDevice, device)) {
                Log.d(TAG, "Found an A2DP fallback device: " + device);
                setA2dpActiveDevice(device);
                if (Flags.alwaysFallbackToAvailableDevice()) {
                    setHfpActiveDevice(headsetFallbackDevice);
                } else {
                    if (Objects.equals(headsetFallbackDevice, device)) {
                        setHfpActiveDevice(device);
                    } else {
                        setHfpActiveDevice(null);
                    }
                }
                /* If dual mode is enabled, LEA will be made active once all supported
                classic audio profiles are made active for the device. */
                if (!Utils.isDualModeAudioEnabled()) {
@@ -1138,11 +1146,15 @@ public class ActiveDeviceManager implements AdapterService.BluetoothStateCallbac
            if (Objects.equals(headsetFallbackDevice, device)) {
                Log.d(TAG, "Found a HFP fallback device: " + device);
                setHfpActiveDevice(device);
                if (Flags.alwaysFallbackToAvailableDevice()) {
                    setA2dpActiveDevice(a2dpFallbackDevice);
                } else {
                    if (Objects.equals(a2dpFallbackDevice, device)) {
                        setA2dpActiveDevice(a2dpFallbackDevice);
                    } else {
                        setA2dpActiveDevice(null, true);
                    }
                }
                if (!Utils.isDualModeAudioEnabled()) {
                    setLeAudioActiveDevice(null, true);
                }
+125 −14
Original line number Diff line number Diff line
@@ -3019,7 +3019,11 @@ public class LeAudioService extends ProfileService {
        mDialingOutTimeoutEvent = null;
    }

    void notifyAudioFrameworkForCodecConfigUpdate(int groupId, LeAudioGroupDescriptor descriptor) {
    void notifyAudioFrameworkForCodecConfigUpdate(
            int groupId,
            LeAudioGroupDescriptor descriptor,
            boolean outputCodecOrFreqChanged,
            boolean inputCodecOrFreqChanged) {
        Log.i(TAG, "notifyAudioFrameworkForCodecConfigUpdate groupId: " + groupId);

        if (!Flags.leaudioCodecConfigCallbackOrderFix()) {
@@ -3027,7 +3031,7 @@ public class LeAudioService extends ProfileService {
            return;
        }

        if (mActiveAudioOutDevice != null) {
        if (mActiveAudioOutDevice != null && outputCodecOrFreqChanged) {
            int volume = getAudioDeviceGroupVolume(groupId);

            final BluetoothProfileConnectionInfo connectionInfo;
@@ -3042,14 +3046,115 @@ public class LeAudioService extends ProfileService {
                    mActiveAudioOutDevice, mActiveAudioOutDevice, connectionInfo);
        }

        if (mActiveAudioInDevice != null) {
        if (mActiveAudioInDevice != null && inputCodecOrFreqChanged) {
            mAudioManager.handleBluetoothActiveDeviceChanged(
                    mActiveAudioOutDevice,
                    mActiveAudioOutDevice,
                    mActiveAudioInDevice,
                    mActiveAudioInDevice,
                    BluetoothProfileConnectionInfo.createLeAudioInfo(false, false));
        }
    }

    boolean isOutputCodecOfSampleFrequencyChanged(
            BluetoothLeAudioCodecStatus previous, BluetoothLeAudioCodecStatus next) {
        if ((previous == null) && (next == null)) {
            return false;
        }

        if ((previous == null) || (next == null)) {
            Log.d(TAG, previous + " != " + next);
            return true;
        }

        if ((previous.getOutputCodecConfig() == null) && (next.getOutputCodecConfig() == null)) {
            /* Nothing changed here.*/
            return false;
        }

        if ((previous.getOutputCodecConfig() == null || next.getOutputCodecConfig() == null)) {
            Log.d(
                    TAG,
                    "New output codec: "
                            + (previous.getOutputCodecConfig()
                                    + " != "
                                    + next.getOutputCodecConfig()));
            return true;
        }

        if (previous.getOutputCodecConfig().getCodecType()
                != next.getOutputCodecConfig().getCodecType()) {
            Log.d(
                    TAG,
                    "Different output codec type: "
                            + (previous.getOutputCodecConfig().getCodecType()
                                    + " != "
                                    + next.getOutputCodecConfig().getCodecType()));
            return true;
        }
        if (previous.getOutputCodecConfig().getSampleRate()
                != next.getOutputCodecConfig().getSampleRate()) {
            Log.d(
                    TAG,
                    "Different output samplerate: "
                            + (previous.getOutputCodecConfig().getSampleRate()
                                    + " != "
                                    + next.getOutputCodecConfig().getSampleRate()));
            return true;
        }

        return false;
    }

    boolean isInputCodecOfSampleFrequencyChanged(
            BluetoothLeAudioCodecStatus previous, BluetoothLeAudioCodecStatus next) {
        if ((previous == null) && (next == null)) {
            return false;
        }

        if ((previous == null) || (next == null)) {
            Log.d(TAG, previous + " != " + next);
            return true;
        }

        if ((previous.getInputCodecConfig() == null) && (next.getInputCodecConfig() == null)) {
            /* Nothing changed here.*/
            return false;
        }

        if ((previous.getInputCodecConfig() == null) || (next.getInputCodecConfig() == null)) {
            Log.d(
                    TAG,
                    "New input codec: "
                            + (previous.getInputCodecConfig()
                                    + " != "
                                    + next.getInputCodecConfig()));
            return true;
        }

        if (previous.getInputCodecConfig().getCodecType()
                != next.getInputCodecConfig().getCodecType()) {
            Log.d(
                    TAG,
                    "Different input codec type: "
                            + (previous.getInputCodecConfig().getCodecType()
                                    + " != "
                                    + next.getInputCodecConfig().getCodecType()));
            return true;
        }

        if (previous.getInputCodecConfig().getSampleRate()
                != next.getInputCodecConfig().getSampleRate()) {
            Log.d(
                    TAG,
                    "Different input samplerate: "
                            + (previous.getInputCodecConfig().getSampleRate()
                                    + " != "
                                    + next.getInputCodecConfig().getSampleRate()));
            return true;
        }

        return false;
    }

    // Suppressed since this is part of a local process
    @SuppressLint("AndroidFrameworkRequiresPermission")
    void messageFromNative(LeAudioStackEvent stackEvent) {
@@ -3199,18 +3304,24 @@ public class LeAudioService extends ProfileService {
                            descriptor.mInputSelectableConfig,
                            descriptor.mOutputSelectableConfig);

            if (descriptor.mCodecStatus != null) {
                Log.d(TAG, " Replacing codec status for group: " + groupId);
            } else {
                Log.d(TAG, " New codec status for group: " + groupId);
            }
            boolean outputCodecOrFreqChanged =
                    isOutputCodecOfSampleFrequencyChanged(descriptor.mCodecStatus, status);
            boolean inputCodecOrFreqChanged =
                    isInputCodecOfSampleFrequencyChanged(descriptor.mCodecStatus, status);

            Log.d(
                    TAG,
                    ("Codec update for group:" + groupId)
                            + (", outputCodecOrFreqChanged: " + outputCodecOrFreqChanged)
                            + (", inputCodecOrFreqChanged: " + inputCodecOrFreqChanged));

            descriptor.mCodecStatus = status;
            mHandler.post(() -> notifyUnicastCodecConfigChanged(groupId, status));

            if (descriptor.isActive()) {
            if (descriptor.isActive() && (outputCodecOrFreqChanged || inputCodecOrFreqChanged)) {
                // Audio framework needs to be notified so it get new codec config
                notifyAudioFrameworkForCodecConfigUpdate(groupId, descriptor);
                notifyAudioFrameworkForCodecConfigUpdate(
                        groupId, descriptor, outputCodecOrFreqChanged, inputCodecOrFreqChanged);
            }
        } else if (stackEvent.type == LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED) {
            int direction = stackEvent.valueInt1;
+72 −0
Original line number Diff line number Diff line
@@ -509,6 +509,78 @@ public class ActiveDeviceManagerTest {
        Mockito.clearInvocations(mA2dpService);
    }

    @Test
    @EnableFlags(Flags.FLAG_ALWAYS_FALLBACK_TO_AVAILABLE_DEVICE)
    public void a2dpHeadsetActivated_checkFallbackMeachanismOneA2dpOneHeadset() {
        // Active call
        when(mAudioManager.getMode()).thenReturn(AudioManager.MODE_IN_CALL);

        // Connect 1st device
        a2dpConnected(mA2dpHeadsetDevice, true);
        headsetConnected(mA2dpHeadsetDevice, true);

        a2dpActiveDeviceChanged(mA2dpHeadsetDevice);
        headsetActiveDeviceChanged(mA2dpHeadsetDevice);

        verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice);
        verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice);

        TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper());
        assertThat(mActiveDeviceManager.getA2dpActiveDevice()).isEqualTo(mA2dpHeadsetDevice);
        assertThat(mActiveDeviceManager.getHfpActiveDevice()).isEqualTo(mA2dpHeadsetDevice);

        // Disable audio for the first device
        when(mA2dpService.getFallbackDevice()).thenReturn(null);
        when(mA2dpService.removeActiveDevice(anyBoolean())).thenReturn(true);
        when(mHeadsetService.getFallbackDevice()).thenReturn(mA2dpHeadsetDevice);

        mDatabaseManager.setProfileConnectionPolicy(
                mA2dpHeadsetDevice,
                BluetoothProfile.A2DP,
                BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
        a2dpDisconnected(mA2dpHeadsetDevice);

        verify(mHeadsetService, timeout(TIMEOUT_MS).times(2)).setActiveDevice(mA2dpHeadsetDevice);
        verify(mA2dpService, timeout(TIMEOUT_MS)).removeActiveDevice(anyBoolean());

        a2dpActiveDeviceChanged(null);

        TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper());
        assertThat(mActiveDeviceManager.getA2dpActiveDevice()).isEqualTo(null);
        assertThat(mActiveDeviceManager.getHfpActiveDevice()).isEqualTo(mA2dpHeadsetDevice);

        // Connect 2nd device
        a2dpConnected(mSecondaryAudioDevice, true);
        headsetConnected(mSecondaryAudioDevice, true);

        verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mSecondaryAudioDevice);
        verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mSecondaryAudioDevice);

        a2dpActiveDeviceChanged(mSecondaryAudioDevice);
        headsetActiveDeviceChanged(mSecondaryAudioDevice);

        TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper());
        assertThat(mActiveDeviceManager.getA2dpActiveDevice()).isEqualTo(mSecondaryAudioDevice);
        assertThat(mActiveDeviceManager.getHfpActiveDevice()).isEqualTo(mSecondaryAudioDevice);

        // Disable phone calls for the second device
        when(mA2dpService.getFallbackDevice()).thenReturn(mSecondaryAudioDevice);
        when(mHeadsetService.getFallbackDevice()).thenReturn(mA2dpHeadsetDevice);

        mDatabaseManager.setProfileConnectionPolicy(
                mSecondaryAudioDevice,
                BluetoothProfile.HEADSET,
                BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
        headsetDisconnected(mSecondaryAudioDevice);

        verify(mHeadsetService, timeout(TIMEOUT_MS).times(3)).setActiveDevice(mA2dpHeadsetDevice);
        verify(mA2dpService, timeout(TIMEOUT_MS).times(2)).setActiveDevice(mSecondaryAudioDevice);

        TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper());
        assertThat(mActiveDeviceManager.getA2dpActiveDevice()).isEqualTo(mSecondaryAudioDevice);
        assertThat(mActiveDeviceManager.getHfpActiveDevice()).isEqualTo(mA2dpHeadsetDevice);
    }

    @Test
    public void hfpActivated_whileActivatingA2dpHeadset() {
        headsetConnected(mHeadsetDevice, false);
+3 −0
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ import android.content.AttributionSource;
import android.content.Context;
import android.content.res.Resources;
import android.location.LocationManager;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.flag.junit.SetFlagsRule;

import androidx.test.InstrumentationRegistry;
@@ -640,6 +641,7 @@ public class GattServiceTest {
    }

    @Test
    @DisableFlags(Flags.FLAG_SCAN_MANAGER_REFACTOR)
    public void numHwTrackFiltersAvailable() {
        mService.getTransitionalScanHelper().numHwTrackFiltersAvailable(mAttributionSource);
        verify(mScanManager).getCurrentUsedTrackingAdvertisement();
@@ -680,6 +682,7 @@ public class GattServiceTest {
    }

    @Test
    @DisableFlags(Flags.FLAG_SCAN_MANAGER_REFACTOR)
    public void profileConnectionStateChanged_notifyScanManager() {
        mService.notifyProfileConnectionStateChange(
                BluetoothProfile.A2DP,
+123 −2
Original line number Diff line number Diff line
@@ -52,8 +52,8 @@ import android.media.BluetoothProfileConnectionInfo;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelUuid;
import android.sysprop.BluetoothProperties;
import android.platform.test.flag.junit.SetFlagsRule;
import android.sysprop.BluetoothProperties;

import androidx.test.filters.MediumTest;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -2171,7 +2171,8 @@ public class LeAudioServiceTest {

    /** Test native interface group status message handling */
    @Test
    public void testMessageFromNativeGroupCodecConfigChanged() {
    public void testMessageFromNativeGroupCodecConfigChangedNonActiveDevice() {
        mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_CODEC_CONFIG_CALLBACK_ORDER_FIX);
        onGroupCodecConfChangedCallbackCalled = false;

        injectLocalCodecConfigCapaChanged(INPUT_CAPABILITIES_CONFIG, OUTPUT_CAPABILITIES_CONFIG);
@@ -2213,15 +2214,135 @@ public class LeAudioServiceTest {

        injectGroupSelectableCodecConfigChanged(
                testGroupId, INPUT_SELECTABLE_CONFIG, OUTPUT_SELECTABLE_CONFIG);
        // Inject configuration and check that AF is NOT notified.
        injectGroupCurrentCodecConfigChanged(testGroupId, LC3_16KHZ_CONFIG, LC3_48KHZ_CONFIG);

        TestUtils.waitForLooperToFinishScheduledTask(mService.getMainLooper());
        assertThat(onGroupCodecConfChangedCallbackCalled).isTrue();

        verify(mAudioManager, times(0))
                .handleBluetoothActiveDeviceChanged(
                        any(), any(), any(BluetoothProfileConnectionInfo.class));

        onGroupCodecConfChangedCallbackCalled = false;

        // Now inject again new configuration and check that AF is not notified.
        testCodecStatus =
                new BluetoothLeAudioCodecStatus(
                        LC3_16KHZ_CONFIG,
                        LC3_16KHZ_CONFIG,
                        INPUT_CAPABILITIES_CONFIG,
                        OUTPUT_CAPABILITIES_CONFIG,
                        INPUT_SELECTABLE_CONFIG,
                        OUTPUT_SELECTABLE_CONFIG);

        injectGroupCurrentCodecConfigChanged(testGroupId, LC3_16KHZ_CONFIG, LC3_16KHZ_CONFIG);

        TestUtils.waitForLooperToFinishScheduledTask(mService.getMainLooper());
        assertThat(onGroupCodecConfChangedCallbackCalled).isTrue();

        verify(mAudioManager, times(0))
                .handleBluetoothActiveDeviceChanged(
                        any(), any(), any(BluetoothProfileConnectionInfo.class));

        onGroupCodecConfChangedCallbackCalled = false;
        mService.mLeAudioCallbacks.unregister(leAudioCallbacks);
    }

    /** Test native interface group status message handling */
    @Test
    public void testMessageFromNativeGroupCodecConfigChangedActiveDevice_DifferentConfiguration() {
        mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_CODEC_CONFIG_CALLBACK_ORDER_FIX);
        onGroupCodecConfChangedCallbackCalled = false;

        injectLocalCodecConfigCapaChanged(INPUT_CAPABILITIES_CONFIG, OUTPUT_CAPABILITIES_CONFIG);

        doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
        connectTestDevice(mSingleDevice, testGroupId);

        testCodecStatus =
                new BluetoothLeAudioCodecStatus(
                        LC3_16KHZ_CONFIG,
                        LC3_48KHZ_CONFIG,
                        INPUT_CAPABILITIES_CONFIG,
                        OUTPUT_CAPABILITIES_CONFIG,
                        INPUT_SELECTABLE_CONFIG,
                        OUTPUT_SELECTABLE_CONFIG);

        IBluetoothLeAudioCallback leAudioCallbacks =
                new IBluetoothLeAudioCallback.Stub() {
                    @Override
                    public void onCodecConfigChanged(int gid, BluetoothLeAudioCodecStatus status) {
                        onGroupCodecConfChangedCallbackCalled = true;
                        assertThat(status.equals(testCodecStatus)).isTrue();
                    }

                    @Override
                    public void onGroupStatusChanged(int gid, int gStatus) {}

                    @Override
                    public void onGroupNodeAdded(BluetoothDevice device, int gid) {}

                    @Override
                    public void onGroupNodeRemoved(BluetoothDevice device, int gid) {}

                    @Override
                    public void onGroupStreamStatusChanged(int groupId, int groupStreamStatus) {}
                };

        synchronized (this.mService.mLeAudioCallbacks) {
            mService.mLeAudioCallbacks.register(leAudioCallbacks);
        }

        injectGroupSelectableCodecConfigChanged(
                testGroupId, INPUT_SELECTABLE_CONFIG, OUTPUT_SELECTABLE_CONFIG);

        injectGroupCurrentCodecConfigChanged(testGroupId, LC3_16KHZ_CONFIG, LC3_48KHZ_CONFIG);

        injectAudioConfChanged(
                testGroupId,
                BluetoothLeAudio.CONTEXT_TYPE_MEDIA | BluetoothLeAudio.CONTEXT_TYPE_CONVERSATIONAL,
                3);

        injectGroupStatusChange(testGroupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE);

        TestUtils.waitForLooperToFinishScheduledTask(mService.getMainLooper());
        assertThat(onGroupCodecConfChangedCallbackCalled).isTrue();

        verify(mAudioManager, times(2))
                .handleBluetoothActiveDeviceChanged(
                        any(), any(), any(BluetoothProfileConnectionInfo.class));

        onGroupCodecConfChangedCallbackCalled = false;
        reset(mAudioManager);

        // Now inject configuration different sample rate on one direction
        testCodecStatus =
                new BluetoothLeAudioCodecStatus(
                        LC3_16KHZ_CONFIG,
                        LC3_16KHZ_CONFIG,
                        INPUT_CAPABILITIES_CONFIG,
                        OUTPUT_CAPABILITIES_CONFIG,
                        INPUT_SELECTABLE_CONFIG,
                        OUTPUT_SELECTABLE_CONFIG);

        injectGroupCurrentCodecConfigChanged(testGroupId, LC3_16KHZ_CONFIG, LC3_16KHZ_CONFIG);

        TestUtils.waitForLooperToFinishScheduledTask(mService.getMainLooper());
        assertThat(onGroupCodecConfChangedCallbackCalled).isTrue();

        verify(mAudioManager, times(1))
                .handleBluetoothActiveDeviceChanged(
                        any(), any(), any(BluetoothProfileConnectionInfo.class));

        synchronized (this.mService.mLeAudioCallbacks) {
            mService.mLeAudioCallbacks.unregister(leAudioCallbacks);
        }

        onGroupCodecConfChangedCallbackCalled = false;
        reset(mAudioManager);
    }

    /** Test native interface group status message handling */
    @Test
    public void testMessageFromNativeGroupCodecConfigChanged_OneDirectionOnly() {
Loading