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