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

Commit a91f811e authored by Vlad Popa's avatar Vlad Popa
Browse files

Add new API for notifying volume changed on absolute device

Previously the client would call set/adjustStreamVolume with
FLAG_ABSOLUTE_VOLUE/FLAG_BLUETOOTH_ABS_VOLUME. This is replaced with the
AudioDeviceVolumeManager#notifyAbsoluteVolumeChanged method.

Test: atest AudioDeviceVolumeManagerTest
Flag: android.media.audio.unify_absolute_volume_management
Bug: 393657380

Change-Id: Idc2b217cbdc4e551312342e87a48a8e1346984b4
parent e1d1ce10
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -7400,6 +7400,7 @@ package android.media {
  public class AudioDeviceVolumeManager {
    method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public android.media.VolumeInfo getDeviceVolume(@NonNull android.media.VolumeInfo, @NonNull android.media.AudioDeviceAttributes);
    method @FlaggedApi("android.media.audio.unify_absolute_volume_management") @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, "android.permission.QUERY_AUDIO_STATE", android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public int getDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes);
    method @FlaggedApi("android.media.audio.unify_absolute_volume_management") @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public void notifyAbsoluteVolumeChanged(@NonNull android.media.VolumeInfo, @NonNull android.media.AudioDeviceAttributes);
    method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public void setDeviceVolume(@NonNull android.media.VolumeInfo, @NonNull android.media.AudioDeviceAttributes);
    method @FlaggedApi("android.media.audio.unify_absolute_volume_management") @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public void setDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, int);
    field @FlaggedApi("android.media.audio.unify_absolute_volume_management") public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE = 3; // 0x3
+1 −0
Original line number Diff line number Diff line
@@ -1958,6 +1958,7 @@ package android.media {

  public class AudioDeviceVolumeManager {
    method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.QUERY_AUDIO_STATE, android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public boolean isFullVolumeDevice();
    method @FlaggedApi("android.media.audio.unify_absolute_volume_management") @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED, android.Manifest.permission.BLUETOOTH_PRIVILEGED, "android.permission.BLUETOOTH_STACK"}) public void resetDeviceAbsoluteVolumeBehavior(@NonNull android.media.AudioDeviceAttributes);
  }

  public final class AudioFocusRequest {
+46 −1
Original line number Diff line number Diff line
@@ -280,7 +280,7 @@ public class AudioDeviceVolumeManager {
                android.Manifest.permission.BLUETOOTH_PRIVILEGED })
        public void register(boolean register, @NonNull AudioDeviceAttributes device,
                @NonNull List<VolumeInfo> volumes, boolean handlesVolumeAdjustment,
                @AbsoluteDeviceVolumeBehavior int behavior) {
                @DeviceVolumeBehaviorState int behavior) {
            try {
                getService().registerDeviceVolumeDispatcherForAbsoluteVolume(register,
                        this, mPackageName,
@@ -568,6 +568,29 @@ public class AudioDeviceVolumeManager {
                handlesVolumeAdjustment, DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
    }

    /**
     * @hide
     * Resets any set volume behavior to
     * {@link AudioDeviceVolumeManager#DEVICE_VOLUME_BEHAVIOR_VARIABLE} for the given device
     */
    @TestApi
    @RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
            android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED,
            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
            android.Manifest.permission.BLUETOOTH_STACK})
    @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
    public void resetDeviceAbsoluteVolumeBehavior(@NonNull AudioDeviceAttributes device) {
        synchronized (mDeviceVolumeListenerLock) {
            if (mDeviceVolumeDispatcherStub == null) {
                mDeviceVolumeDispatcherStub = new DeviceVolumeDispatcherStub();
            }

            mDeviceVolumeDispatcherStub.register(/*register=*/false, device,
                    new ArrayList<>(), /*handlesVolumeAdjustment=*/false,
                    DEVICE_VOLUME_BEHAVIOR_VARIABLE);
        }
    }

    /**
     * Base method for configuring a device to use absolute volume behavior, or one of its variants.
     * See {@link AbsoluteDeviceVolumeBehavior} for a list of allowed behaviors.
@@ -681,6 +704,28 @@ public class AudioDeviceVolumeManager {
        }
    }

    /**
     * @hide
     * Notifies the audio framework that the volume on the given absolute volume device has changed.
     * @param vi the volume information, only stream-based volumes are supported
     * @param ada the device for which volume is to be modified. Must have been registered before
     *            as an absolute volume device
     */
    @SystemApi
    @RequiresPermission(anyOf = {
            Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED,
            Manifest.permission.BLUETOOTH_PRIVILEGED
    })
    @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
    public void notifyAbsoluteVolumeChanged(@NonNull VolumeInfo vi,
            @NonNull AudioDeviceAttributes ada) {
        try {
            getService().notifyAbsoluteVolumeChanged(vi, ada, mPackageName);
        } catch (RemoteException e) {
            e.rethrowFromSystemServer();
        }
    }

    /**
     * @hide
     * Returns the volume on the given audio device for the given volume information.
+4 −0
Original line number Diff line number Diff line
@@ -122,6 +122,10 @@ interface IAudioService {
    void setDeviceVolume(in VolumeInfo vi, in AudioDeviceAttributes ada,
            in String callingPackage);

    @EnforcePermission(anyOf = {"MODIFY_AUDIO_SETTINGS_PRIVILEGED", "BLUETOOTH_PRIVILEGED"})
    oneway void notifyAbsoluteVolumeChanged(in VolumeInfo vi, in AudioDeviceAttributes ada,
            in String callingPackage);

    @EnforcePermission(anyOf = {"MODIFY_AUDIO_ROUTING", "MODIFY_AUDIO_SETTINGS_PRIVILEGED"})
    VolumeInfo getDeviceVolume(in VolumeInfo vi, in AudioDeviceAttributes ada,
            in String callingPackage);
+78 −20
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import static android.media.AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL
import static android.media.AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_UNSET;
import static android.media.AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE;
import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES;
import static android.media.AudioManager.FLAG_ABSOLUTE_VOLUME;
import static android.media.AudioManager.RINGER_MODE_NORMAL;
import static android.media.AudioManager.RINGER_MODE_SILENT;
import static android.media.AudioManager.RINGER_MODE_VIBRATE;
@@ -4693,21 +4694,13 @@ public class AudioService extends IAudioService.Stub
    public void setDeviceVolume(@NonNull VolumeInfo vi, @NonNull AudioDeviceAttributes ada,
            @NonNull String callingPackage) {
        super.setDeviceVolume_enforcePermission();
        Objects.requireNonNull(vi);
        Objects.requireNonNull(ada);
        Objects.requireNonNull(callingPackage);
        if (!vi.hasStreamType()) {
            Log.e(TAG, "Unsupported non-stream type based VolumeInfo", new Exception());
        if (!isVolumeInfoValid(vi)) {
            return;
        }
        int index = vi.getVolumeIndex();
        if (index == VolumeInfo.INDEX_NOT_SET && !vi.hasMuteCommand()) {
            throw new IllegalArgumentException(
                    "changing device volume requires a volume index or mute command");
        }
        // force a cache clear to force reevaluating stream type to audio device selection
        // that can interfere with the sending of the VOLUME_CHANGED_ACTION intent
        mAudioSystem.clearRoutingCache();
@@ -4724,7 +4717,8 @@ public class AudioService extends IAudioService.Stub
                (unifyAbsoluteVolumeManagement() ? currDevAttr.equalTypeAddress(ada)
                        : currDevAttr.getInternalType() == ada.getInternalType()) || (vss == null);
        AudioService.sVolumeLogger.enqueue(new DeviceVolumeEvent(streamType, index, ada,
        AudioService.sVolumeLogger.enqueue(
                new DeviceVolumeEvent(streamType, vi.getVolumeIndex(), ada,
                        currDevAttr.getInternalType(), callingPackage, skipping));
        if (skipping) {
@@ -4732,22 +4726,86 @@ public class AudioService extends IAudioService.Stub
            return;
        }
        // TODO handle unmuting of current audio device
        setDeviceVolumeInt(vi, vss, ada, callingPackage, /*flags=*/0, /*changeMute=*/false,
                "setDeviceVolume");
    }
    @Override
    @android.annotation.EnforcePermission(anyOf = {MODIFY_AUDIO_SETTINGS_PRIVILEGED,
            BLUETOOTH_PRIVILEGED})
    /** @see AudioDeviceVolumeManager#notifyAbsoluteVolumeChanged(VolumeInfo, AudioDeviceAttributes)
     * Part of service interface, check permissions and parameters here
     * Note calling package is for logging purposes only, not to be trusted
     */
    public void notifyAbsoluteVolumeChanged(@NonNull VolumeInfo vi,
            @NonNull AudioDeviceAttributes ada,
            @NonNull String callingPackage) {
        super.notifyAbsoluteVolumeChanged_enforcePermission();
        Objects.requireNonNull(ada);
        Objects.requireNonNull(callingPackage);
        if (!isVolumeInfoValid(vi)) {
            return;
        }
        if (!isAbsoluteVolumeDevice(ada.getInternalType())) {
            Slog.e(TAG, "notifyAbsoluteVolumeChanged(): device " + ada
                    + " is not registered as an absolute volume device");
            return;
        }
        int streamType = replaceBtScoStreamWithVoiceCall(vi.getStreamType(),
                "notifyAbsoluteVolumeChanged");
        final VolumeStreamState vss = getVssForStream(streamType);
        if (vss == null) {
            Slog.e(TAG, "No VolumeStreamState for stream type " + streamType);
            return;
        }
        final int currDev = getDeviceForStream(streamType);
        AudioService.sVolumeLogger.enqueue(
                new DeviceVolumeEvent(streamType, vi.getVolumeIndex(), ada, callingPackage,
                        currDev == ada.getInternalType()));
        setDeviceVolumeInt(vi, vss, ada, callingPackage,
                FLAG_ABSOLUTE_VOLUME, currDev == ada.getInternalType(),
                "notifyAbsoluteVolumeChanged");
    }
    private static boolean isVolumeInfoValid(VolumeInfo vi) {
        Objects.requireNonNull(vi);
        // TODO(b/409634289): for now we only allow stream volume info commands, this needs to be
        //    adapted for volume groups.
        if (!vi.hasStreamType()) {
            Log.e(TAG, "Unsupported non-stream type based VolumeInfo", new Exception());
            return false;
        }
        int index = vi.getVolumeIndex();
        if (index == VolumeInfo.INDEX_NOT_SET && !vi.hasMuteCommand()) {
            throw new IllegalArgumentException(
                    "changing device volume requires a volume index or mute command");
        }
        return true;
    }
    private void setDeviceVolumeInt(VolumeInfo vi, VolumeStreamState vss, AudioDeviceAttributes ada,
            String callingPackage, int flags, boolean changeMute, String caller) {
        int streamType = vss.getStreamType();
        int index = vi.getVolumeIndex();
        // if a stream is not muted but the VolumeInfo is for muting, set the volume index
        // for the device to min volume
        if (vi.hasMuteCommand() && vi.isMuted() && !isStreamMute(streamType)) {
            setStreamVolumeWithAttributionInt(streamType,
                    vss.getMinIndex(),
                    /*flags*/ 0,
                    flags,
                    ada, callingPackage, null,
                    //TODO handle unmuting of current audio device
                    false /*canChangeMuteAndUpdateController*/);
                    changeMute);
            return;
        }
        AudioService.sVolumeLogger.enqueueAndLog("setDeviceVolume" + " from:" + callingPackage
                + " " + vi + " " + ada, EventLogger.Event.ALOGI, TAG);
        if (vi.getMinVolumeIndex() == VolumeInfo.INDEX_NOT_SET
                || vi.getMaxVolumeIndex() == VolumeInfo.INDEX_NOT_SET) {
            // assume index meant to be in stream type range, validate
@@ -4766,9 +4824,9 @@ public class AudioService extends IAudioService.Stub
                        /*dstMin*/ min, /*dstMax*/ max);
            }
        }
        setStreamVolumeWithAttributionInt(streamType, index, /*flags*/ 0,
        setStreamVolumeWithAttributionInt(streamType, index, flags,
                ada, callingPackage, null,
                false /*canChangeMuteAndUpdateController*/);
                changeMute);
    }
    /** Retain API for unsupported app usage */
Loading