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

Commit 3ee3167b authored by Łukasz Rymanowski's avatar Łukasz Rymanowski Committed by Jakub Pawłowski
Browse files

LeAudioService: Set inband ringtone flag only for active devices

If inband ringtone is supported by the phone, it should be exposed over
GTBS only for device which is active.

This patch assures that, all the devices from the active group have flag
set, and all devices from not Active groups have this flag cleared

Bug: 242685105
Bug: 260660659
Test: atest BluetoothInstrumentationTests
Test: manual tests
Tag: #feature
Change-Id: I6f55f93aeb223ae4d74d4d5dc7058a60da0804e4
parent 4c924332
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import com.android.bluetooth.hid.HidHostService;
import com.android.bluetooth.le_audio.LeAudioService;
import com.android.bluetooth.mcp.McpService;
import com.android.bluetooth.pan.PanService;
import com.android.bluetooth.tbs.TbsService;
import com.android.bluetooth.vc.VolumeControlService;

// Factory class to create instances of static services. Useful in mocking the service objects.
@@ -73,6 +74,10 @@ public class ServiceFactory {
        return McpService.getMcpService();
    }

    public TbsService getTbsService() {
        return TbsService.getTbsService();
    }

    public VolumeControlService getVolumeControlService() {
        return VolumeControlService.getVolumeControlService();
    }
+168 −33
Original line number Diff line number Diff line
@@ -67,6 +67,7 @@ import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.bluetooth.hfp.HeadsetService;
import com.android.bluetooth.mcp.McpService;
import com.android.bluetooth.tbs.TbsGatt;
import com.android.bluetooth.tbs.TbsService;
import com.android.bluetooth.vc.VolumeControlService;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -129,6 +130,9 @@ public class LeAudioService extends ProfileService {
    AudioManager mAudioManager;
    LeAudioTmapGattServer mTmapGattServer;

    @VisibleForTesting
    TbsService mTbsService;

    @VisibleForTesting
    McpService mMcpService;

@@ -142,12 +146,13 @@ public class LeAudioService extends ProfileService {
    RemoteCallbackList<IBluetoothLeAudioCallback> mLeAudioCallbacks;

    private class LeAudioGroupDescriptor {
        LeAudioGroupDescriptor() {
        LeAudioGroupDescriptor(boolean isInbandRingtonEnabled) {
            mIsConnected = false;
            mIsActive = false;
            mDirection = AUDIO_DIRECTION_NONE;
            mCodecStatus = null;
            mLostLeadDeviceWhileStreaming = null;
            mInbandRingtoneEnabled = isInbandRingtonEnabled;
        }

        public Boolean mIsConnected;
@@ -156,20 +161,23 @@ public class LeAudioService extends ProfileService {
        public BluetoothLeAudioCodecStatus mCodecStatus;
        /* This can be non empty only for the streaming time */
        BluetoothDevice mLostLeadDeviceWhileStreaming;
        Boolean mInbandRingtoneEnabled;
    }

    private static class LeAudioDeviceDescriptor {
        LeAudioDeviceDescriptor() {
        LeAudioDeviceDescriptor(boolean isInbandRingtonEnabled) {
            mStateMachine = null;
            mGroupId = LE_AUDIO_GROUP_ID_INVALID;
            mSinkAudioLocation = BluetoothLeAudio.AUDIO_LOCATION_INVALID;
            mDirection = AUDIO_DIRECTION_NONE;
            mDevInbandRingtoneEnabled = isInbandRingtonEnabled;
        }

        public LeAudioStateMachine mStateMachine;
        public Integer mGroupId;
        public Integer mSinkAudioLocation;
        public Integer mDirection;
        Boolean mDevInbandRingtoneEnabled;
    }

    List<BluetoothLeAudioCodecConfig> mInputLocalCodecCapabilities = new ArrayList<>();
@@ -411,6 +419,7 @@ public class LeAudioService extends ProfileService {
        mAdapterService = null;
        mAudioManager = null;
        mMcpService = null;
        mTbsService = null;
        mVolumeControlService = null;

        return true;
@@ -454,7 +463,8 @@ public class LeAudioService extends ProfileService {
        return mVolumeControlService.getAudioDeviceGroupVolume(groupId);
    }

    LeAudioDeviceDescriptor createDeviceDescriptor(BluetoothDevice device) {
    LeAudioDeviceDescriptor createDeviceDescriptor(BluetoothDevice device,
            boolean isInbandRingtoneEnabled) {
        LeAudioDeviceDescriptor descriptor = mDeviceDescriptors.get(device);
        if (descriptor == null) {

@@ -465,7 +475,7 @@ public class LeAudioService extends ProfileService {
                return null;
            }

            mDeviceDescriptors.put(device, new LeAudioDeviceDescriptor());
            mDeviceDescriptors.put(device, new LeAudioDeviceDescriptor(isInbandRingtoneEnabled));
            descriptor = mDeviceDescriptors.get(device);
            Log.d(TAG, "Created descriptor for device: " + device);
        } else {
@@ -491,7 +501,13 @@ public class LeAudioService extends ProfileService {
        }

        synchronized (mGroupLock) {
            if (createDeviceDescriptor(device) == null) {
            boolean isInbandRingtoneEnabled = false;
            int groupId = getGroupId(device);
            if (groupId != LE_AUDIO_GROUP_ID_INVALID) {
                isInbandRingtoneEnabled = getGroupDescriptor(groupId).mInbandRingtoneEnabled;
            }

            if (createDeviceDescriptor(device, isInbandRingtoneEnabled) == null) {
                return false;
            }

@@ -1420,6 +1436,7 @@ public class LeAudioService extends ProfileService {

            if (descriptor.mIsActive) {
                notifyGroupStatusChanged(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE);
                updateInbandRingtoneForTheGroup(groupId);
            }
        }
    }
@@ -1439,6 +1456,7 @@ public class LeAudioService extends ProfileService {
            if (DBG) Log.d(TAG, "Clear for group: " + groupId);
            clearLostDevicesWhileStreaming(descriptor);
            notifyGroupStatusChanged(groupId, LeAudioStackEvent.GROUP_STATUS_INACTIVE);
            updateInbandRingtoneForTheGroup(groupId);
        }
    }

@@ -1473,6 +1491,67 @@ public class LeAudioService extends ProfileService {
        mHfpHandoverDevice = null;
    }

    void updateInbandRingtoneForTheGroup(int groupId) {
        if (!mLeAudioInbandRingtoneSupportedByPlatform) {
            if (DBG) {
                Log.d(TAG, "Platform does not support inband ringtone");
            }
            return;
        }

        synchronized (mGroupLock) {
            LeAudioGroupDescriptor groupDescriptor = getGroupDescriptor(groupId);
            if (groupDescriptor == null) {
                Log.e(TAG, "group descriptor for " + groupId + " does not exist");
                return;
            }

            boolean isRingtoneEnabled = groupDescriptor.mIsActive;

            if (DBG) {
                Log.d(TAG, "updateInbandRingtoneForTheGroup old: "
                        + groupDescriptor.mInbandRingtoneEnabled + " new: " + isRingtoneEnabled);
            }

            /* If at least one device from the group removes the Ringtone from available
            * context types, the inband ringtone will be removed
            */
            groupDescriptor.mInbandRingtoneEnabled = isRingtoneEnabled;
            TbsService tbsService = getTbsService();
            if (tbsService == null) {
                Log.w(TAG, "updateInbandRingtoneForTheGroup, tbsService not available");
                return;
            }

            for (Map.Entry<BluetoothDevice, LeAudioDeviceDescriptor> entry :
                                                    mDeviceDescriptors.entrySet()) {
                if (entry.getValue().mGroupId == groupId) {
                    BluetoothDevice device = entry.getKey();
                    LeAudioDeviceDescriptor deviceDescriptor = entry.getValue();
                    Log.i(TAG, "updateInbandRingtoneForTheGroup, setting inband ringtone to: "
                                + groupDescriptor.mInbandRingtoneEnabled + " for " + device
                                + " " + deviceDescriptor.mDevInbandRingtoneEnabled);
                    if (groupDescriptor.mInbandRingtoneEnabled
                                    == deviceDescriptor.mDevInbandRingtoneEnabled) {
                        if (DBG) {
                            Log.d(TAG, "Device " + device + " has already set inband ringtone to "
                                            + groupDescriptor.mInbandRingtoneEnabled);
                        }
                        continue;
                    }

                    deviceDescriptor.mDevInbandRingtoneEnabled =
                            groupDescriptor.mInbandRingtoneEnabled;
                    if (deviceDescriptor.mDevInbandRingtoneEnabled) {
                        tbsService.setInbandRingtoneSupport(device);
                    } else {
                        tbsService.clearInbandRingtoneSupport(device);
                    }
                }
            }
        }
    }

    // Suppressed since this is part of a local process
    @SuppressLint("AndroidFrameworkRequiresPermission")
    void messageFromNative(LeAudioStackEvent stackEvent) {
@@ -2077,10 +2156,18 @@ public class LeAudioService extends ProfileService {
     * @return true if inband ringtone is enabled, false otherwise
     */
    public boolean isInbandRingtoneEnabled(int groupId) {
        /* TODO Take into account device available context type */
        if (!mLeAudioInbandRingtoneSupportedByPlatform) {
            return mLeAudioInbandRingtoneSupportedByPlatform;
        }

        LeAudioGroupDescriptor descriptor = getGroupDescriptor(groupId);
        if (descriptor == null) {
            return false;
        }

        return descriptor.mInbandRingtoneEnabled;
    }

    /**
     * Set In Call state
     * @param inCall True if device in call (any state), false otherwise.
@@ -2223,6 +2310,15 @@ public class LeAudioService extends ProfileService {
        }
    }

    TbsService getTbsService() {
        if (mTbsService != null) {
            return mTbsService;
        }

        mTbsService = mServiceFactory.getTbsService();
        return mTbsService;
    }

    McpService getMcpService() {
        if (mMcpService != null) {
            return mMcpService;
@@ -2289,9 +2385,20 @@ public class LeAudioService extends ProfileService {
                Log.d(TAG, "Device " + device + " added to group " + groupId);
            }

            LeAudioGroupDescriptor groupDescriptor = getGroupDescriptor(groupId);
            if (groupDescriptor == null) {
                mGroupDescriptors.put(groupId,
                        new LeAudioGroupDescriptor(false));
            }
            groupDescriptor = getGroupDescriptor(groupId);
            if (groupDescriptor == null) {
                Log.e(TAG, "Could not create group description");
                return;
            }
            LeAudioDeviceDescriptor deviceDescriptor = getDeviceDescriptor(device);
            if (deviceDescriptor == null) {
                deviceDescriptor = createDeviceDescriptor(device);
                deviceDescriptor = createDeviceDescriptor(device,
                        groupDescriptor.mInbandRingtoneEnabled);
                if (deviceDescriptor == null) {
                    Log.e(TAG, "handleGroupNodeAdded: Can't create descriptor for added from"
                            + " storage device: " + device);
@@ -2306,10 +2413,6 @@ public class LeAudioService extends ProfileService {
            }
            deviceDescriptor.mGroupId = groupId;

            LeAudioGroupDescriptor descriptor = mGroupDescriptors.get(groupId);
            if (descriptor == null) {
                mGroupDescriptors.put(groupId, new LeAudioGroupDescriptor());
            }
            notifyGroupNodeAdded(device, groupId);
        }

@@ -3320,27 +3423,59 @@ public class LeAudioService extends ProfileService {
        ProfileService.println(sb, "  mLeAudioIsInbandRingtoneSupported:"
                                + mLeAudioInbandRingtoneSupportedByPlatform);

        for (Map.Entry<BluetoothDevice, LeAudioDeviceDescriptor> entry
                : mDeviceDescriptors.entrySet()) {
            LeAudioDeviceDescriptor descriptor = entry.getValue();

            descriptor.mStateMachine.dump(sb);
            ProfileService.println(sb, "    mGroupId: " + descriptor.mGroupId);
            ProfileService.println(sb, "    mSinkAudioLocation: " + descriptor.mSinkAudioLocation);
            ProfileService.println(sb, "    mDirection: " + descriptor.mDirection);
        }

        for (Map.Entry<Integer, LeAudioGroupDescriptor> entry : mGroupDescriptors.entrySet()) {
            LeAudioGroupDescriptor descriptor = entry.getValue();
            Integer groupId = entry.getKey();
        int numberOfUngroupedDevs = 0;
        synchronized (mGroupLock) {
            for (Map.Entry<Integer, LeAudioGroupDescriptor> groupEntry
                                                : mGroupDescriptors.entrySet()) {
                LeAudioGroupDescriptor groupDescriptor = groupEntry.getValue();
                Integer groupId = groupEntry.getKey();
                ProfileService.println(sb, "Group: " + groupId);
            ProfileService.println(sb, "    isActive: " + descriptor.mIsActive);
            ProfileService.println(sb, "    isConnected: " + descriptor.mIsConnected);
            ProfileService.println(sb, "    mDirection: " + descriptor.mDirection);
                ProfileService.println(sb, "  isActive: " + groupDescriptor.mIsActive);
                ProfileService.println(sb, "  isConnected: " + groupDescriptor.mIsConnected);
                ProfileService.println(sb, "  mDirection: " + groupDescriptor.mDirection);
                ProfileService.println(sb, "  group lead: " + getConnectedGroupLeadDevice(groupId));
                ProfileService.println(sb, "  first device: " + getFirstDeviceFromGroup(groupId));
                ProfileService.println(sb, "  lost lead device: "
                    + descriptor.mLostLeadDeviceWhileStreaming);
                        + groupDescriptor.mLostLeadDeviceWhileStreaming);
                ProfileService.println(sb, "  mInbandRingtoneEnabled: "
                        + groupDescriptor.mInbandRingtoneEnabled);

                for (Map.Entry<BluetoothDevice, LeAudioDeviceDescriptor> deviceEntry
                        : mDeviceDescriptors.entrySet()) {
                    LeAudioDeviceDescriptor deviceDescriptor = deviceEntry.getValue();
                    if (deviceDescriptor.mGroupId != groupId) {
                        if (deviceDescriptor.mGroupId == LE_AUDIO_GROUP_ID_INVALID) {
                            numberOfUngroupedDevs++;
                        }
                        continue;
                    }

                    deviceDescriptor.mStateMachine.dump(sb);
                    ProfileService.println(sb, "    mDevInbandRingtoneEnabled: "
                            + deviceDescriptor.mDevInbandRingtoneEnabled);
                    ProfileService.println(sb, "    mSinkAudioLocation: "
                            + deviceDescriptor.mSinkAudioLocation);
                    ProfileService.println(sb, "    mDirection: " + deviceDescriptor.mDirection);
                }
            }
        }

        if (numberOfUngroupedDevs > 0) {
            ProfileService.println(sb, "UnGroup devices:");
            for (Map.Entry<BluetoothDevice, LeAudioDeviceDescriptor> entry
                    : mDeviceDescriptors.entrySet()) {
                LeAudioDeviceDescriptor deviceDescriptor = entry.getValue();
                if (deviceDescriptor.mGroupId != LE_AUDIO_GROUP_ID_INVALID) {
                    continue;
                }

                deviceDescriptor.mStateMachine.dump(sb);
                ProfileService.println(sb, "    mDevInbandRingtoneEnabled: "
                        + deviceDescriptor.mDevInbandRingtoneEnabled);
                ProfileService.println(sb, "    mSinkAudioLocation: "
                        + deviceDescriptor.mSinkAudioLocation);
                ProfileService.println(sb, "    mDirection: " + deviceDescriptor.mDirection);
            }
        }
    }
}
+42 −0
Original line number Diff line number Diff line
@@ -61,6 +61,7 @@ import com.android.bluetooth.btservice.ServiceFactory;
import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.bluetooth.hfp.HeadsetService;
import com.android.bluetooth.mcp.McpService;
import com.android.bluetooth.tbs.TbsService;
import com.android.bluetooth.vc.VolumeControlService;

import org.junit.After;
@@ -112,6 +113,7 @@ public class LeAudioServiceTest {
    @Mock private LeAudioNativeInterface mNativeInterface;
    @Mock private LeAudioTmapGattServer mTmapGattServer;
    @Mock private McpService mMcpService;
    @Mock private TbsService mTbsService;
    @Mock private VolumeControlService mVolumeControlService;
    @Spy private LeAudioObjectsFactory mObjectsFactory = LeAudioObjectsFactory.getInstance();
    @Spy private ServiceFactory mServiceFactory = new ServiceFactory();
@@ -179,6 +181,7 @@ public class LeAudioServiceTest {
        startService();
        mService.mAudioManager = mAudioManager;
        mService.mMcpService = mMcpService;
        mService.mTbsService = mTbsService;
        mService.mServiceFactory = mServiceFactory;
        when(mServiceFactory.getVolumeControlService()).thenReturn(mVolumeControlService);

@@ -1030,6 +1033,11 @@ public class LeAudioServiceTest {
    @Test
    public void testSetActiveDeviceGroup() {
        int groupId = 1;
        /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
        int direction = 1;
        int snkAudioLocation = 3;
        int srcAudioLocation = 4;
        int availableContexts = 5;

        // Not connected device
        assertThat(mService.setActiveDevice(mSingleDevice)).isFalse();
@@ -1037,10 +1045,39 @@ public class LeAudioServiceTest {
        // Connected device
        doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
        connectTestDevice(mSingleDevice, testGroupId);

             // Add location support
        LeAudioStackEvent audioConfChangedEvent =
             new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
        audioConfChangedEvent.device = mSingleDevice;
        audioConfChangedEvent.valueInt1 = direction;
        audioConfChangedEvent.valueInt2 = groupId;
        audioConfChangedEvent.valueInt3 = snkAudioLocation;
        audioConfChangedEvent.valueInt4 = srcAudioLocation;
        audioConfChangedEvent.valueInt5 = availableContexts;
        mService.messageFromNative(audioConfChangedEvent);

        assertThat(mService.setActiveDevice(mSingleDevice)).isTrue();
        verify(mNativeInterface).groupSetActive(groupId);

        //Set group and device as active
        LeAudioStackEvent groupStatusChangedEvent =
                new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED);
        groupStatusChangedEvent.valueInt1 = groupId;
        groupStatusChangedEvent.valueInt2 = LeAudioStackEvent.GROUP_STATUS_ACTIVE;
        mService.messageFromNative(groupStatusChangedEvent);

        verify(mTbsService).setInbandRingtoneSupport(mSingleDevice);

        // no active device
        assertThat(mService.setActiveDevice(null)).isTrue();
        verify(mNativeInterface).groupSetActive(BluetoothLeAudio.GROUP_ID_INVALID);

        //Set group and device as inactive active
        groupStatusChangedEvent.valueInt2 = LeAudioStackEvent.GROUP_STATUS_INACTIVE;
        mService.messageFromNative(groupStatusChangedEvent);

        verify(mTbsService).clearInbandRingtoneSupport(mSingleDevice);
    }

    /**
@@ -1378,6 +1415,8 @@ public class LeAudioServiceTest {
        verify(mAudioManager, times(1)).handleBluetoothActiveDeviceChanged(any(), eq(leadDevice),
                any(BluetoothProfileConnectionInfo.class));

        verify(mTbsService).setInbandRingtoneSupport(mLeftDevice);
        verify(mTbsService).setInbandRingtoneSupport(mRightDevice);
    }

    /**
@@ -1449,6 +1488,9 @@ public class LeAudioServiceTest {

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

        verify(mTbsService).setInbandRingtoneSupport(mLeftDevice);
        verify(mTbsService).setInbandRingtoneSupport(mRightDevice);
    }

    /**