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.NonNull; 19 import android.annotation.SystemApi; 20 import android.car.CarLibLog; 21 import android.car.CarManagerBase; 22 import android.car.CarNotConnectedException; 23 import android.content.ContentResolver; 24 import android.content.Context; 25 import android.database.ContentObserver; 26 import android.media.AudioAttributes; 27 import android.os.Handler; 28 import android.os.IBinder; 29 import android.os.RemoteException; 30 import android.provider.Settings; 31 import android.util.Log; 32 33 /** 34 * APIs for handling car specific audio stuff. 35 */ 36 public final class CarAudioManager implements CarManagerBase { 37 38 // The trailing slash forms a directory-liked hierarchy and 39 // allows listening for both GROUP/MEDIA and GROUP/NAVIGATION. 40 private static final String VOLUME_SETTINGS_KEY_FOR_GROUP_PREFIX = "android.car.VOLUME_GROUP/"; 41 42 /** 43 * @param groupId The volume group id 44 * @return Key to persist volume index for volume group in {@link Settings.Global} 45 */ 46 public static String getVolumeSettingsKeyForGroup(int groupId) { 47 return VOLUME_SETTINGS_KEY_FOR_GROUP_PREFIX + groupId; 48 } 49 50 private final ContentResolver mContentResolver; 51 private final ICarAudio mService; 52 53 /** 54 * Registers a {@link ContentObserver} to listen for volume group changes. 55 * Note that this observer is valid for bus based car audio stack only. 56 * 57 * {@link ContentObserver#onChange(boolean)} will be called on every group volume change. 58 * 59 * @param observer The {@link ContentObserver} instance to register, non-null 60 */ 61 @SystemApi 62 public void registerVolumeChangeObserver(@NonNull ContentObserver observer) { 63 mContentResolver.registerContentObserver( 64 Settings.Global.getUriFor(VOLUME_SETTINGS_KEY_FOR_GROUP_PREFIX), 65 true, observer); 66 } 67 68 /** 69 * Unregisters the {@link ContentObserver} which listens for volume group changes. 70 * 71 * @param observer The {@link ContentObserver} instance to unregister, non-null 72 */ 73 @SystemApi 74 public void unregisterVolumeChangeObserver(@NonNull ContentObserver observer) { 75 mContentResolver.unregisterContentObserver(observer); 76 } 77 78 /** 79 * Sets the volume index for a volume group. 80 * 81 * Requires {@link android.car.Car#PERMISSION_CAR_CONTROL_AUDIO_VOLUME} permission. 82 * 83 * @param groupId The volume group id whose volume index should be set. 84 * @param index The volume index to set. See 85 * {@link #getGroupMaxVolume(int)} for the largest valid value. 86 * @param flags One or more flags (e.g., {@link android.media.AudioManager#FLAG_SHOW_UI}, 87 * {@link android.media.AudioManager#FLAG_PLAY_SOUND}) 88 */ 89 @SystemApi 90 public void setGroupVolume(int groupId, int index, int flags) throws CarNotConnectedException { 91 try { 92 mService.setGroupVolume(groupId, index, flags); 93 } catch (RemoteException e) { 94 Log.e(CarLibLog.TAG_CAR, "setGroupVolume failed", e); 95 throw new CarNotConnectedException(e); 96 } 97 } 98 99 /** 100 * Returns the maximum volume index for a volume group. 101 * 102 * Requires {@link android.car.Car#PERMISSION_CAR_CONTROL_AUDIO_VOLUME} permission. 103 * 104 * @param groupId The volume group id whose maximum volume index is returned. 105 * @return The maximum valid volume index for the given group. 106 */ 107 @SystemApi 108 public int getGroupMaxVolume(int groupId) throws CarNotConnectedException { 109 try { 110 return mService.getGroupMaxVolume(groupId); 111 } catch (RemoteException e) { 112 Log.e(CarLibLog.TAG_CAR, "getUsageMaxVolume failed", e); 113 throw new CarNotConnectedException(e); 114 } 115 } 116 117 /** 118 * Returns the minimum volume index for a volume group. 119 * 120 * Requires {@link android.car.Car#PERMISSION_CAR_CONTROL_AUDIO_VOLUME} permission. 121 * 122 * @param groupId The volume group id whose minimum volume index is returned. 123 * @return The minimum valid volume index for the given group, non-negative 124 */ 125 @SystemApi 126 public int getGroupMinVolume(int groupId) throws CarNotConnectedException { 127 try { 128 return mService.getGroupMinVolume(groupId); 129 } catch (RemoteException e) { 130 Log.e(CarLibLog.TAG_CAR, "getUsageMinVolume failed", e); 131 throw new CarNotConnectedException(e); 132 } 133 } 134 135 /** 136 * Returns the current volume index for a volume group. 137 * 138 * Requires {@link android.car.Car#PERMISSION_CAR_CONTROL_AUDIO_VOLUME} permission. 139 * 140 * @param groupId The volume group id whose volume index is returned. 141 * @return The current volume index for the given group. 142 * 143 * @see #getGroupMaxVolume(int) 144 * @see #setGroupVolume(int, int, int) 145 */ 146 @SystemApi 147 public int getGroupVolume(int groupId) throws CarNotConnectedException { 148 try { 149 return mService.getGroupVolume(groupId); 150 } catch (RemoteException e) { 151 Log.e(CarLibLog.TAG_CAR, "getUsageVolume failed", e); 152 throw new CarNotConnectedException(e); 153 } 154 } 155 156 /** 157 * Adjust the relative volume in the front vs back of the vehicle cabin. 158 * 159 * Requires {@link android.car.Car#PERMISSION_CAR_CONTROL_AUDIO_VOLUME} permission. 160 * 161 * @param value in the range -1.0 to 1.0 for fully toward the back through 162 * fully toward the front. 0.0 means evenly balanced. 163 * 164 * @see #setBalanceTowardRight(float) 165 */ 166 @SystemApi 167 public void setFadeTowardFront(float value) throws CarNotConnectedException { 168 try { 169 mService.setFadeTowardFront(value); 170 } catch (RemoteException e) { 171 Log.e(CarLibLog.TAG_CAR, "setFadeTowardFront failed", e); 172 throw new CarNotConnectedException(e); 173 } 174 } 175 176 /** 177 * Adjust the relative volume on the left vs right side of the vehicle cabin. 178 * 179 * Requires {@link android.car.Car#PERMISSION_CAR_CONTROL_AUDIO_VOLUME} permission. 180 * 181 * @param value in the range -1.0 to 1.0 for fully toward the left through 182 * fully toward the right. 0.0 means evenly balanced. 183 * 184 * @see #setFadeTowardFront(float) 185 */ 186 @SystemApi 187 public void setBalanceTowardRight(float value) throws CarNotConnectedException { 188 try { 189 mService.setBalanceTowardRight(value); 190 } catch (RemoteException e) { 191 Log.e(CarLibLog.TAG_CAR, "setBalanceTowardRight failed", e); 192 throw new CarNotConnectedException(e); 193 } 194 } 195 196 /** 197 * Queries the system configuration in order to report the available, non-microphone audio 198 * input devices. 199 * 200 * Requires {@link android.car.Car#PERMISSION_CAR_CONTROL_AUDIO_SETTINGS} permission. 201 * 202 * @return An array of strings representing the available input ports. 203 * Each port is identified by it's "address" tag in the audioPolicyConfiguration xml file. 204 * Empty array if we find nothing. 205 * 206 * @see #createAudioPatch(String, int, int) 207 * @see #releaseAudioPatch(CarAudioPatchHandle) 208 */ 209 @SystemApi 210 public @NonNull String[] getExternalSources() throws CarNotConnectedException { 211 try { 212 return mService.getExternalSources(); 213 } catch (RemoteException e) { 214 Log.e(CarLibLog.TAG_CAR, "getExternalSources failed", e); 215 throw new CarNotConnectedException(e); 216 } 217 } 218 219 /** 220 * Given an input port identified by getExternalSources(), request that it's audio signal 221 * be routed below the HAL to the output port associated with the given usage. For example, 222 * The output of a tuner might be routed directly to the output buss associated with 223 * AudioAttributes.USAGE_MEDIA while the tuner is playing. 224 * 225 * Requires {@link android.car.Car#PERMISSION_CAR_CONTROL_AUDIO_SETTINGS} permission. 226 * 227 * @param sourceAddress the input port name obtained from getExternalSources(). 228 * @param usage the type of audio represented by this source (usually USAGE_MEDIA). 229 * @param gainInMillibels How many steps above the minimum value defined for the source port to 230 * set the gain when creating the patch. 231 * This may be used for source balancing without affecting the user 232 * controlled volumes applied to the destination ports. A value of 233 * 0 indicates no gain change is requested. 234 * @return A handle for the created patch which can be used to later remove it. 235 * 236 * @see #getExternalSources() 237 * @see #releaseAudioPatch(CarAudioPatchHandle) 238 */ 239 @SystemApi 240 public CarAudioPatchHandle createAudioPatch(String sourceAddress, 241 @AudioAttributes.AttributeUsage int usage, int gainInMillibels) 242 throws CarNotConnectedException { 243 try { 244 return mService.createAudioPatch(sourceAddress, usage, gainInMillibels); 245 } catch (RemoteException e) { 246 Log.e(CarLibLog.TAG_CAR, "createAudioPatch failed", e); 247 throw new CarNotConnectedException(e); 248 } 249 } 250 251 /** 252 * Removes the association between an input port and an output port identified by the provided 253 * handle. 254 * 255 * Requires {@link android.car.Car#PERMISSION_CAR_CONTROL_AUDIO_SETTINGS} permission. 256 * 257 * @param patch CarAudioPatchHandle returned from createAudioPatch(). 258 * 259 * @see #getExternalSources() 260 * @see #createAudioPatch(String, int, int) 261 */ 262 @SystemApi 263 public void releaseAudioPatch(CarAudioPatchHandle patch) throws CarNotConnectedException { 264 try { 265 mService.releaseAudioPatch(patch); 266 } catch (RemoteException e) { 267 Log.e(CarLibLog.TAG_CAR, "releaseAudioPatch failed", e); 268 throw new CarNotConnectedException(e); 269 } 270 } 271 272 /** 273 * Gets the count of available volume groups in the system. 274 * 275 * Requires {@link android.car.Car#PERMISSION_CAR_CONTROL_AUDIO_VOLUME} permission. 276 * 277 * @return Count of volume groups 278 */ 279 @SystemApi 280 public int getVolumeGroupCount() throws CarNotConnectedException { 281 try { 282 return mService.getVolumeGroupCount(); 283 } catch (RemoteException e) { 284 Log.e(CarLibLog.TAG_CAR, "getVolumeGroupCount failed", e); 285 throw new CarNotConnectedException(e); 286 } 287 } 288 289 /** 290 * Gets the volume group id for a given {@link AudioAttributes} usage. 291 * 292 * Requires {@link android.car.Car#PERMISSION_CAR_CONTROL_AUDIO_VOLUME} permission. 293 * 294 * @param usage The {@link AudioAttributes} usage to get a volume group from. 295 * @return The volume group id where the usage belongs to 296 */ 297 @SystemApi 298 public int getVolumeGroupIdForUsage(@AudioAttributes.AttributeUsage int usage) 299 throws CarNotConnectedException { 300 try { 301 return mService.getVolumeGroupIdForUsage(usage); 302 } catch (RemoteException e) { 303 Log.e(CarLibLog.TAG_CAR, "getVolumeGroupIdForUsage failed", e); 304 throw new CarNotConnectedException(e); 305 } 306 } 307 308 /** 309 * Gets array of {@link AudioAttributes} usages for a given volume group id. 310 * 311 * Requires {@link android.car.Car#PERMISSION_CAR_CONTROL_AUDIO_VOLUME} permission. 312 * 313 * @param groupId The volume group id whose associated audio usages is returned. 314 * @return Array of {@link AudioAttributes} usages for a given volume group id 315 */ 316 @SystemApi 317 public @NonNull int[] getUsagesForVolumeGroupId(int groupId) throws CarNotConnectedException { 318 try { 319 return mService.getUsagesForVolumeGroupId(groupId); 320 } catch (RemoteException e) { 321 Log.e(CarLibLog.TAG_CAR, "getUsagesForVolumeGroupId failed", e); 322 throw new CarNotConnectedException(e); 323 } 324 } 325 326 /** 327 * Register {@link ICarVolumeCallback} to receive the volume key events 328 * 329 * Requires {@link android.car.Car#PERMISSION_CAR_CONTROL_AUDIO_VOLUME} permission. 330 * 331 * @param binder {@link IBinder} instance of {@link ICarVolumeCallback} to receive 332 * volume key event callbacks 333 * @throws CarNotConnectedException 334 */ 335 @SystemApi 336 public void registerVolumeCallback(@NonNull IBinder binder) 337 throws CarNotConnectedException { 338 try { 339 mService.registerVolumeCallback(binder); 340 } catch (RemoteException e) { 341 Log.e(CarLibLog.TAG_CAR, "registerVolumeCallback failed", e); 342 throw new CarNotConnectedException(e); 343 } 344 } 345 346 /** 347 * Unregister {@link ICarVolumeCallback} from receiving volume key events 348 * 349 * Requires {@link android.car.Car#PERMISSION_CAR_CONTROL_AUDIO_VOLUME} permission. 350 * 351 * @param binder {@link IBinder} instance of {@link ICarVolumeCallback} to stop receiving 352 * volume key event callbacks 353 * @throws CarNotConnectedException 354 */ 355 @SystemApi 356 public void unregisterVolumeCallback(@NonNull IBinder binder) 357 throws CarNotConnectedException { 358 try { 359 mService.unregisterVolumeCallback(binder); 360 } catch (RemoteException e) { 361 Log.e(CarLibLog.TAG_CAR, "unregisterVolumeCallback failed", e); 362 throw new CarNotConnectedException(e); 363 } 364 } 365 366 /** @hide */ 367 @Override 368 public void onCarDisconnected() { 369 } 370 371 /** @hide */ 372 public CarAudioManager(IBinder service, Context context, Handler handler) { 373 mContentResolver = context.getContentResolver(); 374 mService = ICarAudio.Stub.asInterface(service); 375 } 376 } 377