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 android.Manifest;
20 import android.annotation.CallbackExecutor;
21 import android.annotation.IntDef;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.RequiresPermission;
25 import android.annotation.SystemApi;
26 import android.content.Context;
27 import android.os.IBinder;
28 import android.os.RemoteException;
29 import android.os.ServiceManager;
30 
31 import com.android.internal.annotations.GuardedBy;
32 
33 import java.lang.annotation.Retention;
34 import java.lang.annotation.RetentionPolicy;
35 import java.util.ArrayList;
36 import java.util.List;
37 import java.util.Objects;
38 import java.util.concurrent.Executor;
39 
40 /**
41  * @hide
42  * AudioDeviceVolumeManager provides access to audio device volume control.
43  */
44 @SystemApi
45 public class AudioDeviceVolumeManager {
46 
47     private static final String TAG = "AudioDeviceVolumeManager";
48 
49     /** @hide
50      * Indicates no special treatment in the handling of the volume adjustment */
51     public static final int ADJUST_MODE_NORMAL = 0;
52     /** @hide
53      * Indicates the start of a volume adjustment */
54     public static final int ADJUST_MODE_START = 1;
55     /** @hide
56      * Indicates the end of a volume adjustment */
57     public static final int ADJUST_MODE_END = 2;
58 
59     /** @hide */
60     @IntDef(flag = false, prefix = "ADJUST_MODE", value = {
61             ADJUST_MODE_NORMAL,
62             ADJUST_MODE_START,
63             ADJUST_MODE_END}
64     )
65     /** @hide */
66     @Retention(RetentionPolicy.SOURCE)
67     public @interface VolumeAdjustmentMode {}
68 
69     private static IAudioService sService;
70 
71     private final @NonNull String mPackageName;
72 
73     /**
74      * @hide
75      * Constructor
76      * @param context the Context for the device volume operations
77      */
AudioDeviceVolumeManager(@onNull Context context)78     public AudioDeviceVolumeManager(@NonNull Context context) {
79         Objects.requireNonNull(context);
80         mPackageName = context.getApplicationContext().getOpPackageName();
81     }
82 
83     /**
84      * @hide
85      * Interface to receive volume changes on a device that behaves in absolute volume mode.
86      * @see #setDeviceAbsoluteMultiVolumeBehavior(AudioDeviceAttributes, List, Executor,
87      *         OnAudioDeviceVolumeChangeListener)
88      * @see #setDeviceAbsoluteVolumeBehavior(AudioDeviceAttributes, VolumeInfo, Executor,
89      *         OnAudioDeviceVolumeChangeListener)
90      */
91     public interface OnAudioDeviceVolumeChangedListener {
92         /**
93          * Called the device for the given audio device has changed.
94          * @param device the audio device whose volume has changed
95          * @param vol the new volume for the device
96          */
onAudioDeviceVolumeChanged( @onNull AudioDeviceAttributes device, @NonNull VolumeInfo vol)97         void onAudioDeviceVolumeChanged(
98                 @NonNull AudioDeviceAttributes device,
99                 @NonNull VolumeInfo vol);
100 
101         /**
102          * Called when the volume for the given audio device has been adjusted.
103          * @param device the audio device whose volume has been adjusted
104          * @param vol the volume info for the device
105          * @param direction the direction of the adjustment
106          * @param mode the volume adjustment mode
107          */
onAudioDeviceVolumeAdjusted( @onNull AudioDeviceAttributes device, @NonNull VolumeInfo vol, @AudioManager.VolumeAdjustment int direction, @VolumeAdjustmentMode int mode)108         void onAudioDeviceVolumeAdjusted(
109                 @NonNull AudioDeviceAttributes device,
110                 @NonNull VolumeInfo vol,
111                 @AudioManager.VolumeAdjustment int direction,
112                 @VolumeAdjustmentMode int mode);
113     }
114 
115     /** @hide */
116     static class ListenerInfo {
117         final @NonNull OnAudioDeviceVolumeChangedListener mListener;
118         final @NonNull Executor mExecutor;
119         final @NonNull AudioDeviceAttributes mDevice;
120         final @NonNull boolean mHandlesVolumeAdjustment;
121 
ListenerInfo(@onNull OnAudioDeviceVolumeChangedListener listener, @NonNull Executor exe, @NonNull AudioDeviceAttributes device, boolean handlesVolumeAdjustment)122         ListenerInfo(@NonNull OnAudioDeviceVolumeChangedListener listener, @NonNull Executor exe,
123                 @NonNull AudioDeviceAttributes device, boolean handlesVolumeAdjustment) {
124             mListener = listener;
125             mExecutor = exe;
126             mDevice = device;
127             mHandlesVolumeAdjustment = handlesVolumeAdjustment;
128         }
129     }
130 
131     private final Object mDeviceVolumeListenerLock = new Object();
132     /**
133      * List of listeners for volume changes, the associated device, and their associated Executor.
134      * List is lazy-initialized on first registration
135      */
136     @GuardedBy("mDeviceVolumeListenerLock")
137     private @Nullable ArrayList<ListenerInfo> mDeviceVolumeListeners;
138 
139     @GuardedBy("mDeviceVolumeListenerLock")
140     private DeviceVolumeDispatcherStub mDeviceVolumeDispatcherStub;
141 
142     /** @hide */
143     final class DeviceVolumeDispatcherStub extends IAudioDeviceVolumeDispatcher.Stub {
144         /**
145          * Register / unregister the stub
146          * @param register true for registering, false for unregistering
147          * @param device device for which volume is monitored
148          */
149         @RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
150                 android.Manifest.permission.BLUETOOTH_PRIVILEGED })
register(boolean register, @NonNull AudioDeviceAttributes device, @NonNull List<VolumeInfo> volumes, boolean handlesVolumeAdjustment, @AudioManager.AbsoluteDeviceVolumeBehavior int behavior)151         public void register(boolean register, @NonNull AudioDeviceAttributes device,
152                 @NonNull List<VolumeInfo> volumes, boolean handlesVolumeAdjustment,
153                 @AudioManager.AbsoluteDeviceVolumeBehavior int behavior) {
154             try {
155                 getService().registerDeviceVolumeDispatcherForAbsoluteVolume(register,
156                         this, mPackageName,
157                         Objects.requireNonNull(device), Objects.requireNonNull(volumes),
158                         handlesVolumeAdjustment, behavior);
159             } catch (RemoteException e) {
160                 e.rethrowFromSystemServer();
161             }
162         }
163 
164         @Override
dispatchDeviceVolumeChanged( @onNull AudioDeviceAttributes device, @NonNull VolumeInfo vol)165         public void dispatchDeviceVolumeChanged(
166                 @NonNull AudioDeviceAttributes device, @NonNull VolumeInfo vol) {
167             final ArrayList<ListenerInfo> volumeListeners;
168             synchronized (mDeviceVolumeListenerLock) {
169                 volumeListeners = (ArrayList<ListenerInfo>) mDeviceVolumeListeners.clone();
170             }
171             for (ListenerInfo listenerInfo : volumeListeners) {
172                 if (listenerInfo.mDevice.equalTypeAddress(device)) {
173                     listenerInfo.mExecutor.execute(
174                             () -> listenerInfo.mListener.onAudioDeviceVolumeChanged(device, vol));
175                 }
176             }
177         }
178 
179         @Override
dispatchDeviceVolumeAdjusted( @onNull AudioDeviceAttributes device, @NonNull VolumeInfo vol, int direction, int mode)180         public void dispatchDeviceVolumeAdjusted(
181                 @NonNull AudioDeviceAttributes device, @NonNull VolumeInfo vol, int direction,
182                 int mode) {
183             final ArrayList<ListenerInfo> volumeListeners;
184             synchronized (mDeviceVolumeListenerLock) {
185                 volumeListeners = (ArrayList<ListenerInfo>) mDeviceVolumeListeners.clone();
186             }
187             for (ListenerInfo listenerInfo : volumeListeners) {
188                 if (listenerInfo.mDevice.equalTypeAddress(device)) {
189                     listenerInfo.mExecutor.execute(
190                             () -> listenerInfo.mListener.onAudioDeviceVolumeAdjusted(device, vol,
191                                     direction, mode));
192                 }
193             }
194         }
195     }
196 
197     /**
198      * @hide
199      * Configures a device to use absolute volume model, and registers a listener for receiving
200      * volume updates to apply on that device
201      * @param device the audio device set to absolute volume mode
202      * @param volume the type of volume this device responds to
203      * @param executor the Executor used for receiving volume updates through the listener
204      * @param vclistener the callback for volume updates
205      */
206     @RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
207             android.Manifest.permission.BLUETOOTH_PRIVILEGED })
setDeviceAbsoluteVolumeBehavior( @onNull AudioDeviceAttributes device, @NonNull VolumeInfo volume, @NonNull @CallbackExecutor Executor executor, @NonNull OnAudioDeviceVolumeChangedListener vclistener, boolean handlesVolumeAdjustment)208     public void setDeviceAbsoluteVolumeBehavior(
209             @NonNull AudioDeviceAttributes device,
210             @NonNull VolumeInfo volume,
211             @NonNull @CallbackExecutor Executor executor,
212             @NonNull OnAudioDeviceVolumeChangedListener vclistener,
213             boolean handlesVolumeAdjustment) {
214         final ArrayList<VolumeInfo> volumes = new ArrayList<>(1);
215         volumes.add(volume);
216         setDeviceAbsoluteMultiVolumeBehavior(device, volumes, executor, vclistener,
217                 handlesVolumeAdjustment);
218     }
219 
220     /**
221      * @hide
222      * Configures a device to use absolute volume model applied to different volume types, and
223      * registers a listener for receiving volume updates to apply on that device
224      * @param device the audio device set to absolute multi-volume mode
225      * @param volumes the list of volumes the given device responds to
226      * @param executor the Executor used for receiving volume updates through the listener
227      * @param vclistener the callback for volume updates
228      * @param handlesVolumeAdjustment whether the controller handles volume adjustments separately
229      *  from volume changes. If true, adjustments from {@link AudioManager#adjustStreamVolume}
230      *  will be sent via {@link OnAudioDeviceVolumeChangedListener#onAudioDeviceVolumeAdjusted}.
231      */
232     @RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
233             android.Manifest.permission.BLUETOOTH_PRIVILEGED })
setDeviceAbsoluteMultiVolumeBehavior( @onNull AudioDeviceAttributes device, @NonNull List<VolumeInfo> volumes, @NonNull @CallbackExecutor Executor executor, @NonNull OnAudioDeviceVolumeChangedListener vclistener, boolean handlesVolumeAdjustment)234     public void setDeviceAbsoluteMultiVolumeBehavior(
235             @NonNull AudioDeviceAttributes device,
236             @NonNull List<VolumeInfo> volumes,
237             @NonNull @CallbackExecutor Executor executor,
238             @NonNull OnAudioDeviceVolumeChangedListener vclistener,
239             boolean handlesVolumeAdjustment) {
240         baseSetDeviceAbsoluteMultiVolumeBehavior(device, volumes, executor, vclistener,
241                 handlesVolumeAdjustment, AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
242     }
243 
244     /**
245      * @hide
246      * Configures a device to use absolute volume model, and registers a listener for receiving
247      * volume updates to apply on that device.
248      *
249      * Should be used instead of {@link #setDeviceAbsoluteVolumeBehavior} when there is no reliable
250      * way to set the device's volume to a percentage.
251      *
252      * @param device the audio device set to absolute volume mode
253      * @param volume the type of volume this device responds to
254      * @param executor the Executor used for receiving volume updates through the listener
255      * @param vclistener the callback for volume updates
256      */
257     @RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
258             android.Manifest.permission.BLUETOOTH_PRIVILEGED })
setDeviceAbsoluteVolumeAdjustOnlyBehavior( @onNull AudioDeviceAttributes device, @NonNull VolumeInfo volume, @NonNull @CallbackExecutor Executor executor, @NonNull OnAudioDeviceVolumeChangedListener vclistener, boolean handlesVolumeAdjustment)259     public void setDeviceAbsoluteVolumeAdjustOnlyBehavior(
260             @NonNull AudioDeviceAttributes device,
261             @NonNull VolumeInfo volume,
262             @NonNull @CallbackExecutor Executor executor,
263             @NonNull OnAudioDeviceVolumeChangedListener vclistener,
264             boolean handlesVolumeAdjustment) {
265         final ArrayList<VolumeInfo> volumes = new ArrayList<>(1);
266         volumes.add(volume);
267         setDeviceAbsoluteMultiVolumeAdjustOnlyBehavior(device, volumes, executor, vclistener,
268                 handlesVolumeAdjustment);
269     }
270 
271     /**
272      * @hide
273      * Configures a device to use absolute volume model applied to different volume types, and
274      * registers a listener for receiving volume updates to apply on that device.
275      *
276      * Should be used instead of {@link #setDeviceAbsoluteMultiVolumeBehavior} when there is
277      * no reliable way to set the device's volume to a percentage.
278      *
279      * @param device the audio device set to absolute multi-volume mode
280      * @param volumes the list of volumes the given device responds to
281      * @param executor the Executor used for receiving volume updates through the listener
282      * @param vclistener the callback for volume updates
283      */
284     @RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
285             android.Manifest.permission.BLUETOOTH_PRIVILEGED })
setDeviceAbsoluteMultiVolumeAdjustOnlyBehavior( @onNull AudioDeviceAttributes device, @NonNull List<VolumeInfo> volumes, @NonNull @CallbackExecutor Executor executor, @NonNull OnAudioDeviceVolumeChangedListener vclistener, boolean handlesVolumeAdjustment)286     public void setDeviceAbsoluteMultiVolumeAdjustOnlyBehavior(
287             @NonNull AudioDeviceAttributes device,
288             @NonNull List<VolumeInfo> volumes,
289             @NonNull @CallbackExecutor Executor executor,
290             @NonNull OnAudioDeviceVolumeChangedListener vclistener,
291             boolean handlesVolumeAdjustment) {
292         baseSetDeviceAbsoluteMultiVolumeBehavior(device, volumes, executor, vclistener,
293                 handlesVolumeAdjustment, AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
294     }
295 
296     /**
297      * Base method for configuring a device to use absolute volume behavior, or one of its variants.
298      * See {@link AudioManager#AbsoluteDeviceVolumeBehavior} for a list of allowed behaviors.
299      *
300      * @param behavior the variant of absolute device volume behavior to adopt
301      */
302     @RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
303             android.Manifest.permission.BLUETOOTH_PRIVILEGED })
baseSetDeviceAbsoluteMultiVolumeBehavior( @onNull AudioDeviceAttributes device, @NonNull List<VolumeInfo> volumes, @NonNull @CallbackExecutor Executor executor, @NonNull OnAudioDeviceVolumeChangedListener vclistener, boolean handlesVolumeAdjustment, @AudioManager.AbsoluteDeviceVolumeBehavior int behavior)304     private void baseSetDeviceAbsoluteMultiVolumeBehavior(
305             @NonNull AudioDeviceAttributes device,
306             @NonNull List<VolumeInfo> volumes,
307             @NonNull @CallbackExecutor Executor executor,
308             @NonNull OnAudioDeviceVolumeChangedListener vclistener,
309             boolean handlesVolumeAdjustment,
310             @AudioManager.AbsoluteDeviceVolumeBehavior int behavior) {
311         Objects.requireNonNull(device);
312         Objects.requireNonNull(volumes);
313         Objects.requireNonNull(executor);
314         Objects.requireNonNull(vclistener);
315 
316         final ListenerInfo listenerInfo = new ListenerInfo(
317                 vclistener, executor, device, handlesVolumeAdjustment);
318         synchronized (mDeviceVolumeListenerLock) {
319             if (mDeviceVolumeListeners == null) {
320                 mDeviceVolumeListeners = new ArrayList<>();
321             }
322             if (mDeviceVolumeListeners.size() == 0) {
323                 if (mDeviceVolumeDispatcherStub == null) {
324                     mDeviceVolumeDispatcherStub = new DeviceVolumeDispatcherStub();
325                 }
326             } else {
327                 mDeviceVolumeListeners.removeIf(info -> info.mDevice.equalTypeAddress(device));
328             }
329             mDeviceVolumeListeners.add(listenerInfo);
330             mDeviceVolumeDispatcherStub.register(true, device, volumes, handlesVolumeAdjustment,
331                     behavior);
332         }
333     }
334 
335     /**
336      * Manages the OnDeviceVolumeBehaviorChangedListener listeners and
337      * DeviceVolumeBehaviorDispatcherStub
338      */
339     private final CallbackUtil.LazyListenerManager<OnDeviceVolumeBehaviorChangedListener>
340             mDeviceVolumeBehaviorChangedListenerMgr = new CallbackUtil.LazyListenerManager();
341 
342     /**
343      * @hide
344      * Interface definition of a callback to be invoked when the volume behavior of an audio device
345      * is updated.
346      */
347     public interface OnDeviceVolumeBehaviorChangedListener {
348         /**
349          * Called on the listener to indicate that the volume behavior of a device has changed.
350          * @param device the audio device whose volume behavior changed
351          * @param volumeBehavior the new volume behavior of the audio device
352          */
onDeviceVolumeBehaviorChanged( @onNull AudioDeviceAttributes device, @AudioManager.DeviceVolumeBehavior int volumeBehavior)353         void onDeviceVolumeBehaviorChanged(
354                 @NonNull AudioDeviceAttributes device,
355                 @AudioManager.DeviceVolumeBehavior int volumeBehavior);
356     }
357 
358     /**
359      * @hide
360      * Adds a listener for being notified of changes to any device's volume behavior.
361      * @throws SecurityException if the caller doesn't hold the required permission
362      */
363     @RequiresPermission(anyOf = {
364             android.Manifest.permission.MODIFY_AUDIO_ROUTING,
365             android.Manifest.permission.QUERY_AUDIO_STATE
366     })
addOnDeviceVolumeBehaviorChangedListener( @onNull @allbackExecutor Executor executor, @NonNull OnDeviceVolumeBehaviorChangedListener listener)367     public void addOnDeviceVolumeBehaviorChangedListener(
368             @NonNull @CallbackExecutor Executor executor,
369             @NonNull OnDeviceVolumeBehaviorChangedListener listener)
370             throws SecurityException {
371         mDeviceVolumeBehaviorChangedListenerMgr.addListener(executor, listener,
372                 "addOnDeviceVolumeBehaviorChangedListener",
373                 () -> new DeviceVolumeBehaviorDispatcherStub());
374     }
375 
376     /**
377      * @hide
378      * Removes a previously added listener of changes to device volume behavior.
379      */
380     @RequiresPermission(anyOf = {
381             android.Manifest.permission.MODIFY_AUDIO_ROUTING,
382             android.Manifest.permission.QUERY_AUDIO_STATE
383     })
removeOnDeviceVolumeBehaviorChangedListener( @onNull OnDeviceVolumeBehaviorChangedListener listener)384     public void removeOnDeviceVolumeBehaviorChangedListener(
385             @NonNull OnDeviceVolumeBehaviorChangedListener listener) {
386         mDeviceVolumeBehaviorChangedListenerMgr.removeListener(listener,
387                 "removeOnDeviceVolumeBehaviorChangedListener");
388     }
389 
390     /**
391      * @hide
392      * Sets the volume on the given audio device
393      * @param vi the volume information, only stream-based volumes are supported
394      * @param ada the device for which volume is to be modified
395      */
396     @SystemApi
397     @RequiresPermission(anyOf = {
398             Manifest.permission.MODIFY_AUDIO_ROUTING,
399             Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
400     })
setDeviceVolume(@onNull VolumeInfo vi, @NonNull AudioDeviceAttributes ada)401     public void setDeviceVolume(@NonNull VolumeInfo vi, @NonNull AudioDeviceAttributes ada) {
402         try {
403             getService().setDeviceVolume(vi, ada, mPackageName);
404         } catch (RemoteException e) {
405             e.rethrowFromSystemServer();
406         }
407     }
408 
409     /**
410      * @hide
411      * Returns the volume on the given audio device for the given volume information.
412      * For instance if using a {@link VolumeInfo} configured for {@link AudioManager#STREAM_ALARM},
413      * it will return the alarm volume. When no volume index has ever been set for the given
414      * device, the default volume will be returned (the volume setting that would have been
415      * applied if playback for that use case had started).
416      * @param vi the volume information, only stream-based volumes are supported. Information
417      *           other than the stream type is ignored.
418      * @param ada the device for which volume is to be retrieved
419      */
420     @SystemApi
421     @RequiresPermission(anyOf = {
422             Manifest.permission.MODIFY_AUDIO_ROUTING,
423             Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
424     })
getDeviceVolume(@onNull VolumeInfo vi, @NonNull AudioDeviceAttributes ada)425     public @NonNull VolumeInfo getDeviceVolume(@NonNull VolumeInfo vi,
426             @NonNull AudioDeviceAttributes ada) {
427         try {
428             return getService().getDeviceVolume(vi, ada, mPackageName);
429         } catch (RemoteException e) {
430             e.rethrowFromSystemServer();
431         }
432         return VolumeInfo.getDefaultVolumeInfo();
433     }
434 
435     /**
436      * @hide
437      * Return human-readable name for volume behavior
438      * @param behavior one of the volume behaviors defined in AudioManager
439      * @return a string for the given behavior
440      */
volumeBehaviorName(@udioManager.DeviceVolumeBehavior int behavior)441     public static String volumeBehaviorName(@AudioManager.DeviceVolumeBehavior int behavior) {
442         switch (behavior) {
443             case AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE:
444                 return "DEVICE_VOLUME_BEHAVIOR_VARIABLE";
445             case AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL:
446                 return "DEVICE_VOLUME_BEHAVIOR_FULL";
447             case AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED:
448                 return "DEVICE_VOLUME_BEHAVIOR_FIXED";
449             case AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE:
450                 return "DEVICE_VOLUME_BEHAVIOR_ABSOLUTE";
451             case AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE:
452                 return "DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE";
453             case AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY:
454                 return "DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY";
455             default:
456                 return "invalid volume behavior " + behavior;
457         }
458     }
459 
460     private final class DeviceVolumeBehaviorDispatcherStub
461             extends IDeviceVolumeBehaviorDispatcher.Stub implements CallbackUtil.DispatcherStub {
register(boolean register)462         public void register(boolean register) {
463             try {
464                 getService().registerDeviceVolumeBehaviorDispatcher(register, this);
465             } catch (RemoteException e) {
466                 e.rethrowFromSystemServer();
467             }
468         }
469 
470         @Override
dispatchDeviceVolumeBehaviorChanged(@onNull AudioDeviceAttributes device, @AudioManager.DeviceVolumeBehavior int volumeBehavior)471         public void dispatchDeviceVolumeBehaviorChanged(@NonNull AudioDeviceAttributes device,
472                 @AudioManager.DeviceVolumeBehavior int volumeBehavior) {
473             mDeviceVolumeBehaviorChangedListenerMgr.callListeners((listener) ->
474                     listener.onDeviceVolumeBehaviorChanged(device, volumeBehavior));
475         }
476     }
477 
getService()478     private static IAudioService getService() {
479         if (sService != null) {
480             return sService;
481         }
482         IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
483         sService = IAudioService.Stub.asInterface(b);
484         return sService;
485     }
486 }
487