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