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

Commit 0a182bd2 authored by Rongxuan Liu's avatar Rongxuan Liu Committed by Automerger Merge Worker
Browse files

Merge changes Ic7fd250a,I4ebb08d6 into main am: f2a34430 am: 7fdb78a1

parents 30919026 7fdb78a1
Loading
Loading
Loading
Loading
+258 −47
Original line number Diff line number Diff line
@@ -31,6 +31,9 @@ import static android.bluetooth.IBluetoothCsipSetCoordinator.CSIS_GROUP_ID_INVAL
import static android.bluetooth.IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID;
import static android.bluetooth.IBluetoothVolumeControl.VOLUME_CONTROL_UNKNOWN_VOLUME;

import static com.android.bluetooth.flags.Flags.leaudioBroadcastVolumeControlPrimaryGroupOnly;
import static com.android.bluetooth.flags.Flags.vcpDeviceVolumeApiImprovements;

import static java.util.Objects.requireNonNull;
import static java.util.Objects.requireNonNullElseGet;

@@ -64,7 +67,6 @@ import com.android.bluetooth.btservice.ProfileService;
import com.android.bluetooth.btservice.ServiceFactory;
import com.android.bluetooth.btservice.storage.DatabaseManager;
import com.android.bluetooth.csip.CsipSetCoordinatorService;
import com.android.bluetooth.flags.Flags;
import com.android.bluetooth.le_audio.LeAudioService;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -117,9 +119,12 @@ public class VolumeControlService extends ProfileService {
            new HashMap<>();
    private final Map<BluetoothDevice, VolumeControlInputDescriptor> mAudioInputs =
            new ConcurrentHashMap<>();
    private final Map<Integer, Integer> mGroupVolumeCache = new HashMap<>();
    private final Map<Integer, Boolean> mGroupMuteCache = new HashMap<>();
    private final Map<BluetoothDevice, Integer> mDeviceVolumeCache = new HashMap<>();
    private final Map<Integer, Integer> mGroupVolumeCache = new ConcurrentHashMap<>();
    private final Map<Integer, Boolean> mGroupMuteCache = new ConcurrentHashMap<>();
    private final Map<BluetoothDevice, Integer> mDeviceVolumeCache = new ConcurrentHashMap<>();
    private final Map<BluetoothDevice, Boolean> mDeviceMuteCache = new ConcurrentHashMap<>();

    private Boolean mIgnoreSetVolumeFromAF = false;

    @VisibleForTesting ServiceFactory mFactory = new ServiceFactory();

@@ -198,6 +203,7 @@ public class VolumeControlService extends ProfileService {
        mGroupVolumeCache.clear();
        mGroupMuteCache.clear();
        mDeviceVolumeCache.clear();
        mDeviceMuteCache.clear();

        synchronized (mCallbacks) {
            mCallbacks.kill();
@@ -529,7 +535,8 @@ public class VolumeControlService extends ProfileService {
        mNativeInterface.setExtAudioOutVolumeOffset(device, instanceId, volumeOffset);
    }

    void setDeviceVolume(BluetoothDevice device, int volume, boolean isGroupOp) {
    public synchronized void setDeviceVolume(
            BluetoothDevice device, int volume, boolean isGroupOp) {
        Log.d(
                TAG,
                "setDeviceVolume: " + device + ", volume: " + volume + ", isGroupOp: " + isGroupOp);
@@ -551,18 +558,60 @@ public class VolumeControlService extends ProfileService {
            Log.i(TAG, "Setting individual device volume");
            mDeviceVolumeCache.put(device, volume);
            mNativeInterface.setVolume(device, volume);

            if (vcpDeviceVolumeApiImprovements()) {
                // We only receive the volume change and mute state needs to be acquired manually
                Boolean isStreamMute =
                        mAudioManager.isStreamMute(getBluetoothContextualVolumeStream());
                adjustDeviceMute(device, volume, isStreamMute);
            }
        }
    }

    public void setGroupVolume(int groupId, int volume) {
    private void adjustDeviceMute(BluetoothDevice device, int volume, Boolean isStreamMute) {
        Boolean isMute = getMute(device);
        if (!isMute.equals(isStreamMute)) {
            Log.w(
                    TAG,
                    "Mute state mismatch, stream mute: "
                            + isStreamMute
                            + ", device mute: "
                            + isMute
                            + ", new volume: "
                            + volume);
            if (isStreamMute) {
                Log.i(TAG, "Mute the device " + device);
                mute(device);
            }
            if (!isStreamMute && (volume > 0)) {
                Log.i(TAG, "Unmute the device " + device);
                unmute(device);
            }
        }
    }

    public synchronized void setGroupVolume(int groupId, int volume) {
        Log.d(TAG, "setGroupVolume: " + groupId + ", volume: " + volume);

        if (mIgnoreSetVolumeFromAF) {
            Log.d(TAG, "setGroupVolume ignored (from AF) because persisted/cached volume was used");
            mIgnoreSetVolumeFromAF = false;
            return;
        }

        if (volume < 0) {
            Log.w(TAG, "Tried to set invalid volume " + volume + ". Ignored.");
            return;
        }

        synchronized (mDeviceVolumeCache) {
            mGroupVolumeCache.put(groupId, volume);
            if (vcpDeviceVolumeApiImprovements()) {
                for (BluetoothDevice dev : getGroupDevices(groupId)) {
                    mDeviceVolumeCache.put(dev, volume);
                }
            }
        }
        mNativeInterface.setGroupVolume(groupId, volume);

        // We only receive the volume change and mute state needs to be acquired manually
@@ -594,22 +643,61 @@ public class VolumeControlService extends ProfileService {
                Log.i(TAG, "Unmute the group " + groupId);
                unmuteGroup(groupId);
            }
        } else if (vcpDeviceVolumeApiImprovements()) {
            for (BluetoothDevice device : getGroupDevices(groupId)) {
                adjustDeviceMute(device, volume, isStreamMute);
            }
        }
    }

    /**
     * Get group cached volume. If not cached, then try to read from any device from this group.
     *
     * @param groupId the group identifier
     * @return the cached volume
     */
    public int getGroupVolume(int groupId) {
        if (vcpDeviceVolumeApiImprovements()) {
            synchronized (mDeviceVolumeCache) {
                Integer volume = mGroupVolumeCache.get(groupId);
                if (volume != null) {
                    return volume;
                }
                Log.w(TAG, "No group volume available");
                for (BluetoothDevice device : getGroupDevices(groupId)) {
                    volume = mDeviceVolumeCache.get(device);
                    if (volume != null) {
                        Log.w(TAG, "Volume taken from device: " + device);
                        return volume;
                    }
                }
                return VOLUME_CONTROL_UNKNOWN_VOLUME;
            }
        } else {
            return mGroupVolumeCache.getOrDefault(groupId, VOLUME_CONTROL_UNKNOWN_VOLUME);
        }
    }

    /**
     * Get device cached volume.
     * Get device cached volume. If not cached, then try to read from its group.
     *
     * @param device the device
     * @return the cached volume
     */
    public int getDeviceVolume(BluetoothDevice device) {
        if (vcpDeviceVolumeApiImprovements()) {
            synchronized (mDeviceVolumeCache) {
                Integer volume = mDeviceVolumeCache.get(device);
                if (volume != null) {
                    return volume;
                }
                return mGroupVolumeCache.getOrDefault(
                        getGroupId(device), VOLUME_CONTROL_UNKNOWN_VOLUME);
            }
        } else {
            return mDeviceVolumeCache.getOrDefault(device, VOLUME_CONTROL_UNKNOWN_VOLUME);
        }
    }

    /**
     * This should be called by LeAudioService when LE Audio group change it active state.
@@ -617,7 +705,7 @@ public class VolumeControlService extends ProfileService {
     * @param groupId the group identifier
     * @param active indicator if group is active or not
     */
    public void setGroupActive(int groupId, boolean active) {
    public synchronized void setGroupActive(int groupId, boolean active) {
        Log.d(TAG, "setGroupActive: " + groupId + ", active: " + active);
        if (!active) {
            /* For now we don't need to handle group inactivation */
@@ -634,27 +722,75 @@ public class VolumeControlService extends ProfileService {
    }

    /**
     * Get device cached mute status. If not cached, then try to read from its group.
     *
     * @param device the device
     * @return mute status
     */
    public Boolean getMute(BluetoothDevice device) {
        synchronized (mDeviceMuteCache) {
            Boolean isMute = mDeviceMuteCache.get(device);
            if (isMute != null) {
                return isMute;
            }
            return mGroupMuteCache.getOrDefault(getGroupId(device), false);
        }
    }

    /**
     * Get group cached mute status. If not cached, then try to read from any device from this
     * group.
     *
     * @param groupId the group identifier
     * @return mute status
     */
    public Boolean getGroupMute(int groupId) {
        if (vcpDeviceVolumeApiImprovements()) {
            synchronized (mDeviceMuteCache) {
                Boolean isMute = mGroupMuteCache.get(groupId);
                if (isMute != null) {
                    return isMute;
                }
                for (BluetoothDevice device : getGroupDevices(groupId)) {
                    isMute = mDeviceMuteCache.get(device);
                    if (isMute != null) {
                        return isMute;
                    }
                }
                return false;
            }
        } else {
            return mGroupMuteCache.getOrDefault(groupId, false);
        }
    }

    public void mute(BluetoothDevice device) {
        mDeviceMuteCache.put(device, true);
        mNativeInterface.mute(device);
    }

    public void muteGroup(int groupId) {
        synchronized (mDeviceMuteCache) {
            mGroupMuteCache.put(groupId, true);
            for (BluetoothDevice dev : getGroupDevices(groupId)) {
                mDeviceMuteCache.put(dev, true);
            }
        }
        mNativeInterface.muteGroup(groupId);
    }

    public void unmute(BluetoothDevice device) {
        mDeviceMuteCache.put(device, false);
        mNativeInterface.unmute(device);
    }

    public void unmuteGroup(int groupId) {
        synchronized (mDeviceMuteCache) {
            mGroupMuteCache.put(groupId, false);
            for (BluetoothDevice dev : getGroupDevices(groupId)) {
                mDeviceMuteCache.put(dev, false);
            }
        }
        mNativeInterface.unmuteGroup(groupId);
    }

@@ -724,7 +860,7 @@ public class VolumeControlService extends ProfileService {
        notifyNewCallbackOfKnownVolumeInfo(callback);
    }

    public void handleGroupNodeAdded(int groupId, BluetoothDevice device) {
    public synchronized void handleGroupNodeAdded(int groupId, BluetoothDevice device) {
        // Ignore disconnected device, its volume will be set once it connects
        synchronized (mStateMachines) {
            VolumeControlStateMachine sm = mStateMachines.get(device);
@@ -737,6 +873,20 @@ public class VolumeControlService extends ProfileService {
        }

        // If group volume has already changed, the new group member should set it
        if (vcpDeviceVolumeApiImprovements()) {
            int volume = getDeviceVolume(device);
            if (volume != VOLUME_CONTROL_UNKNOWN_VOLUME) {
                Log.i(TAG, "Setting device/group volume:" + volume + " to the device:" + device);
                setDeviceVolume(device, volume, false);
                Boolean isDeviceMuted = getMute(device);
                Log.i(TAG, "Setting mute:" + isDeviceMuted + " to " + device);
                if (isDeviceMuted) {
                    mute(device);
                } else {
                    unmute(device);
                }
            }
        } else {
            Integer groupVolume = getGroupVolume(groupId);
            if (groupVolume != VOLUME_CONTROL_UNKNOWN_VOLUME) {
                Log.i(TAG, "Setting value:" + groupVolume + " to " + device);
@@ -751,6 +901,7 @@ public class VolumeControlService extends ProfileService {
                mNativeInterface.unmute(device);
            }
        }
    }

    void updateGroupCacheAndAudioSystem(int groupId, int volume, boolean mute, boolean showInUI) {
        Log.d(
@@ -769,14 +920,22 @@ public class VolumeControlService extends ProfileService {
            return;
        }

        synchronized (mDeviceVolumeCache) {
            mGroupVolumeCache.put(groupId, volume);
            mGroupMuteCache.put(groupId, mute);
            if (vcpDeviceVolumeApiImprovements()) {
                for (BluetoothDevice dev : getGroupDevices(groupId)) {
                    mDeviceVolumeCache.put(dev, volume);
                    mDeviceMuteCache.put(dev, mute);
                }
            }
        }

        LeAudioService leAudioService = mFactory.getLeAudioService();
        if (leAudioService != null) {
            int currentlyActiveGroupId = leAudioService.getActiveGroupId();
            if (currentlyActiveGroupId == GROUP_ID_INVALID || groupId != currentlyActiveGroupId) {
                if (!Flags.leaudioBroadcastVolumeControlPrimaryGroupOnly()) {
                if (!leaudioBroadcastVolumeControlPrimaryGroupOnly()) {
                    Log.i(
                            TAG,
                            "Skip updating to audio system if not updating volume for current"
@@ -823,7 +982,7 @@ public class VolumeControlService extends ProfileService {
        return (int) Math.round((double) streamVolume * LE_AUDIO_MAX_VOL / streamMaxVolume);
    }

    void handleVolumeControlChanged(
    synchronized void handleVolumeControlChanged(
            BluetoothDevice device,
            int groupId,
            int volume,
@@ -838,9 +997,6 @@ public class VolumeControlService extends ProfileService {
            return;
        }

        int groupVolume = getGroupVolume(groupId);
        Boolean groupMute = getGroupMute(groupId);

        if (isAutonomous && device != null) {
            Log.i(
                    TAG,
@@ -851,26 +1007,60 @@ public class VolumeControlService extends ProfileService {
            /* We are here, because system has just started and LeAudio device is connected. If
             * remote device has User Persistent flag set, Android sets the volume to local cache
             * and to the audio system if not already streaming to other devices.
             * If Reset Flag is set, then Android sets to remote devices either cached volume volume
             * taken from audio manager.
             * If Reset Flag is set, then Android sets to remote devices either cached volume or
             * volume taken from audio manager (AF always call setVolume via LeAudioService at first
             * connected remote from group).
             * Note, to match BR/EDR behavior, don't show volume change in UI here
             */
            if (((flags & VOLUME_FLAGS_PERSISTED_USER_SET_VOLUME_MASK) == 0x01)
                    && (getConnectedDevices(groupId).size() == 1)) {
                Log.i(TAG, "Setting device: " + device + " volume: " + volume + " to the system");
                if (vcpDeviceVolumeApiImprovements()) {
                    // Ignore volume from AF because persisted volume was used
                    mIgnoreSetVolumeFromAF = true;
                }
                updateGroupCacheAndAudioSystem(groupId, volume, mute, false);
                return;
            }

            // Reset flag is used
            if (vcpDeviceVolumeApiImprovements()) {
                int deviceVolume = getDeviceVolume(device);
                if (deviceVolume != VOLUME_CONTROL_UNKNOWN_VOLUME) {
                    Log.i(
                            TAG,
                            "Setting device/group volume: "
                                    + deviceVolume
                                    + " to the device: "
                                    + device);
                    setDeviceVolume(device, deviceVolume, false);
                    Boolean isDeviceMuted = getMute(device);
                    Log.i(TAG, "Setting mute:" + isDeviceMuted + " to " + device);
                    if (isDeviceMuted) {
                        mute(device);
                    } else {
                        unmute(device);
                    }
                    if (getConnectedDevices(groupId).size() == 1) {
                        // Ignore volume from AF because cached volume was used
                        mIgnoreSetVolumeFromAF = true;
                    }
                }
            } else {
                int groupVolume = getGroupVolume(groupId);
                if (groupVolume != VOLUME_CONTROL_UNKNOWN_VOLUME) {
                Log.i(TAG, "Setting group volume: " + groupVolume + " to the device: " + device);
                    Log.i(
                            TAG,
                            "Setting group volume: " + groupVolume + " to the device: " + device);
                    setGroupVolume(groupId, groupVolume);
                } else {
                    int systemVolume = getBleVolumeFromCurrentStream();
                Log.i(TAG, "Setting system volume: " + systemVolume + " to the group: " + groupId);
                    Log.i(
                            TAG,
                            "Setting system volume: " + systemVolume + " to the group: " + groupId);
                    setGroupVolume(groupId, systemVolume);
                }
            }

            return;
        }
@@ -894,13 +1084,16 @@ public class VolumeControlService extends ProfileService {
            }
        }

        if (!isAutonomous) {
        if (!vcpDeviceVolumeApiImprovements() && !isAutonomous) {
            /* If the change is triggered by Android device, the stream is already changed.
             * However it might be called with isAutonomous, one the first read of after
             * reconnection. Make sure device has group volume. Also it might happen that
             * remote side send us wrong value - lets check it.
             */

            int groupVolume = getGroupVolume(groupId);
            Boolean groupMute = getGroupMute(groupId);

            if ((groupVolume == volume) && (groupMute == mute)) {
                Log.i(TAG, " Volume:" + volume + ", mute:" + mute + " confirmed by remote side.");
                return;
@@ -936,7 +1129,9 @@ public class VolumeControlService extends ProfileService {
                                + " expected volume: "
                                + groupVolume);
            }
        } else {
        }

        if (isAutonomous && device == null) {
            /* Received group notification for autonomous change. Update cache and audio system. */
            updateGroupCacheAndAudioSystem(groupId, volume, mute, true);
        }
@@ -1249,7 +1444,7 @@ public class VolumeControlService extends ProfileService {
        input.onGainSettingsPropertiesChanged(id, unit, min, max);
    }

    void handleStackEvent(VolumeControlStackEvent stackEvent) {
    synchronized void handleStackEvent(VolumeControlStackEvent stackEvent) {
        if (!isAvailable()) {
            Log.e(TAG, "Event ignored, service not available: " + stackEvent);
            return;
@@ -1375,10 +1570,13 @@ public class VolumeControlService extends ProfileService {
            int broadcastVolume = VOLUME_CONTROL_UNKNOWN_VOLUME;
            if (volume.isPresent()) {
                broadcastVolume = volume.get();
                if (!vcpDeviceVolumeApiImprovements()) {
                    mDeviceVolumeCache.put(dev, broadcastVolume);
                }
            } else {
                broadcastVolume = getDeviceVolume(dev);
                if (broadcastVolume == VOLUME_CONTROL_UNKNOWN_VOLUME) {
                if (!vcpDeviceVolumeApiImprovements()
                        && broadcastVolume == VOLUME_CONTROL_UNKNOWN_VOLUME) {
                    broadcastVolume = getGroupVolume(getGroupId(dev));
                }
            }
@@ -1474,7 +1672,7 @@ public class VolumeControlService extends ProfileService {
                Log.d(TAG, device + " is unbond. Remove state machine");
                removeStateMachine(device);
            }
        } else if (toState == STATE_CONNECTED) {
        } else if (!vcpDeviceVolumeApiImprovements() && toState == STATE_CONNECTED) {
            // Restore the group volume if it was changed while the device was not yet connected.
            Integer groupId = getGroupId(device);
            if (groupId != GROUP_ID_INVALID) {
@@ -2025,5 +2223,18 @@ public class VolumeControlService extends ProfileService {
                            + ", mute: "
                            + getGroupMute(entry.getKey()));
        }

        if (vcpDeviceVolumeApiImprovements()) {
            for (Map.Entry<BluetoothDevice, Integer> entry : mDeviceVolumeCache.entrySet()) {
                ProfileService.println(
                        sb,
                        "    Device: "
                                + entry.getKey()
                                + " volume: "
                                + entry.getValue()
                                + ", mute: "
                                + getMute(entry.getKey()));
            }
        }
    }
}
+152 −11

File changed.

Preview size limit exceeded, changes collapsed.