• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.media;
18 
19 import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
20 import static android.media.audio.Flags.FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT;
21 
22 import static com.android.media.flags.Flags.FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL;
23 
24 import android.Manifest;
25 import android.annotation.CallbackExecutor;
26 import android.annotation.FlaggedApi;
27 import android.annotation.IntDef;
28 import android.annotation.NonNull;
29 import android.annotation.Nullable;
30 import android.annotation.RequiresPermission;
31 import android.annotation.SystemApi;
32 import android.annotation.TestApi;
33 import android.content.Context;
34 import android.os.IBinder;
35 import android.os.RemoteException;
36 import android.os.ServiceManager;
37 
38 import com.android.internal.annotations.GuardedBy;
39 
40 import java.lang.annotation.Retention;
41 import java.lang.annotation.RetentionPolicy;
42 import java.util.ArrayList;
43 import java.util.List;
44 import java.util.Objects;
45 import java.util.concurrent.Executor;
46 
47 /**
48  * @hide
49  * AudioDeviceVolumeManager provides access to audio device volume control.
50  */
51 @SystemApi
52 public class AudioDeviceVolumeManager {
53 
54     private static final String TAG = "AudioDeviceVolumeManager";
55 
56     /**
57      * @hide
58      * Volume behavior for an audio device that has no particular volume behavior set. Invalid as
59      * an argument to {@link #setDeviceVolumeBehavior(AudioDeviceAttributes, int)} and should not
60      * be returned by {@link #getDeviceVolumeBehavior(AudioDeviceAttributes)}.
61      */
62     public static final int DEVICE_VOLUME_BEHAVIOR_UNSET = -1;
63     /**
64      * @hide
65      * Volume behavior for an audio device where a software attenuation is applied
66      * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
67      */
68     @SystemApi
69     @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
70     public static final int DEVICE_VOLUME_BEHAVIOR_VARIABLE = 0;
71     /**
72      * @hide
73      * Volume behavior for an audio device where the volume is always set to provide no attenuation
74      *     nor gain (e.g. unit gain).
75      * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
76      */
77     @SystemApi
78     @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
79     public static final int DEVICE_VOLUME_BEHAVIOR_FULL = 1;
80     /**
81      * @hide
82      * Volume behavior for an audio device where the volume is either set to muted, or to provide
83      *     no attenuation nor gain (e.g. unit gain).
84      * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
85      */
86     @SystemApi
87     @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
88     public static final int DEVICE_VOLUME_BEHAVIOR_FIXED = 2;
89     /**
90      * @hide
91      * Volume behavior for an audio device where no software attenuation is applied, and
92      *     the volume is kept synchronized between the host and the device itself through a
93      *     device-specific protocol such as BT AVRCP.
94      * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
95      */
96     @SystemApi
97     @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
98     public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE = 3;
99     /**
100      * @hide
101      * Volume behavior for an audio device where no software attenuation is applied, and
102      *     the volume is kept synchronized between the host and the device itself through a
103      *     device-specific protocol (such as for hearing aids), based on the audio mode (e.g.
104      *     normal vs in phone call).
105      * @see AudioManager#setMode(int)
106      * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
107      */
108     @SystemApi
109     @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
110     public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE = 4;
111 
112     /**
113      * @hide
114      * A variant of {@link #DEVICE_VOLUME_BEHAVIOR_ABSOLUTE} where the host cannot reliably set
115      * the volume percentage of the audio device. Specifically, {@link AudioManager#setStreamVolume}
116      * will have no effect, or an unreliable effect.
117      */
118     @SystemApi
119     @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
120     public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY = 5;
121 
122     /** @hide */
123     @IntDef({
124             DEVICE_VOLUME_BEHAVIOR_VARIABLE,
125             DEVICE_VOLUME_BEHAVIOR_FULL,
126             DEVICE_VOLUME_BEHAVIOR_FIXED,
127             DEVICE_VOLUME_BEHAVIOR_ABSOLUTE,
128             DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE,
129             DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY,
130     })
131     @Retention(RetentionPolicy.SOURCE)
132     public @interface DeviceVolumeBehavior {}
133 
134     /** @hide */
135     @IntDef({
136             DEVICE_VOLUME_BEHAVIOR_UNSET,
137             DEVICE_VOLUME_BEHAVIOR_VARIABLE,
138             DEVICE_VOLUME_BEHAVIOR_FULL,
139             DEVICE_VOLUME_BEHAVIOR_FIXED,
140             DEVICE_VOLUME_BEHAVIOR_ABSOLUTE,
141             DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE,
142             DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY,
143     })
144     @Retention(RetentionPolicy.SOURCE)
145     public @interface DeviceVolumeBehaviorState {}
146 
147     /**
148      * Variants of absolute volume behavior that are set in for absolute volume management.
149      * @hide
150      */
151     @IntDef({
152             DEVICE_VOLUME_BEHAVIOR_ABSOLUTE,
153             DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY,
154     })
155     @Retention(RetentionPolicy.SOURCE)
156     public @interface AbsoluteDeviceVolumeBehavior {}
157 
158     /**
159      * @hide
160      * Throws IAE on an invalid volume behavior value
161      * @param volumeBehavior behavior value to check
162      */
enforceValidVolumeBehavior(int volumeBehavior)163     public static void enforceValidVolumeBehavior(int volumeBehavior) {
164         switch (volumeBehavior) {
165             case DEVICE_VOLUME_BEHAVIOR_VARIABLE:
166             case DEVICE_VOLUME_BEHAVIOR_FULL:
167             case DEVICE_VOLUME_BEHAVIOR_FIXED:
168             case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE:
169             case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE:
170             case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY:
171                 return;
172             default:
173                 throw new IllegalArgumentException("Illegal volume behavior " + volumeBehavior);
174         }
175     }
176 
177     /** @hide
178      * Indicates no special treatment in the handling of the volume adjustment */
179     public static final int ADJUST_MODE_NORMAL = 0;
180     /** @hide
181      * Indicates the start of a volume adjustment */
182     public static final int ADJUST_MODE_START = 1;
183     /** @hide
184      * Indicates the end of a volume adjustment */
185     public static final int ADJUST_MODE_END = 2;
186 
187     /** @hide */
188     @IntDef(flag = false, prefix = "ADJUST_MODE", value = {
189             ADJUST_MODE_NORMAL,
190             ADJUST_MODE_START,
191             ADJUST_MODE_END}
192     )
193     /** @hide */
194     @Retention(RetentionPolicy.SOURCE)
195     public @interface VolumeAdjustmentMode {}
196 
197     private static IAudioService sService;
198 
199     private final @NonNull String mPackageName;
200 
201     /**
202      * @hide
203      * Constructor
204      * @param context the Context for the device volume operations
205      */
AudioDeviceVolumeManager(@onNull Context context)206     public AudioDeviceVolumeManager(@NonNull Context context) {
207         Objects.requireNonNull(context);
208         mPackageName = context.getApplicationContext().getOpPackageName();
209     }
210 
211     /**
212      * @hide
213      * Interface to receive volume changes on a device that behaves in absolute volume mode.
214      * @see #setDeviceAbsoluteMultiVolumeBehavior(AudioDeviceAttributes, List, boolean, Executor,
215      *          OnAudioDeviceVolumeChangedListener)
216      * @see #setDeviceAbsoluteVolumeBehavior(AudioDeviceAttributes, VolumeInfo, boolean, Executor,
217      *          OnAudioDeviceVolumeChangedListener)
218      */
219     @SystemApi(client = MODULE_LIBRARIES)
220     @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
221     public interface OnAudioDeviceVolumeChangedListener {
222         /**
223          * Called the device for the given audio device has changed.
224          * @param device the audio device whose volume has changed
225          * @param vol the new volume for the device
226          */
onAudioDeviceVolumeChanged( @onNull AudioDeviceAttributes device, @NonNull VolumeInfo vol)227         void onAudioDeviceVolumeChanged(
228                 @NonNull AudioDeviceAttributes device,
229                 @NonNull VolumeInfo vol);
230 
231         /**
232          * Called when the volume for the given audio device has been adjusted.
233          * @param device the audio device whose volume has been adjusted
234          * @param vol the volume info for the device
235          * @param direction the direction of the adjustment
236          * @param mode the volume adjustment mode
237          */
onAudioDeviceVolumeAdjusted( @onNull AudioDeviceAttributes device, @NonNull VolumeInfo vol, @AudioManager.VolumeAdjustment int direction, @VolumeAdjustmentMode int mode)238         void onAudioDeviceVolumeAdjusted(
239                 @NonNull AudioDeviceAttributes device,
240                 @NonNull VolumeInfo vol,
241                 @AudioManager.VolumeAdjustment int direction,
242                 @VolumeAdjustmentMode int mode);
243     }
244 
245     /** @hide */
246     static class ListenerInfo {
247         final @NonNull OnAudioDeviceVolumeChangedListener mListener;
248         final @NonNull Executor mExecutor;
249         final @NonNull AudioDeviceAttributes mDevice;
250         final @NonNull boolean mHandlesVolumeAdjustment;
251 
ListenerInfo(@onNull OnAudioDeviceVolumeChangedListener listener, @NonNull Executor exe, @NonNull AudioDeviceAttributes device, boolean handlesVolumeAdjustment)252         ListenerInfo(@NonNull OnAudioDeviceVolumeChangedListener listener, @NonNull Executor exe,
253                 @NonNull AudioDeviceAttributes device, boolean handlesVolumeAdjustment) {
254             mListener = listener;
255             mExecutor = exe;
256             mDevice = device;
257             mHandlesVolumeAdjustment = handlesVolumeAdjustment;
258         }
259     }
260 
261     private final Object mDeviceVolumeListenerLock = new Object();
262     /**
263      * List of listeners for volume changes, the associated device, and their associated Executor.
264      * List is lazy-initialized on first registration
265      */
266     @GuardedBy("mDeviceVolumeListenerLock")
267     private @Nullable ArrayList<ListenerInfo> mDeviceVolumeListeners;
268 
269     @GuardedBy("mDeviceVolumeListenerLock")
270     private DeviceVolumeDispatcherStub mDeviceVolumeDispatcherStub;
271 
272     /** @hide */
273     final class DeviceVolumeDispatcherStub extends IAudioDeviceVolumeDispatcher.Stub {
274         /**
275          * Register / unregister the stub
276          * @param register true for registering, false for unregistering
277          * @param device device for which volume is monitored
278          */
279         @RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
280                 android.Manifest.permission.BLUETOOTH_PRIVILEGED })
register(boolean register, @NonNull AudioDeviceAttributes device, @NonNull List<VolumeInfo> volumes, boolean handlesVolumeAdjustment, @AbsoluteDeviceVolumeBehavior int behavior)281         public void register(boolean register, @NonNull AudioDeviceAttributes device,
282                 @NonNull List<VolumeInfo> volumes, boolean handlesVolumeAdjustment,
283                 @AbsoluteDeviceVolumeBehavior int behavior) {
284             try {
285                 getService().registerDeviceVolumeDispatcherForAbsoluteVolume(register,
286                         this, mPackageName,
287                         Objects.requireNonNull(device), Objects.requireNonNull(volumes),
288                         handlesVolumeAdjustment, behavior);
289             } catch (RemoteException e) {
290                 e.rethrowFromSystemServer();
291             }
292         }
293 
294         @Override
dispatchDeviceVolumeChanged( @onNull AudioDeviceAttributes device, @NonNull VolumeInfo vol)295         public void dispatchDeviceVolumeChanged(
296                 @NonNull AudioDeviceAttributes device, @NonNull VolumeInfo vol) {
297             final ArrayList<ListenerInfo> volumeListeners;
298             synchronized (mDeviceVolumeListenerLock) {
299                 volumeListeners = (ArrayList<ListenerInfo>) mDeviceVolumeListeners.clone();
300             }
301             for (ListenerInfo listenerInfo : volumeListeners) {
302                 if (listenerInfo.mDevice.equalTypeAddress(device)) {
303                     listenerInfo.mExecutor.execute(
304                             () -> listenerInfo.mListener.onAudioDeviceVolumeChanged(device, vol));
305                 }
306             }
307         }
308 
309         @Override
dispatchDeviceVolumeAdjusted( @onNull AudioDeviceAttributes device, @NonNull VolumeInfo vol, int direction, int mode)310         public void dispatchDeviceVolumeAdjusted(
311                 @NonNull AudioDeviceAttributes device, @NonNull VolumeInfo vol, int direction,
312                 int mode) {
313             final ArrayList<ListenerInfo> volumeListeners;
314             synchronized (mDeviceVolumeListenerLock) {
315                 volumeListeners = (ArrayList<ListenerInfo>) mDeviceVolumeListeners.clone();
316             }
317             for (ListenerInfo listenerInfo : volumeListeners) {
318                 if (listenerInfo.mDevice.equalTypeAddress(device)) {
319                     listenerInfo.mExecutor.execute(
320                             () -> listenerInfo.mListener.onAudioDeviceVolumeAdjusted(device, vol,
321                                     direction, mode));
322                 }
323             }
324         }
325     }
326 
327     /**
328      * @hide
329      * Sets the volume behavior for an audio output device.
330      * @see #DEVICE_VOLUME_BEHAVIOR_VARIABLE
331      * @see #DEVICE_VOLUME_BEHAVIOR_FULL
332      * @see #DEVICE_VOLUME_BEHAVIOR_FIXED
333      * @see #DEVICE_VOLUME_BEHAVIOR_ABSOLUTE
334      * @see #DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE
335      * @param device the device to be affected
336      * @param deviceVolumeBehavior one of the device behaviors
337      */
338     @SystemApi
339     @RequiresPermission(anyOf = {
340             Manifest.permission.MODIFY_AUDIO_ROUTING,
341             Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
342     })
343     @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
setDeviceVolumeBehavior(@onNull AudioDeviceAttributes device, @DeviceVolumeBehavior int deviceVolumeBehavior)344     public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
345             @DeviceVolumeBehavior int deviceVolumeBehavior) {
346         // verify arguments (validity of device type is enforced in server)
347         Objects.requireNonNull(device);
348         enforceValidVolumeBehavior(deviceVolumeBehavior);
349         // communicate with service
350         final IAudioService service = getService();
351         try {
352             service.setDeviceVolumeBehavior(device, deviceVolumeBehavior, mPackageName);
353         } catch (RemoteException e) {
354             throw e.rethrowFromSystemServer();
355         }
356     }
357 
358     /**
359      * @hide
360      * Returns the volume device behavior for the given audio device
361      * @param device the audio device
362      * @return the volume behavior for the device
363      */
364     @SystemApi
365     @RequiresPermission(anyOf = {
366             Manifest.permission.MODIFY_AUDIO_ROUTING,
367             Manifest.permission.QUERY_AUDIO_STATE,
368             Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
369     })
370     @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
getDeviceVolumeBehavior( @onNull AudioDeviceAttributes device)371     public @DeviceVolumeBehavior int getDeviceVolumeBehavior(
372             @NonNull AudioDeviceAttributes device) {
373         // verify arguments (validity of device type is enforced in server)
374         Objects.requireNonNull(device);
375         // communicate with service
376         final IAudioService service = getService();
377         try {
378             return service.getDeviceVolumeBehavior(device);
379         } catch (RemoteException e) {
380             throw e.rethrowFromSystemServer();
381         }
382     }
383 
384     /**
385      * @hide
386      * Returns {@code true} if the volume device behavior is {@link #DEVICE_VOLUME_BEHAVIOR_FULL}.
387      */
388     @TestApi
389     @RequiresPermission(anyOf = {
390             Manifest.permission.MODIFY_AUDIO_ROUTING,
391             Manifest.permission.QUERY_AUDIO_STATE,
392             Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
393     })
394     @SuppressWarnings("UnflaggedApi")  // @TestApi without associated feature.
isFullVolumeDevice()395     public boolean isFullVolumeDevice() {
396         final AudioAttributes attributes = new AudioAttributes.Builder()
397                 .setUsage(AudioAttributes.USAGE_MEDIA)
398                 .build();
399         List<AudioDeviceAttributes> devices;
400         final IAudioService service = getService();
401         try {
402             devices = service.getDevicesForAttributes(attributes);
403         } catch (RemoteException e) {
404             throw e.rethrowFromSystemServer();
405         }
406 
407         for (AudioDeviceAttributes device : devices) {
408             if (getDeviceVolumeBehavior(device) == DEVICE_VOLUME_BEHAVIOR_FULL) {
409                 return true;
410             }
411         }
412         return false;
413     }
414 
415     /**
416      * @hide
417      * Configures a device to use absolute volume model, and registers a listener for receiving
418      * volume updates to apply on that device
419      * @param device the audio device set to absolute volume mode
420      * @param volume the type of volume this device responds to
421      * @param executor the Executor used for receiving volume updates through the listener
422      * @param vclistener the callback for volume updates
423      */
424     @SystemApi(client = MODULE_LIBRARIES)
425     @RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
426             android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED,
427             android.Manifest.permission.BLUETOOTH_PRIVILEGED,
428             android.Manifest.permission.BLUETOOTH_STACK})
429     @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
setDeviceAbsoluteVolumeBehavior( @onNull AudioDeviceAttributes device, @NonNull VolumeInfo volume, @NonNull @CallbackExecutor Executor executor, @NonNull OnAudioDeviceVolumeChangedListener vclistener)430     public void setDeviceAbsoluteVolumeBehavior(
431             @NonNull AudioDeviceAttributes device,
432             @NonNull VolumeInfo volume,
433             @NonNull @CallbackExecutor Executor executor,
434             @NonNull OnAudioDeviceVolumeChangedListener vclistener) {
435         setDeviceAbsoluteVolumeBehavior(device, volume, /*handlesVolumeAdjustment=*/false, executor,
436                 vclistener);
437     }
438 
439     /**
440      * @hide
441      * Configures a device to use absolute volume model, and registers a listener for receiving
442      * volume updates to apply on that device
443      * @param device the audio device set to absolute volume mode
444      * @param volume the type of volume this device responds to
445      * @param handlesVolumeAdjustment whether the controller handles volume adjustments separately
446      * from volume changes. If true, adjustments from {@link AudioManager#adjustStreamVolume}
447      * will be sent via {@link OnAudioDeviceVolumeChangedListener#onAudioDeviceVolumeAdjusted}.
448      * @param executor the Executor used for receiving volume updates through the listener
449      * @param vclistener the callback for volume updates
450      */
451     @RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
452             android.Manifest.permission.BLUETOOTH_PRIVILEGED})
setDeviceAbsoluteVolumeBehavior( @onNull AudioDeviceAttributes device, @NonNull VolumeInfo volume, boolean handlesVolumeAdjustment, @NonNull @CallbackExecutor Executor executor, @NonNull OnAudioDeviceVolumeChangedListener vclistener)453     public void setDeviceAbsoluteVolumeBehavior(
454             @NonNull AudioDeviceAttributes device,
455             @NonNull VolumeInfo volume,
456             boolean handlesVolumeAdjustment,
457             @NonNull @CallbackExecutor Executor executor,
458             @NonNull OnAudioDeviceVolumeChangedListener vclistener) {
459         final ArrayList<VolumeInfo> volumes = new ArrayList<>(1);
460         volumes.add(volume);
461         setDeviceAbsoluteMultiVolumeBehavior(device, volumes, handlesVolumeAdjustment, executor,
462                 vclistener);
463     }
464 
465     /**
466      * @hide
467      * Configures a device to use absolute volume model applied to different volume types, and
468      * registers a listener for receiving volume updates to apply on that device
469      * @param device the audio device set to absolute multi-volume mode
470      * @param volumes the list of volumes the given device responds to
471      * @param executor the Executor used for receiving volume updates through the listener
472      * @param vclistener the callback for volume updates
473      */
474     @SystemApi(client = MODULE_LIBRARIES)
475     @RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
476             android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED,
477             android.Manifest.permission.BLUETOOTH_PRIVILEGED,
478             android.Manifest.permission.BLUETOOTH_STACK})
479     @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
setDeviceAbsoluteMultiVolumeBehavior( @onNull AudioDeviceAttributes device, @NonNull List<VolumeInfo> volumes, @NonNull @CallbackExecutor Executor executor, @NonNull OnAudioDeviceVolumeChangedListener vclistener)480     public void setDeviceAbsoluteMultiVolumeBehavior(
481             @NonNull AudioDeviceAttributes device,
482             @NonNull List<VolumeInfo> volumes,
483             @NonNull @CallbackExecutor Executor executor,
484             @NonNull OnAudioDeviceVolumeChangedListener vclistener) {
485         setDeviceAbsoluteMultiVolumeBehavior(device, volumes, /*handlesVolumeAdjustment=*/false,
486                 executor, vclistener);
487     }
488 
489     /**
490      * @hide
491      * Configures a device to use absolute volume model applied to different volume types, and
492      * registers a listener for receiving volume updates to apply on that device
493      * @param device the audio device set to absolute multi-volume mode
494      * @param volumes the list of volumes the given device responds to
495      * @param handlesVolumeAdjustment whether the controller handles volume adjustments separately
496      * from volume changes. If true, adjustments from {@link AudioManager#adjustStreamVolume}
497      * will be sent via {@link OnAudioDeviceVolumeChangedListener#onAudioDeviceVolumeAdjusted}.
498      * @param executor the Executor used for receiving volume updates through the listener
499      * @param vclistener the callback for volume updates
500      */
501     @RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
502             android.Manifest.permission.BLUETOOTH_PRIVILEGED})
setDeviceAbsoluteMultiVolumeBehavior( @onNull AudioDeviceAttributes device, @NonNull List<VolumeInfo> volumes, boolean handlesVolumeAdjustment, @NonNull @CallbackExecutor Executor executor, @NonNull OnAudioDeviceVolumeChangedListener vclistener)503     public void setDeviceAbsoluteMultiVolumeBehavior(
504             @NonNull AudioDeviceAttributes device,
505             @NonNull List<VolumeInfo> volumes,
506             boolean handlesVolumeAdjustment,
507             @NonNull @CallbackExecutor Executor executor,
508             @NonNull OnAudioDeviceVolumeChangedListener vclistener) {
509         baseSetDeviceAbsoluteMultiVolumeBehavior(device, volumes, executor, vclistener,
510                 handlesVolumeAdjustment, DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
511     }
512 
513     /**
514      * @hide
515      * Configures a device to use absolute volume model, and registers a listener for receiving
516      * volume updates to apply on that device.
517      *
518      * <p>Should be used instead of {@link #setDeviceAbsoluteVolumeBehavior} when there is no
519      * reliable way to set the device's volume to a percentage.
520      *
521      * @param device the audio device set to absolute volume mode
522      * @param volume the type of volume this device responds to
523      * @param handlesVolumeAdjustment whether the controller handles volume adjustments separately
524      * from volume changes. If true, adjustments from {@link AudioManager#adjustStreamVolume}
525      * will be sent via {@link OnAudioDeviceVolumeChangedListener#onAudioDeviceVolumeAdjusted}.
526      * @param executor the Executor used for receiving volume updates through the listener
527      * @param vclistener the callback for volume updates
528      */
529     @RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
530             android.Manifest.permission.BLUETOOTH_PRIVILEGED })
setDeviceAbsoluteVolumeAdjustOnlyBehavior( @onNull AudioDeviceAttributes device, @NonNull VolumeInfo volume, boolean handlesVolumeAdjustment, @NonNull @CallbackExecutor Executor executor, @NonNull OnAudioDeviceVolumeChangedListener vclistener)531     public void setDeviceAbsoluteVolumeAdjustOnlyBehavior(
532             @NonNull AudioDeviceAttributes device,
533             @NonNull VolumeInfo volume,
534             boolean handlesVolumeAdjustment,
535             @NonNull @CallbackExecutor Executor executor,
536             @NonNull OnAudioDeviceVolumeChangedListener vclistener) {
537         final ArrayList<VolumeInfo> volumes = new ArrayList<>(1);
538         volumes.add(volume);
539         setDeviceAbsoluteMultiVolumeAdjustOnlyBehavior(device, volumes, handlesVolumeAdjustment,
540                 executor, vclistener);
541     }
542 
543     /**
544      * @hide
545      * Configures a device to use absolute volume model applied to different volume types, and
546      * registers a listener for receiving volume updates to apply on that device.
547      *
548      * <p>Should be used instead of {@link #setDeviceAbsoluteMultiVolumeBehavior} when there is
549      * no reliable way to set the device's volume to a percentage.
550      *
551      * @param device the audio device set to absolute multi-volume mode
552      * @param volumes the list of volumes the given device responds to
553      * @param handlesVolumeAdjustment whether the controller handles volume adjustments separately
554      * from volume changes. If true, adjustments from {@link AudioManager#adjustStreamVolume}
555      * will be sent via {@link OnAudioDeviceVolumeChangedListener#onAudioDeviceVolumeAdjusted}.
556      * @param executor the Executor used for receiving volume updates through the listener
557      * @param vclistener the callback for volume updates
558      */
559     @RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
560             android.Manifest.permission.BLUETOOTH_PRIVILEGED })
setDeviceAbsoluteMultiVolumeAdjustOnlyBehavior( @onNull AudioDeviceAttributes device, @NonNull List<VolumeInfo> volumes, boolean handlesVolumeAdjustment, @NonNull @CallbackExecutor Executor executor, @NonNull OnAudioDeviceVolumeChangedListener vclistener)561     public void setDeviceAbsoluteMultiVolumeAdjustOnlyBehavior(
562             @NonNull AudioDeviceAttributes device,
563             @NonNull List<VolumeInfo> volumes,
564             boolean handlesVolumeAdjustment,
565             @NonNull @CallbackExecutor Executor executor,
566             @NonNull OnAudioDeviceVolumeChangedListener vclistener) {
567         baseSetDeviceAbsoluteMultiVolumeBehavior(device, volumes, executor, vclistener,
568                 handlesVolumeAdjustment, DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
569     }
570 
571     /**
572      * Base method for configuring a device to use absolute volume behavior, or one of its variants.
573      * See {@link AbsoluteDeviceVolumeBehavior} for a list of allowed behaviors.
574      *
575      * @param behavior the variant of absolute device volume behavior to adopt
576      */
577     @RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
578             android.Manifest.permission.BLUETOOTH_PRIVILEGED })
baseSetDeviceAbsoluteMultiVolumeBehavior( @onNull AudioDeviceAttributes device, @NonNull List<VolumeInfo> volumes, @NonNull @CallbackExecutor Executor executor, @NonNull OnAudioDeviceVolumeChangedListener vclistener, boolean handlesVolumeAdjustment, @AbsoluteDeviceVolumeBehavior int behavior)579     private void baseSetDeviceAbsoluteMultiVolumeBehavior(
580             @NonNull AudioDeviceAttributes device,
581             @NonNull List<VolumeInfo> volumes,
582             @NonNull @CallbackExecutor Executor executor,
583             @NonNull OnAudioDeviceVolumeChangedListener vclistener,
584             boolean handlesVolumeAdjustment,
585             @AbsoluteDeviceVolumeBehavior int behavior) {
586         Objects.requireNonNull(device);
587         Objects.requireNonNull(volumes);
588         Objects.requireNonNull(executor);
589         Objects.requireNonNull(vclistener);
590 
591         final ListenerInfo listenerInfo = new ListenerInfo(
592                 vclistener, executor, device, handlesVolumeAdjustment);
593         synchronized (mDeviceVolumeListenerLock) {
594             if (mDeviceVolumeListeners == null) {
595                 mDeviceVolumeListeners = new ArrayList<>();
596             }
597             if (mDeviceVolumeListeners.size() == 0) {
598                 if (mDeviceVolumeDispatcherStub == null) {
599                     mDeviceVolumeDispatcherStub = new DeviceVolumeDispatcherStub();
600                 }
601             } else {
602                 mDeviceVolumeListeners.removeIf(info -> info.mDevice.equalTypeAddress(device));
603             }
604             mDeviceVolumeListeners.add(listenerInfo);
605             mDeviceVolumeDispatcherStub.register(true, device, volumes, handlesVolumeAdjustment,
606                     behavior);
607         }
608     }
609 
610     /**
611      * Manages the OnDeviceVolumeBehaviorChangedListener listeners and
612      * DeviceVolumeBehaviorDispatcherStub
613      */
614     private final CallbackUtil.LazyListenerManager<OnDeviceVolumeBehaviorChangedListener>
615             mDeviceVolumeBehaviorChangedListenerMgr = new CallbackUtil.LazyListenerManager();
616 
617     /**
618      * @hide
619      * Interface definition of a callback to be invoked when the volume behavior of an audio device
620      * is updated.
621      */
622     public interface OnDeviceVolumeBehaviorChangedListener {
623         /**
624          * Called on the listener to indicate that the volume behavior of a device has changed.
625          * @param device the audio device whose volume behavior changed
626          * @param volumeBehavior the new volume behavior of the audio device
627          */
onDeviceVolumeBehaviorChanged( @onNull AudioDeviceAttributes device, @DeviceVolumeBehavior int volumeBehavior)628         void onDeviceVolumeBehaviorChanged(
629                 @NonNull AudioDeviceAttributes device,
630                 @DeviceVolumeBehavior int volumeBehavior);
631     }
632 
633     /**
634      * @hide
635      * Adds a listener for being notified of changes to any device's volume behavior.
636      * @throws SecurityException if the caller doesn't hold the required permission
637      */
638     @RequiresPermission(anyOf = {
639             android.Manifest.permission.MODIFY_AUDIO_ROUTING,
640             android.Manifest.permission.QUERY_AUDIO_STATE
641     })
addOnDeviceVolumeBehaviorChangedListener( @onNull @allbackExecutor Executor executor, @NonNull OnDeviceVolumeBehaviorChangedListener listener)642     public void addOnDeviceVolumeBehaviorChangedListener(
643             @NonNull @CallbackExecutor Executor executor,
644             @NonNull OnDeviceVolumeBehaviorChangedListener listener)
645             throws SecurityException {
646         mDeviceVolumeBehaviorChangedListenerMgr.addListener(executor, listener,
647                 "addOnDeviceVolumeBehaviorChangedListener",
648                 () -> new DeviceVolumeBehaviorDispatcherStub());
649     }
650 
651     /**
652      * @hide
653      * Removes a previously added listener of changes to device volume behavior.
654      */
655     @RequiresPermission(anyOf = {
656             android.Manifest.permission.MODIFY_AUDIO_ROUTING,
657             android.Manifest.permission.QUERY_AUDIO_STATE
658     })
removeOnDeviceVolumeBehaviorChangedListener( @onNull OnDeviceVolumeBehaviorChangedListener listener)659     public void removeOnDeviceVolumeBehaviorChangedListener(
660             @NonNull OnDeviceVolumeBehaviorChangedListener listener) {
661         mDeviceVolumeBehaviorChangedListenerMgr.removeListener(listener,
662                 "removeOnDeviceVolumeBehaviorChangedListener");
663     }
664 
665     /**
666      * @hide
667      * Sets the volume on the given audio device
668      * @param vi the volume information, only stream-based volumes are supported
669      * @param ada the device for which volume is to be modified
670      */
671     @SystemApi
672     @RequiresPermission(anyOf = {
673             Manifest.permission.MODIFY_AUDIO_ROUTING,
674             Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
675     })
setDeviceVolume(@onNull VolumeInfo vi, @NonNull AudioDeviceAttributes ada)676     public void setDeviceVolume(@NonNull VolumeInfo vi, @NonNull AudioDeviceAttributes ada) {
677         try {
678             getService().setDeviceVolume(vi, ada, mPackageName);
679         } catch (RemoteException e) {
680             e.rethrowFromSystemServer();
681         }
682     }
683 
684     /**
685      * @hide
686      * Returns the volume on the given audio device for the given volume information.
687      * For instance if using a {@link VolumeInfo} configured for {@link AudioManager#STREAM_ALARM},
688      * it will return the alarm volume. When no volume index has ever been set for the given
689      * device, the default volume will be returned (the volume setting that would have been
690      * applied if playback for that use case had started).
691      * @param vi the volume information, only stream-based volumes are supported. Information
692      *           other than the stream type is ignored.
693      * @param ada the device for which volume is to be retrieved
694      */
695     @SystemApi
696     @RequiresPermission(anyOf = {
697             Manifest.permission.MODIFY_AUDIO_ROUTING,
698             Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
699     })
getDeviceVolume(@onNull VolumeInfo vi, @NonNull AudioDeviceAttributes ada)700     public @NonNull VolumeInfo getDeviceVolume(@NonNull VolumeInfo vi,
701             @NonNull AudioDeviceAttributes ada) {
702         try {
703             return getService().getDeviceVolume(vi, ada, mPackageName);
704         } catch (RemoteException e) {
705             e.rethrowFromSystemServer();
706         }
707         return VolumeInfo.getDefaultVolumeInfo();
708     }
709 
710     /**
711      * @hide
712      * Sets the input gain index for a particular AudioDeviceAttributes.
713      * TODO(b/364923030): create InputVolumeInfo on top of VolumeInfo rather than using index to
714      * handle volume information, to solve issues e.g. gain index ranges might be different for
715      * different categories of devices.
716      */
717     @FlaggedApi(FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL)
718     @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
setInputGainIndex(@onNull AudioDeviceAttributes ada, int index)719     public void setInputGainIndex(@NonNull AudioDeviceAttributes ada, int index) {
720         try {
721             getService().setInputGainIndex(ada, index);
722         } catch (RemoteException e) {
723             throw e.rethrowFromSystemServer();
724         }
725     }
726 
727     /**
728      * @hide
729      * Gets the input gain index for a particular AudioDeviceAttributes.
730      */
731     @FlaggedApi(FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL)
732     @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
getInputGainIndex(@onNull AudioDeviceAttributes ada)733     public int getInputGainIndex(@NonNull AudioDeviceAttributes ada) {
734         try {
735             return getService().getInputGainIndex(ada);
736         } catch (RemoteException e) {
737             throw e.rethrowFromSystemServer();
738         }
739     }
740 
741     /**
742      * @hide
743      * Gets the maximum input gain index for input device.
744      */
745     @FlaggedApi(FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL)
746     @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
getMaxInputGainIndex()747     public int getMaxInputGainIndex() {
748         try {
749             return getService().getMaxInputGainIndex();
750         } catch (RemoteException e) {
751             throw e.rethrowFromSystemServer();
752         }
753     }
754 
755     /**
756      * @hide
757      * Gets the minimum input gain index for input device.
758      */
759     @FlaggedApi(FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL)
760     @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
getMinInputGainIndex()761     public int getMinInputGainIndex() {
762         try {
763             return getService().getMinInputGainIndex();
764         } catch (RemoteException e) {
765             throw e.rethrowFromSystemServer();
766         }
767     }
768 
769     /**
770      * @hide
771      * Indicates if an input device does not support input gain control.
772      *     <p>The following APIs have no effect when input gain is fixed:
773      *     <ul>
774      *       <li>{@link #setInputGainIndex(AudioDeviceAttributes, int)}
775      *     </ul>
776      */
777     @FlaggedApi(FLAG_ENABLE_AUDIO_INPUT_DEVICE_ROUTING_AND_VOLUME_CONTROL)
778     @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
isInputGainFixed(@onNull AudioDeviceAttributes ada)779     public boolean isInputGainFixed(@NonNull AudioDeviceAttributes ada) {
780         try {
781             return getService().isInputGainFixed(ada);
782         } catch (RemoteException e) {
783             throw e.rethrowFromSystemServer();
784         }
785     }
786 
787     /**
788      * @hide
789      * Return human-readable name for volume behavior
790      * @param behavior one of the volume behaviors defined in AudioManager
791      * @return a string for the given behavior
792      */
volumeBehaviorName(@eviceVolumeBehavior int behavior)793     public static String volumeBehaviorName(@DeviceVolumeBehavior int behavior) {
794         switch (behavior) {
795             case DEVICE_VOLUME_BEHAVIOR_VARIABLE:
796                 return "DEVICE_VOLUME_BEHAVIOR_VARIABLE";
797             case DEVICE_VOLUME_BEHAVIOR_FULL:
798                 return "DEVICE_VOLUME_BEHAVIOR_FULL";
799             case DEVICE_VOLUME_BEHAVIOR_FIXED:
800                 return "DEVICE_VOLUME_BEHAVIOR_FIXED";
801             case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE:
802                 return "DEVICE_VOLUME_BEHAVIOR_ABSOLUTE";
803             case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE:
804                 return "DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE";
805             case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY:
806                 return "DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY";
807             default:
808                 return "invalid volume behavior " + behavior;
809         }
810     }
811 
812     private final class DeviceVolumeBehaviorDispatcherStub
813             extends IDeviceVolumeBehaviorDispatcher.Stub implements CallbackUtil.DispatcherStub {
register(boolean register)814         public void register(boolean register) {
815             try {
816                 getService().registerDeviceVolumeBehaviorDispatcher(register, this);
817             } catch (RemoteException e) {
818                 e.rethrowFromSystemServer();
819             }
820         }
821 
822         @Override
dispatchDeviceVolumeBehaviorChanged(@onNull AudioDeviceAttributes device, @DeviceVolumeBehavior int volumeBehavior)823         public void dispatchDeviceVolumeBehaviorChanged(@NonNull AudioDeviceAttributes device,
824                 @DeviceVolumeBehavior int volumeBehavior) {
825             mDeviceVolumeBehaviorChangedListenerMgr.callListeners((listener) ->
826                     listener.onDeviceVolumeBehaviorChanged(device, volumeBehavior));
827         }
828     }
829 
getService()830     private static IAudioService getService() {
831         if (sService != null) {
832             return sService;
833         }
834         IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
835         sService = IAudioService.Stub.asInterface(b);
836         return sService;
837     }
838 }
839