1 /* 2 * Copyright (C) 2015 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 package android.car.media; 17 18 import android.annotation.IntDef; 19 import android.annotation.Nullable; 20 import android.annotation.SystemApi; 21 import android.car.CarLibLog; 22 import android.car.CarNotConnectedException; 23 import android.content.Context; 24 import android.media.AudioAttributes; 25 import android.media.AudioManager; 26 import android.media.AudioManager.OnAudioFocusChangeListener; 27 import android.media.IVolumeController; 28 import android.os.Handler; 29 import android.os.IBinder; 30 import android.os.RemoteException; 31 import android.car.CarManagerBase; 32 import android.util.Log; 33 34 import java.lang.annotation.Retention; 35 import java.lang.annotation.RetentionPolicy; 36 import java.lang.ref.WeakReference; 37 38 /** 39 * APIs for handling car specific audio stuffs. 40 */ 41 public final class CarAudioManager implements CarManagerBase { 42 43 /** 44 * Audio usage for unspecified type. 45 */ 46 public static final int CAR_AUDIO_USAGE_DEFAULT = 0; 47 /** 48 * Audio usage for playing music. 49 */ 50 public static final int CAR_AUDIO_USAGE_MUSIC = 1; 51 /** 52 * Audio usage for H/W radio. 53 */ 54 public static final int CAR_AUDIO_USAGE_RADIO = 2; 55 /** 56 * Audio usage for playing navigation guidance. 57 */ 58 public static final int CAR_AUDIO_USAGE_NAVIGATION_GUIDANCE = 3; 59 /** 60 * Audio usage for voice call 61 */ 62 public static final int CAR_AUDIO_USAGE_VOICE_CALL = 4; 63 /** 64 * Audio usage for voice search or voice command. 65 */ 66 public static final int CAR_AUDIO_USAGE_VOICE_COMMAND = 5; 67 /** 68 * Audio usage for playing alarm. 69 */ 70 public static final int CAR_AUDIO_USAGE_ALARM = 6; 71 /** 72 * Audio usage for notification sound. 73 */ 74 public static final int CAR_AUDIO_USAGE_NOTIFICATION = 7; 75 /** 76 * Audio usage for system sound like UI feedback. 77 */ 78 public static final int CAR_AUDIO_USAGE_SYSTEM_SOUND = 8; 79 /** 80 * Audio usage for playing safety alert. 81 */ 82 public static final int CAR_AUDIO_USAGE_SYSTEM_SAFETY_ALERT = 9; 83 /** 84 * Audio usage for external audio usage. 85 * @hide 86 */ 87 public static final int CAR_AUDIO_USAGE_EXTERNAL_AUDIO_SOURCE = 10; 88 89 /** @hide */ 90 public static final int CAR_AUDIO_USAGE_MAX = CAR_AUDIO_USAGE_EXTERNAL_AUDIO_SOURCE; 91 92 /** @hide */ 93 @IntDef({CAR_AUDIO_USAGE_DEFAULT, CAR_AUDIO_USAGE_MUSIC, CAR_AUDIO_USAGE_RADIO, 94 CAR_AUDIO_USAGE_NAVIGATION_GUIDANCE, CAR_AUDIO_USAGE_VOICE_CALL, 95 CAR_AUDIO_USAGE_VOICE_COMMAND, CAR_AUDIO_USAGE_ALARM, CAR_AUDIO_USAGE_NOTIFICATION, 96 CAR_AUDIO_USAGE_SYSTEM_SOUND, CAR_AUDIO_USAGE_SYSTEM_SAFETY_ALERT, 97 CAR_AUDIO_USAGE_EXTERNAL_AUDIO_SOURCE}) 98 @Retention(RetentionPolicy.SOURCE) 99 public @interface CarAudioUsage {} 100 101 /** @hide */ 102 public static final String CAR_RADIO_TYPE_AM_FM = "RADIO_AM_FM"; 103 /** @hide */ 104 public static final String CAR_RADIO_TYPE_AM_FM_HD = "RADIO_AM_FM_HD"; 105 /** @hide */ 106 public static final String CAR_RADIO_TYPE_DAB = "RADIO_DAB"; 107 /** @hide */ 108 public static final String CAR_RADIO_TYPE_SATELLITE = "RADIO_SATELLITE"; 109 110 /** @hide */ 111 public static final String CAR_EXTERNAL_SOURCE_TYPE_CD_DVD = "CD_DVD"; 112 /** @hide */ 113 public static final String CAR_EXTERNAL_SOURCE_TYPE_AUX_IN0 = "AUX_IN0"; 114 /** @hide */ 115 public static final String CAR_EXTERNAL_SOURCE_TYPE_AUX_IN1 = "AUX_IN1"; 116 /** @hide */ 117 public static final String CAR_EXTERNAL_SOURCE_TYPE_EXT_NAV_GUIDANCE = "EXT_NAV_GUIDANCE"; 118 /** @hide */ 119 public static final String CAR_EXTERNAL_SOURCE_TYPE_EXT_VOICE_CALL = "EXT_VOICE_CALL"; 120 /** @hide */ 121 public static final String CAR_EXTERNAL_SOURCE_TYPE_EXT_VOICE_COMMAND = "EXT_VOICE_COMMAND"; 122 /** @hide */ 123 public static final String CAR_EXTERNAL_SOURCE_TYPE_EXT_SAFETY_ALERT = "EXT_SAFETY_ALERT"; 124 125 private final ICarAudio mService; 126 private final AudioManager mAudioManager; 127 private final Handler mHandler; 128 129 private ParameterChangeCallback mParameterChangeCallback; 130 private OnParameterChangeListener mOnParameterChangeListener; 131 132 /** 133 * Get {@link AudioAttributes} relevant for the given usage in car. 134 * @param carUsage 135 * @return 136 */ getAudioAttributesForCarUsage(@arAudioUsage int carUsage)137 public AudioAttributes getAudioAttributesForCarUsage(@CarAudioUsage int carUsage) 138 throws CarNotConnectedException { 139 try { 140 return mService.getAudioAttributesForCarUsage(carUsage); 141 } catch (RemoteException e) { 142 throw new CarNotConnectedException(); 143 } 144 } 145 146 /** 147 * Get AudioAttributes for radio. This is necessary when there are multiple types of radio 148 * in system. 149 * 150 * @param radioType String specifying the desired radio type. Should use only what is listed in 151 * {@link #getSupportedRadioTypes()}. 152 * @return 153 * @throws IllegalArgumentException If not supported type is passed. 154 * 155 * @hide 156 */ getAudioAttributesForRadio(String radioType)157 public AudioAttributes getAudioAttributesForRadio(String radioType) 158 throws CarNotConnectedException, IllegalArgumentException { 159 try { 160 return mService.getAudioAttributesForRadio(radioType); 161 } catch (RemoteException e) { 162 throw new CarNotConnectedException(); 163 } 164 } 165 166 /** 167 * Get AudioAttributes for external audio source. 168 * 169 * @param externalSourceType String specifying the desired source type. Should use only what is 170 * listed in {@link #getSupportedExternalSourceTypes()}. 171 * @return 172 * @throws IllegalArgumentException If not supported type is passed. 173 * 174 * @hide 175 */ getAudioAttributesForExternalSource(String externalSourceType)176 public AudioAttributes getAudioAttributesForExternalSource(String externalSourceType) 177 throws CarNotConnectedException, IllegalArgumentException { 178 try { 179 return mService.getAudioAttributesForExternalSource(externalSourceType); 180 } catch (RemoteException e) { 181 throw new CarNotConnectedException(); 182 } 183 } 184 185 /** 186 * List all supported external audio sources. 187 * 188 * @return 189 * 190 * @hide 191 */ getSupportedExternalSourceTypes()192 public String[] getSupportedExternalSourceTypes() throws CarNotConnectedException { 193 try { 194 return mService.getSupportedExternalSourceTypes(); 195 } catch (RemoteException e) { 196 throw new CarNotConnectedException(); 197 } 198 } 199 200 /** 201 * List all supported radio sources. 202 * 203 * @return 204 * 205 * @hide 206 */ getSupportedRadioTypes()207 public String[] getSupportedRadioTypes() throws CarNotConnectedException { 208 try { 209 return mService.getSupportedRadioTypes(); 210 } catch (RemoteException e) { 211 throw new CarNotConnectedException(); 212 } 213 } 214 215 /** 216 * Request audio focus. 217 * Send a request to obtain the audio focus. 218 * @param l 219 * @param requestAttributes 220 * @param durationHint 221 * @param flags 222 */ requestAudioFocus(OnAudioFocusChangeListener l, AudioAttributes requestAttributes, int durationHint, int flags)223 public int requestAudioFocus(OnAudioFocusChangeListener l, 224 AudioAttributes requestAttributes, 225 int durationHint, 226 int flags) 227 throws CarNotConnectedException, IllegalArgumentException { 228 return mAudioManager.requestAudioFocus(l, requestAttributes, durationHint, flags); 229 } 230 231 /** 232 * Abandon audio focus. Causes the previous focus owner, if any, to receive focus. 233 * @param l 234 * @param aa 235 */ abandonAudioFocus(OnAudioFocusChangeListener l, AudioAttributes aa)236 public void abandonAudioFocus(OnAudioFocusChangeListener l, AudioAttributes aa) { 237 mAudioManager.abandonAudioFocus(l, aa); 238 } 239 240 /** 241 * Sets the volume index for a particular stream. 242 * 243 * Requires {@link android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME} permission. 244 * 245 * @param streamType The stream whose volume index should be set. 246 * @param index The volume index to set. See 247 * {@link #getStreamMaxVolume(int)} for the largest valid value. 248 * @param flags One or more flags (e.g., {@link android.media.AudioManager#FLAG_SHOW_UI}, 249 * {@link android.media.AudioManager#FLAG_PLAY_SOUND}) 250 */ 251 @SystemApi setStreamVolume(int streamType, int index, int flags)252 public void setStreamVolume(int streamType, int index, int flags) 253 throws CarNotConnectedException { 254 try { 255 mService.setStreamVolume(streamType, index, flags); 256 } catch (RemoteException e) { 257 Log.e(CarLibLog.TAG_CAR, "setStreamVolume failed", e); 258 throw new CarNotConnectedException(e); 259 } 260 } 261 262 /** 263 * Registers a global volume controller interface. 264 * 265 * Requires {@link android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME} permission. 266 * 267 * @hide 268 */ 269 @SystemApi setVolumeController(IVolumeController controller)270 public void setVolumeController(IVolumeController controller) 271 throws CarNotConnectedException { 272 try { 273 mService.setVolumeController(controller); 274 } catch (RemoteException e) { 275 Log.e(CarLibLog.TAG_CAR, "setVolumeController failed", e); 276 throw new CarNotConnectedException(e); 277 } 278 } 279 280 /** 281 * Returns the maximum volume index for a particular stream. 282 * 283 * Requires {@link android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME} permission. 284 * 285 * @param stream The stream type whose maximum volume index is returned. 286 * @return The maximum valid volume index for the stream. 287 */ 288 @SystemApi getStreamMaxVolume(int stream)289 public int getStreamMaxVolume(int stream) throws CarNotConnectedException { 290 try { 291 return mService.getStreamMaxVolume(stream); 292 } catch (RemoteException e) { 293 Log.e(CarLibLog.TAG_CAR, "getStreamMaxVolume failed", e); 294 throw new CarNotConnectedException(e); 295 } 296 } 297 298 /** 299 * Returns the minimum volume index for a particular stream. 300 * 301 * Requires {@link android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME} permission. 302 * 303 * @param stream The stream type whose maximum volume index is returned. 304 * @return The maximum valid volume index for the stream. 305 */ 306 @SystemApi getStreamMinVolume(int stream)307 public int getStreamMinVolume(int stream) throws CarNotConnectedException { 308 try { 309 return mService.getStreamMinVolume(stream); 310 } catch (RemoteException e) { 311 Log.e(CarLibLog.TAG_CAR, "getStreamMaxVolume failed", e); 312 throw new CarNotConnectedException(e); 313 } 314 } 315 316 /** 317 * Returns the current volume index for a particular stream. 318 * 319 * Requires {@link android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME} permission. 320 * 321 * @param stream The stream type whose volume index is returned. 322 * @return The current volume index for the stream. 323 * 324 * @see #getStreamMaxVolume(int) 325 * @see #setStreamVolume(int, int, int) 326 */ 327 @SystemApi getStreamVolume(int stream)328 public int getStreamVolume(int stream) throws CarNotConnectedException { 329 try { 330 return mService.getStreamVolume(stream); 331 } catch (RemoteException e) { 332 Log.e(CarLibLog.TAG_CAR, "getStreamVolume failed", e); 333 throw new CarNotConnectedException(e); 334 } 335 } 336 337 /** 338 * Check if media audio is muted or not. This will include music and radio. Any application 339 * taking audio focus for media stream will get it out of mute state. 340 * 341 * @return true if media is muted. 342 * @throws CarNotConnectedException if the connection to the car service has been lost. 343 * @hide 344 */ 345 @SystemApi isMediaMuted()346 public boolean isMediaMuted() throws CarNotConnectedException { 347 try { 348 return mService.isMediaMuted(); 349 } catch (RemoteException e) { 350 Log.e(CarLibLog.TAG_CAR, "isMediaMuted failed", e); 351 throw new CarNotConnectedException(e); 352 } 353 } 354 355 /** 356 * Mute or unmute media stream including radio. This can involve audio focus change to stop 357 * whatever app holding audio focus now. If requester is currently holding audio focus, 358 * it will get LOSS_TRANSIENT focus loss. 359 * This API requires {@link android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME} permission. 360 * 361 * @param mute 362 * @return Mute state of system after the request. Note that mute request can fail if there 363 * is higher priority audio already being played like phone call. 364 * @throws CarNotConnectedException if the connection to the car service has been lost. 365 * @hide 366 */ 367 @SystemApi setMediaMute(boolean mute)368 public boolean setMediaMute(boolean mute) throws CarNotConnectedException { 369 try { 370 return mService.setMediaMute(mute); 371 } catch (RemoteException e) { 372 Log.e(CarLibLog.TAG_CAR, "setMediaMute failed", e); 373 throw new CarNotConnectedException(e); 374 } 375 } 376 377 /** 378 * Listener to monitor audio parameter changes. 379 * @hide 380 */ 381 public interface OnParameterChangeListener { 382 /** 383 * Parameter changed. 384 * @param parameters Have format of key1=value1;key2=value2;... 385 */ onParameterChange(String parameters)386 void onParameterChange(String parameters); 387 } 388 389 /** 390 * Return array of keys supported in this system. 391 * Requires {@link android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS} permission. 392 * The list is static and will not change. 393 * @return null if there is no audio parameters supported. 394 * @throws CarNotConnectedException 395 * 396 * @hide 397 */ getParameterKeys()398 public @Nullable String[] getParameterKeys() throws CarNotConnectedException { 399 try { 400 return mService.getParameterKeys(); 401 } catch (RemoteException e) { 402 Log.e(CarLibLog.TAG_CAR, "getParameterKeys failed", e); 403 throw new CarNotConnectedException(e); 404 } 405 } 406 407 /** 408 * Set car specific audio parameters. 409 * Requires {@link android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS} permission. 410 * Only keys listed from {@link #getParameterKeys()} should be used. 411 * @param parameters has format of key1=value1;key2=value2;... 412 * @throws CarNotConnectedException 413 * 414 * @hide 415 */ setParameters(String parameters)416 public void setParameters(String parameters) throws CarNotConnectedException { 417 try { 418 mService.setParameters(parameters); 419 } catch (RemoteException e) { 420 Log.e(CarLibLog.TAG_CAR, "setParameters failed", e); 421 throw new CarNotConnectedException(e); 422 } 423 } 424 425 /** 426 * Get parameters for the key passed. 427 * Requires {@link android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS} permission. 428 * Only keys listed from {@link #getParameterKeys()} should be used. 429 * @param keys Keys to get value. Format is key1;key2;... 430 * @return Parameters in format of key1=value1;key2=value2;... 431 * @throws CarNotConnectedException 432 * 433 * @hide 434 */ getParameters(String keys)435 public String getParameters(String keys) throws CarNotConnectedException { 436 try { 437 return mService.getParameters(keys); 438 } catch (RemoteException e) { 439 Log.e(CarLibLog.TAG_CAR, "getParameters failed", e); 440 throw new CarNotConnectedException(e); 441 } 442 } 443 444 /** 445 * Set listener to monitor audio parameter changes. 446 * Requires {@link android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS} permission. 447 * @param listener Non-null listener will start monitoring. null listener will stop listening. 448 * @throws CarNotConnectedException 449 * 450 * @hide 451 */ setOnParameterChangeListener(OnParameterChangeListener listener)452 public void setOnParameterChangeListener(OnParameterChangeListener listener) 453 throws CarNotConnectedException { 454 ParameterChangeCallback oldCb = null; 455 ParameterChangeCallback newCb = null; 456 synchronized (this) { 457 if (listener != null) { 458 if (mParameterChangeCallback != null) { 459 oldCb = mParameterChangeCallback; 460 } 461 newCb = new ParameterChangeCallback(this); 462 } 463 mParameterChangeCallback = newCb; 464 mOnParameterChangeListener = listener; 465 } 466 try { 467 if (oldCb != null) { 468 mService.unregisterOnParameterChangeListener(oldCb); 469 } 470 if (newCb != null) { 471 mService.registerOnParameterChangeListener(newCb); 472 } 473 } catch (RemoteException e) { 474 Log.e(CarLibLog.TAG_CAR, "setOnParameterChangeListener failed", e); 475 throw new CarNotConnectedException(e); 476 } 477 } 478 479 /** @hide */ 480 @Override onCarDisconnected()481 public void onCarDisconnected() { 482 } 483 484 /** @hide */ CarAudioManager(IBinder service, Context context, Handler handler)485 public CarAudioManager(IBinder service, Context context, Handler handler) { 486 mService = ICarAudio.Stub.asInterface(service); 487 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 488 mHandler = handler; 489 } 490 createAudioAttributes(int contentType, int usage)491 private AudioAttributes createAudioAttributes(int contentType, int usage) { 492 AudioAttributes.Builder builder = new AudioAttributes.Builder(); 493 return builder.setContentType(contentType).setUsage(usage).build(); 494 } 495 496 private static class ParameterChangeCallback extends ICarAudioCallback.Stub { 497 498 private final WeakReference<CarAudioManager> mManager; 499 ParameterChangeCallback(CarAudioManager manager)500 private ParameterChangeCallback(CarAudioManager manager) { 501 mManager = new WeakReference<>(manager); 502 } 503 504 @Override onParameterChange(final String params)505 public void onParameterChange(final String params) { 506 CarAudioManager manager = mManager.get(); 507 if (manager == null) { 508 return; 509 } 510 final OnParameterChangeListener listener = manager.mOnParameterChangeListener; 511 if (listener == null) { 512 return; 513 } 514 manager.mHandler.post(new Runnable() { 515 @Override 516 public void run() { 517 listener.onParameterChange(params); 518 } 519 }); 520 } 521 } 522 } 523