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

Commit ffd7f45e authored by Eric Laurent's avatar Eric Laurent Committed by Android (Google) Code Review
Browse files

Merge "[IMPR] AudioService: VolumeGroupState: improve implementation"

parents cac4505e d80f3dd4
Loading
Loading
Loading
Loading
+235 −99
Original line number Diff line number Diff line
@@ -3782,7 +3782,7 @@ public class AudioService extends IAudioService.Stub
        VolumeGroupState vgs = sVolumeGroupStates.get(volumeGroup);
        sVolumeLogger.enqueue(new VolumeEvent(VolumeEvent.VOL_SET_GROUP_VOL, attr, vgs.name(),
                index/*val1*/, flags/*val2*/, callingPackage));
                index, flags, callingPackage + ", user " + ActivityManager.getCurrentUser()));
        vgs.setVolumeIndex(index, flags);
@@ -3803,7 +3803,7 @@ public class AudioService extends IAudioService.Stub
    @Nullable
    private AudioVolumeGroup getAudioVolumeGroupById(int volumeGroupId) {
        for (final AudioVolumeGroup avg : AudioVolumeGroup.getAudioVolumeGroups()) {
        for (AudioVolumeGroup avg : AudioVolumeGroup.getAudioVolumeGroups()) {
            if (avg.getId() == volumeGroupId) {
                return avg;
            }
@@ -3819,14 +3819,15 @@ public class AudioService extends IAudioService.Stub
        super.getVolumeIndexForAttributes_enforcePermission();
        Objects.requireNonNull(attr, "attr must not be null");
        final int volumeGroup =
                AudioProductStrategy.getVolumeGroupIdForAudioAttributes(
        synchronized (VolumeStreamState.class) {
            int volumeGroup = AudioProductStrategy.getVolumeGroupIdForAudioAttributes(
                    attr, /* fallbackOnDefault= */false);
            if (sVolumeGroupStates.indexOfKey(volumeGroup) < 0) {
                throw new IllegalArgumentException("No volume group for attributes " + attr);
            }
        final VolumeGroupState vgs = sVolumeGroupStates.get(volumeGroup);
        return vgs.getVolumeIndex();
            VolumeGroupState vgs = sVolumeGroupStates.get(volumeGroup);
            return vgs.isMuted() ? vgs.getMinIndex() : vgs.getVolumeIndex();
        }
    }
    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
@@ -5922,7 +5923,7 @@ public class AudioService extends IAudioService.Stub
        mSoundDoseHelper.restoreMusicActiveMs();
        mSoundDoseHelper.enforceSafeMediaVolumeIfActive(TAG);
        readVolumeGroupsSettings();
        readVolumeGroupsSettings(userSwitch);
        if (DEBUG_VOL) {
            Log.d(TAG, "Restoring device volume behavior");
@@ -7311,6 +7312,7 @@ public class AudioService extends IAudioService.Stub
            try {
                // if no valid attributes, this volume group is not controllable, throw exception
                ensureValidAttributes(avg);
                sVolumeGroupStates.append(avg.getId(), new VolumeGroupState(avg));
            } catch (IllegalArgumentException e) {
                // Volume Groups without attributes are not controllable through set/get volume
                // using attributes. Do not append them.
@@ -7319,11 +7321,10 @@ public class AudioService extends IAudioService.Stub
                }
                continue;
            }
            sVolumeGroupStates.append(avg.getId(), new VolumeGroupState(avg));
        }
        for (int i = 0; i < sVolumeGroupStates.size(); i++) {
            final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i);
            vgs.applyAllVolumes();
            vgs.applyAllVolumes(/* userSwitch= */ false);
        }
    }
@@ -7336,14 +7337,22 @@ public class AudioService extends IAudioService.Stub
        }
    }
    private void readVolumeGroupsSettings() {
    private void readVolumeGroupsSettings(boolean userSwitch) {
        synchronized (mSettingsLock) {
            synchronized (VolumeStreamState.class) {
                if (DEBUG_VOL) {
            Log.v(TAG, "readVolumeGroupsSettings");
                    Log.d(TAG, "readVolumeGroupsSettings userSwitch=" + userSwitch);
                }
                for (int i = 0; i < sVolumeGroupStates.size(); i++) {
            final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i);
                    VolumeGroupState vgs = sVolumeGroupStates.valueAt(i);
                    // as for STREAM_MUSIC, preserve volume from one user to the next.
                    if (!(userSwitch && vgs.isMusic())) {
                        vgs.clearIndexCache();
                        vgs.readSettings();
            vgs.applyAllVolumes();
                    }
                    vgs.applyAllVolumes(userSwitch);
                }
            }
        }
    }
@@ -7354,7 +7363,7 @@ public class AudioService extends IAudioService.Stub
        }
        for (int i = 0; i < sVolumeGroupStates.size(); i++) {
            final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i);
            vgs.applyAllVolumes();
            vgs.applyAllVolumes(false/*userSwitch*/);
        }
    }
@@ -7367,17 +7376,24 @@ public class AudioService extends IAudioService.Stub
        }
    }
    private static boolean isCallStream(int stream) {
        return stream == AudioSystem.STREAM_VOICE_CALL
                || stream == AudioSystem.STREAM_BLUETOOTH_SCO;
    }
    // NOTE: Locking order for synchronized objects related to volume management:
    //  1     mSettingsLock
    //  2       VolumeGroupState.class
    //  2       VolumeStreamState.class
    private class VolumeGroupState {
        private final AudioVolumeGroup mAudioVolumeGroup;
        private final SparseIntArray mIndexMap = new SparseIntArray(8);
        private int mIndexMin;
        private int mIndexMax;
        private int mLegacyStreamType = AudioSystem.STREAM_DEFAULT;
        private boolean mHasValidStreamType = false;
        private int mPublicStreamType = AudioSystem.STREAM_MUSIC;
        private AudioAttributes mAudioAttributes = AudioProductStrategy.getDefaultAttributes();
        private boolean mIsMuted = false;
        private final String mSettingName;
        // No API in AudioSystem to get a device from strategy or from attributes.
        // Need a valid public stream type to use current API getDeviceForStream
@@ -7391,20 +7407,22 @@ public class AudioService extends IAudioService.Stub
                Log.v(TAG, "VolumeGroupState for " + avg.toString());
            }
            // mAudioAttributes is the default at this point
            for (final AudioAttributes aa : avg.getAudioAttributes()) {
            for (AudioAttributes aa : avg.getAudioAttributes()) {
                if (!aa.equals(mAudioAttributes)) {
                    mAudioAttributes = aa;
                    break;
                }
            }
            final int[] streamTypes = mAudioVolumeGroup.getLegacyStreamTypes();
            int[] streamTypes = mAudioVolumeGroup.getLegacyStreamTypes();
            String streamSettingName = "";
            if (streamTypes.length != 0) {
                // Uses already initialized MIN / MAX if a stream type is attached to group
                mLegacyStreamType = streamTypes[0];
                for (final int streamType : streamTypes) {
                for (int streamType : streamTypes) {
                    if (streamType != AudioSystem.STREAM_DEFAULT
                            && streamType < AudioSystem.getNumStreamTypes()) {
                        mPublicStreamType = streamType;
                        mHasValidStreamType = true;
                        streamSettingName = System.VOLUME_SETTINGS_INT[mPublicStreamType];
                        break;
                    }
                }
@@ -7414,10 +7432,10 @@ public class AudioService extends IAudioService.Stub
                mIndexMin = AudioSystem.getMinVolumeIndexForAttributes(mAudioAttributes);
                mIndexMax = AudioSystem.getMaxVolumeIndexForAttributes(mAudioAttributes);
            } else {
                Log.e(TAG, "volume group: " + mAudioVolumeGroup.name()
                throw new IllegalArgumentException("volume group: " + mAudioVolumeGroup.name()
                        + " has neither valid attributes nor valid stream types assigned");
                return;
            }
            mSettingName = !streamSettingName.isEmpty() ? streamSettingName : ("volume_" + name());
            // Load volume indexes from data base
            readSettings();
        }
@@ -7430,23 +7448,81 @@ public class AudioService extends IAudioService.Stub
            return mAudioVolumeGroup.name();
        }
        /**
         * Volume group with non null minimum index are considered as non mutable, thus
         * bijectivity is broken with potential associated stream type.
         * VOICE_CALL stream has minVolumeIndex > 0  but can be muted directly by an
         * app that has MODIFY_PHONE_STATE permission.
         */
        private boolean isVssMuteBijective(int stream) {
            return isStreamAffectedByMute(stream)
                    && (getMinIndex() == (mStreamStates[stream].mIndexMin + 5) / 10)
                    && (getMinIndex() == 0 || isCallStream(stream));
        }
        private boolean isMutable() {
            return mIndexMin == 0 || (mHasValidStreamType && isVssMuteBijective(mPublicStreamType));
        }
        /**
         * Mute/unmute the volume group
         * @param muted the new mute state
         */
        @GuardedBy("AudioService.VolumeStreamState.class")
        public boolean mute(boolean muted) {
            if (!isMutable()) {
                // Non mutable volume group
                if (DEBUG_VOL) {
                    Log.d(TAG, "invalid mute on unmutable volume group " + name());
                }
                return false;
            }
            boolean changed = (mIsMuted != muted);
            // As for VSS, mute shall apply minIndex to all devices found in IndexMap and default.
            if (changed) {
                mIsMuted = muted;
                applyAllVolumes(false /*userSwitch*/);
            }
            return changed;
        }
        public boolean isMuted() {
            return mIsMuted;
        }
        public int getVolumeIndex() {
            synchronized (VolumeStreamState.class) {
                return getIndex(getDeviceForVolume());
            }
        }
        public void setVolumeIndex(int index, int flags) {
            synchronized (VolumeStreamState.class) {
                if (mUseFixedVolume) {
                    return;
                }
                setVolumeIndex(index, getDeviceForVolume(), flags);
            }
        }
        @GuardedBy("AudioService.VolumeStreamState.class")
        private void setVolumeIndex(int index, int device, int flags) {
            // Set the volume index
            // Update cache & persist (muted by volume 0 shall be persisted)
            updateVolumeIndex(index, device);
            // setting non-zero volume for a muted stream unmutes the stream and vice versa,
            boolean changed = mute(index == 0);
            if (!changed) {
                // Set the volume index only if mute operation is a no-op
                index = getValidIndex(index);
                setVolumeIndexInt(index, device, flags);
            }
        }
        @GuardedBy("AudioService.VolumeStreamState.class")
        public void updateVolumeIndex(int index, int device) {
            // Filter persistency if already exist and the index has not changed
            if (mIndexMap.indexOfKey(device) < 0 || mIndexMap.get(device) != index) {
                // Update local cache
            mIndexMap.put(device, index);
                mIndexMap.put(device, getValidIndex(index));
                // update data base - post a persist volume group msg
                sendMsg(mAudioHandler,
@@ -7457,13 +7533,16 @@ public class AudioService extends IAudioService.Stub
                        this,
                        PERSIST_DELAY);
            }
        }
        @GuardedBy("AudioService.VolumeStreamState.class")
        private void setVolumeIndexInt(int index, int device, int flags) {
            // Reflect mute state of corresponding stream by forcing index to 0 if muted
            // Only set audio policy BT SCO stream volume to 0 when the stream is actually muted.
            // This allows RX path muting by the audio HAL only when explicitly muted but not when
            // index is just set to 0 to repect BT requirements
            if (mStreamStates[mPublicStreamType].isFullyMuted()) {
            if (mHasValidStreamType && isVssMuteBijective(mPublicStreamType)
                    && mStreamStates[mPublicStreamType].isFullyMuted()) {
                index = 0;
            } else if (mPublicStreamType == AudioSystem.STREAM_BLUETOOTH_SCO && index == 0) {
                index = 1;
@@ -7472,19 +7551,17 @@ public class AudioService extends IAudioService.Stub
            AudioSystem.setVolumeIndexForAttributes(mAudioAttributes, index, device);
        }
        public int getIndex(int device) {
            synchronized (VolumeGroupState.class) {
        @GuardedBy("AudioService.VolumeStreamState.class")
        private int getIndex(int device) {
            int index = mIndexMap.get(device, -1);
            // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
            return (index != -1) ? index : mIndexMap.get(AudioSystem.DEVICE_OUT_DEFAULT);
        }
        }
        public boolean hasIndexForDevice(int device) {
            synchronized (VolumeGroupState.class) {
        @GuardedBy("AudioService.VolumeStreamState.class")
        private boolean hasIndexForDevice(int device) {
            return (mIndexMap.get(device, -1) != -1);
        }
        }
        public int getMaxIndex() {
            return mIndexMax;
@@ -7494,55 +7571,108 @@ public class AudioService extends IAudioService.Stub
            return mIndexMin;
        }
        private boolean isValidLegacyStreamType() {
            return (mLegacyStreamType != AudioSystem.STREAM_DEFAULT)
                    && (mLegacyStreamType < mStreamStates.length);
        private boolean isValidStream(int stream) {
            return (stream != AudioSystem.STREAM_DEFAULT) && (stream < mStreamStates.length);
        }
        public void applyAllVolumes() {
            synchronized (VolumeGroupState.class) {
                int deviceForStream = AudioSystem.DEVICE_NONE;
                int volumeIndexForStream = 0;
                if (isValidLegacyStreamType()) {
                    // Prevent to apply settings twice when group is associated to public stream
                    deviceForStream = getDeviceForStream(mLegacyStreamType);
                    volumeIndexForStream = getStreamVolume(mLegacyStreamType);
        public boolean isMusic() {
            return mHasValidStreamType && mPublicStreamType == AudioSystem.STREAM_MUSIC;
        }
        public void applyAllVolumes(boolean userSwitch) {
            String caller = "from vgs";
            synchronized (VolumeStreamState.class) {
                // apply device specific volumes first
                int index;
                for (int i = 0; i < mIndexMap.size(); i++) {
                    final int device = mIndexMap.keyAt(i);
                    int device = mIndexMap.keyAt(i);
                    int index = mIndexMap.valueAt(i);
                    boolean synced = false;
                    if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
                        index = mIndexMap.valueAt(i);
                        if (device == deviceForStream && volumeIndexForStream == index) {
                        for (int stream : getLegacyStreamTypes()) {
                            if (isValidStream(stream)) {
                                boolean streamMuted = mStreamStates[stream].mIsMuted;
                                int deviceForStream = getDeviceForStream(stream);
                                int indexForStream =
                                        (mStreamStates[stream].getIndex(deviceForStream) + 5) / 10;
                                if (device == deviceForStream) {
                                    if (indexForStream == index && (isMuted() == streamMuted)
                                            && isVssMuteBijective(stream)) {
                                        synced = true;
                                        continue;
                                    }
                                    if (indexForStream != index) {
                                        mStreamStates[stream].setIndex(index * 10, device, caller,
                                                true /*hasModifyAudioSettings*/);
                                    }
                                    if ((isMuted() != streamMuted) && isVssMuteBijective(stream)) {
                                        mStreamStates[stream].mute(isMuted());
                                    }
                                }
                            }
                        }
                        if (!synced) {
                            if (DEBUG_VOL) {
                            Log.v(TAG, "applyAllVolumes: restore index " + index + " for group "
                                Log.d(TAG, "applyAllVolumes: apply index " + index + ", group "
                                        + mAudioVolumeGroup.name() + " and device "
                                        + AudioSystem.getOutputDeviceName(device));
                            }
                        setVolumeIndexInt(index, device, 0 /*flags*/);
                            setVolumeIndexInt(isMuted() ? 0 : index, device, 0 /*flags*/);
                        }
                    }
                }
                // apply default volume last: by convention , default device volume will be used
                // by audio policy manager if no explicit volume is present for a given device type
                index = getIndex(AudioSystem.DEVICE_OUT_DEFAULT);
                int index = getIndex(AudioSystem.DEVICE_OUT_DEFAULT);
                boolean synced = false;
                int deviceForVolume = getDeviceForVolume();
                boolean forceDeviceSync = userSwitch && (mIndexMap.indexOfKey(deviceForVolume) < 0);
                for (int stream : getLegacyStreamTypes()) {
                    if (isValidStream(stream)) {
                        boolean streamMuted = mStreamStates[stream].mIsMuted;
                        int defaultStreamIndex = (mStreamStates[stream].getIndex(
                                        AudioSystem.DEVICE_OUT_DEFAULT) + 5) / 10;
                        if (forceDeviceSync) {
                            mStreamStates[stream].setIndex(index * 10, deviceForVolume, caller,
                                    true /*hasModifyAudioSettings*/);
                        }
                        if (defaultStreamIndex == index && (isMuted() == streamMuted)
                                && isVssMuteBijective(stream)) {
                            synced = true;
                            continue;
                        }
                        if (defaultStreamIndex != index) {
                            mStreamStates[stream].setIndex(
                                    index * 10, AudioSystem.DEVICE_OUT_DEFAULT, caller,
                                    true /*hasModifyAudioSettings*/);
                        }
                        if ((isMuted() != streamMuted) && isVssMuteBijective(stream)) {
                            mStreamStates[stream].mute(isMuted());
                        }
                    }
                }
                if (!synced) {
                    if (DEBUG_VOL) {
                    Log.v(TAG, "applyAllVolumes: restore default device index " + index
                            + " for group " + mAudioVolumeGroup.name());
                        Log.d(TAG, "applyAllVolumes: apply default device index " + index
                                + ", group " + mAudioVolumeGroup.name());
                    }
                if (isValidLegacyStreamType()) {
                    int defaultStreamIndex = (mStreamStates[mLegacyStreamType]
                            .getIndex(AudioSystem.DEVICE_OUT_DEFAULT) + 5) / 10;
                    if (defaultStreamIndex == index) {
                        return;
                    setVolumeIndexInt(
                            isMuted() ? 0 : index, AudioSystem.DEVICE_OUT_DEFAULT, 0 /*flags*/);
                }
                if (forceDeviceSync) {
                    if (DEBUG_VOL) {
                        Log.d(TAG, "applyAllVolumes: forceDeviceSync index " + index
                                + ", device " + AudioSystem.getOutputDeviceName(deviceForVolume)
                                + ", group " + mAudioVolumeGroup.name());
                    }
                    setVolumeIndexInt(isMuted() ? 0 : index, deviceForVolume, 0);
                }
                setVolumeIndexInt(index, AudioSystem.DEVICE_OUT_DEFAULT, 0 /*flags*/);
            }
        }
        public void clearIndexCache() {
            mIndexMap.clear();
        }
        private void persistVolumeGroup(int device) {
            if (mUseFixedVolume) {
                return;
@@ -7556,16 +7686,14 @@ public class AudioService extends IAudioService.Stub
            boolean success = mSettings.putSystemIntForUser(mContentResolver,
                    getSettingNameForDevice(device),
                    getIndex(device),
                    UserHandle.USER_CURRENT);
                    isMusic() ? UserHandle.USER_SYSTEM : UserHandle.USER_CURRENT);
            if (!success) {
                Log.e(TAG, "persistVolumeGroup failed for group " +  mAudioVolumeGroup.name());
            }
        }
        public void readSettings() {
            synchronized (VolumeGroupState.class) {
                // First clear previously loaded (previous user?) settings
                mIndexMap.clear();
            synchronized (VolumeStreamState.class) {
                // force maximum volume on all streams if fixed volume property is set
                if (mUseFixedVolume) {
                    mIndexMap.put(AudioSystem.DEVICE_OUT_DEFAULT, mIndexMax);
@@ -7580,7 +7708,8 @@ public class AudioService extends IAudioService.Stub
                    int index;
                    String name = getSettingNameForDevice(device);
                    index = mSettings.getSystemIntForUser(
                            mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
                            mContentResolver, name, defaultIndex,
                            isMusic() ? UserHandle.USER_SYSTEM : UserHandle.USER_CURRENT);
                    if (index == -1) {
                        continue;
                    }
@@ -7598,6 +7727,7 @@ public class AudioService extends IAudioService.Stub
            }
        }
        @GuardedBy("AudioService.VolumeStreamState.class")
        private int getValidIndex(int index) {
            if (index < mIndexMin) {
                return mIndexMin;
@@ -7608,15 +7738,17 @@ public class AudioService extends IAudioService.Stub
        }
        public @NonNull String getSettingNameForDevice(int device) {
            final String suffix = AudioSystem.getOutputDeviceName(device);
            String suffix = AudioSystem.getOutputDeviceName(device);
            if (suffix.isEmpty()) {
                return mAudioVolumeGroup.name();
                return mSettingName;
            }
            return mAudioVolumeGroup.name() + "_" + AudioSystem.getOutputDeviceName(device);
            return mSettingName + "_" + AudioSystem.getOutputDeviceName(device);
        }
        private void dump(PrintWriter pw) {
            pw.println("- VOLUME GROUP " + mAudioVolumeGroup.name() + ":");
            pw.print("   Muted: ");
            pw.println(mIsMuted);
            pw.print("   Min: ");
            pw.println(mIndexMin);
            pw.print("   Max: ");
@@ -7626,9 +7758,9 @@ public class AudioService extends IAudioService.Stub
                if (i > 0) {
                    pw.print(", ");
                }
                final int device = mIndexMap.keyAt(i);
                int device = mIndexMap.keyAt(i);
                pw.print(Integer.toHexString(device));
                final String deviceName = device == AudioSystem.DEVICE_OUT_DEFAULT ? "default"
                String deviceName = device == AudioSystem.DEVICE_OUT_DEFAULT ? "default"
                        : AudioSystem.getOutputDeviceName(device);
                if (!deviceName.isEmpty()) {
                    pw.print(" (");
@@ -7641,7 +7773,7 @@ public class AudioService extends IAudioService.Stub
            pw.println();
            pw.print("   Devices: ");
            int n = 0;
            final int devices = getDeviceForVolume();
            int devices = getDeviceForVolume();
            for (int device : AudioSystem.DEVICE_OUT_ALL_SET) {
                if ((devices & device) == device) {
                    if (n++ > 0) {
@@ -7650,6 +7782,10 @@ public class AudioService extends IAudioService.Stub
                    pw.print(AudioSystem.getOutputDeviceName(device));
                }
            }
            pw.println();
            pw.print("   Streams: ");
            Arrays.stream(getLegacyStreamTypes())
                    .forEach(stream -> pw.print(AudioSystem.streamToString(stream) + " "));
        }
    }