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