1 /* 2 * Copyright 2019 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 com.android.server.audio; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.media.AudioAttributes; 22 import android.media.AudioDeviceAttributes; 23 import android.media.AudioMixerAttributes; 24 import android.media.AudioSystem; 25 import android.media.IDevicesForAttributesCallback; 26 import android.media.ISoundDose; 27 import android.media.ISoundDoseCallback; 28 import android.media.audiopolicy.AudioMix; 29 import android.os.IBinder; 30 import android.os.RemoteCallbackList; 31 import android.os.RemoteException; 32 import android.os.SystemClock; 33 import android.util.ArrayMap; 34 import android.util.Log; 35 import android.util.Pair; 36 37 import com.android.internal.annotations.GuardedBy; 38 39 import java.io.PrintWriter; 40 import java.time.Instant; 41 import java.time.ZoneId; 42 import java.time.format.DateTimeFormatter; 43 import java.util.ArrayList; 44 import java.util.List; 45 import java.util.Locale; 46 import java.util.Map; 47 import java.util.concurrent.ConcurrentHashMap; 48 49 /** 50 * Provides an adapter to access functionality of the android.media.AudioSystem class for device 51 * related functionality. 52 * Use the "real" AudioSystem through the default adapter. 53 * Use the "always ok" adapter to avoid dealing with the APM behaviors during a test. 54 */ 55 public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback, 56 AudioSystem.VolumeRangeInitRequestCallback { 57 58 private static final String TAG = "AudioSystemAdapter"; 59 60 // initialized in factory getDefaultAdapter() 61 private static AudioSystemAdapter sSingletonDefaultAdapter; 62 63 /** 64 * should be false by default unless enabling measurements of method call counts and time spent 65 * in measured methods 66 */ 67 private static final boolean ENABLE_GETDEVICES_STATS = false; 68 private static final int NB_MEASUREMENTS = 1; 69 private static final int METHOD_GETDEVICESFORATTRIBUTES = 0; 70 private long[] mMethodTimeNs; 71 private int[] mMethodCallCounter; 72 private String[] mMethodNames = {"getDevicesForAttributes"}; 73 74 private static final boolean USE_CACHE_FOR_GETDEVICES = true; 75 private static final Object sDeviceCacheLock = new Object(); 76 private ConcurrentHashMap<Pair<AudioAttributes, Boolean>, ArrayList<AudioDeviceAttributes>> 77 mLastDevicesForAttr = new ConcurrentHashMap<>(); 78 @GuardedBy("sDeviceCacheLock") 79 private ConcurrentHashMap<Pair<AudioAttributes, Boolean>, ArrayList<AudioDeviceAttributes>> 80 mDevicesForAttrCache; 81 @GuardedBy("sDeviceCacheLock") 82 private long mDevicesForAttributesCacheClearTimeMs = System.currentTimeMillis(); 83 private int[] mMethodCacheHit; 84 /** 85 * Map that stores all attributes + forVolume pairs that are registered for 86 * routing change callback. The key is the {@link IBinder} that corresponds 87 * to the remote callback. 88 */ 89 private final ArrayMap<IBinder, List<Pair<AudioAttributes, Boolean>>> mRegisteredAttributesMap = 90 new ArrayMap<>(); 91 private final RemoteCallbackList<IDevicesForAttributesCallback> 92 mDevicesForAttributesCallbacks = new RemoteCallbackList<>(); 93 94 private static final Object sRoutingListenerLock = new Object(); 95 @GuardedBy("sRoutingListenerLock") 96 private static @Nullable OnRoutingUpdatedListener sRoutingListener; 97 private static final Object sVolRangeInitReqListenerLock = new Object(); 98 @GuardedBy("sVolRangeInitReqListenerLock") 99 private static @Nullable OnVolRangeInitRequestListener sVolRangeInitReqListener; 100 101 /** 102 * should be false except when trying to debug caching errors. When true, the value retrieved 103 * from the cache will be compared against the real queried value, which defeats the purpose of 104 * the cache in terms of performance. 105 */ 106 private static final boolean DEBUG_CACHE = false; 107 108 /** 109 * Implementation of AudioSystem.RoutingUpdateCallback 110 */ 111 @Override onRoutingUpdated()112 public void onRoutingUpdated() { 113 if (DEBUG_CACHE) { 114 Log.d(TAG, "---- onRoutingUpdated (from native) ----------"); 115 } 116 invalidateRoutingCache(); 117 final OnRoutingUpdatedListener listener; 118 synchronized (sRoutingListenerLock) { 119 listener = sRoutingListener; 120 } 121 if (listener != null) { 122 listener.onRoutingUpdatedFromNative(); 123 } 124 125 synchronized (mRegisteredAttributesMap) { 126 final int nbCallbacks = mDevicesForAttributesCallbacks.beginBroadcast(); 127 128 for (int i = 0; i < nbCallbacks; i++) { 129 IDevicesForAttributesCallback cb = 130 mDevicesForAttributesCallbacks.getBroadcastItem(i); 131 List<Pair<AudioAttributes, Boolean>> attrList = 132 mRegisteredAttributesMap.get(cb.asBinder()); 133 134 if (attrList == null) { 135 throw new IllegalStateException("Attribute list must not be null"); 136 } 137 138 for (Pair<AudioAttributes, Boolean> attr : attrList) { 139 ArrayList<AudioDeviceAttributes> devices = 140 getDevicesForAttributes(attr.first, attr.second); 141 if (!mLastDevicesForAttr.containsKey(attr) 142 || !sameDeviceList(devices, mLastDevicesForAttr.get(attr))) { 143 try { 144 cb.onDevicesForAttributesChanged( 145 attr.first, attr.second, devices); 146 } catch (RemoteException e) { } 147 } 148 } 149 } 150 mDevicesForAttributesCallbacks.finishBroadcast(); 151 } 152 } 153 154 interface OnRoutingUpdatedListener { onRoutingUpdatedFromNative()155 void onRoutingUpdatedFromNative(); 156 } 157 setRoutingListener(@ullable OnRoutingUpdatedListener listener)158 static void setRoutingListener(@Nullable OnRoutingUpdatedListener listener) { 159 synchronized (sRoutingListenerLock) { 160 sRoutingListener = listener; 161 } 162 } 163 164 /** 165 * Empties the routing cache if enabled. 166 */ clearRoutingCache()167 public void clearRoutingCache() { 168 if (DEBUG_CACHE) { 169 Log.d(TAG, "---- routing cache clear (from java) ----------"); 170 } 171 invalidateRoutingCache(); 172 } 173 174 /** 175 * @see AudioManager#addOnDevicesForAttributesChangedListener( 176 * AudioAttributes, Executor, OnDevicesForAttributesChangedListener) 177 */ addOnDevicesForAttributesChangedListener(AudioAttributes attributes, boolean forVolume, @NonNull IDevicesForAttributesCallback listener)178 public void addOnDevicesForAttributesChangedListener(AudioAttributes attributes, 179 boolean forVolume, @NonNull IDevicesForAttributesCallback listener) { 180 List<Pair<AudioAttributes, Boolean>> res; 181 final Pair<AudioAttributes, Boolean> attr = new Pair(attributes, forVolume); 182 synchronized (mRegisteredAttributesMap) { 183 res = mRegisteredAttributesMap.get(listener.asBinder()); 184 if (res == null) { 185 res = new ArrayList<>(); 186 mRegisteredAttributesMap.put(listener.asBinder(), res); 187 mDevicesForAttributesCallbacks.register(listener); 188 } 189 190 if (!res.contains(attr)) { 191 res.add(attr); 192 } 193 } 194 195 // Make query on registration to populate cache 196 getDevicesForAttributes(attributes, forVolume); 197 } 198 199 /** 200 * @see AudioManager#removeOnDevicesForAttributesChangedListener( 201 * OnDevicesForAttributesChangedListener) 202 */ removeOnDevicesForAttributesChangedListener( @onNull IDevicesForAttributesCallback listener)203 public void removeOnDevicesForAttributesChangedListener( 204 @NonNull IDevicesForAttributesCallback listener) { 205 synchronized (mRegisteredAttributesMap) { 206 if (!mRegisteredAttributesMap.containsKey(listener.asBinder())) { 207 Log.w(TAG, "listener to be removed is not found."); 208 return; 209 } 210 mRegisteredAttributesMap.remove(listener.asBinder()); 211 mDevicesForAttributesCallbacks.unregister(listener); 212 } 213 } 214 215 /** 216 * Helper function to compare lists of {@link AudioDeviceAttributes} 217 * @return true if the two lists contains the same devices, false otherwise. 218 */ sameDeviceList(@onNull List<AudioDeviceAttributes> a, @NonNull List<AudioDeviceAttributes> b)219 private boolean sameDeviceList(@NonNull List<AudioDeviceAttributes> a, 220 @NonNull List<AudioDeviceAttributes> b) { 221 for (AudioDeviceAttributes device : a) { 222 if (!b.contains(device)) { 223 return false; 224 } 225 } 226 for (AudioDeviceAttributes device : b) { 227 if (!a.contains(device)) { 228 return false; 229 } 230 } 231 return true; 232 } 233 234 /** 235 * Implementation of AudioSystem.VolumeRangeInitRequestCallback 236 */ 237 @Override onVolumeRangeInitializationRequested()238 public void onVolumeRangeInitializationRequested() { 239 final OnVolRangeInitRequestListener listener; 240 synchronized (sVolRangeInitReqListenerLock) { 241 listener = sVolRangeInitReqListener; 242 } 243 if (listener != null) { 244 listener.onVolumeRangeInitRequestFromNative(); 245 } 246 } 247 248 interface OnVolRangeInitRequestListener { onVolumeRangeInitRequestFromNative()249 void onVolumeRangeInitRequestFromNative(); 250 } 251 setVolRangeInitReqListener(@ullable OnVolRangeInitRequestListener listener)252 static void setVolRangeInitReqListener(@Nullable OnVolRangeInitRequestListener listener) { 253 synchronized (sVolRangeInitReqListenerLock) { 254 sVolRangeInitReqListener = listener; 255 } 256 } 257 258 /** 259 * Create a wrapper around the {@link AudioSystem} static methods, all functions are directly 260 * forwarded to the AudioSystem class. 261 * @return an adapter around AudioSystem 262 */ getDefaultAdapter()263 static final synchronized @NonNull AudioSystemAdapter getDefaultAdapter() { 264 if (sSingletonDefaultAdapter == null) { 265 sSingletonDefaultAdapter = new AudioSystemAdapter(); 266 AudioSystem.setRoutingCallback(sSingletonDefaultAdapter); 267 AudioSystem.setVolumeRangeInitRequestCallback(sSingletonDefaultAdapter); 268 if (USE_CACHE_FOR_GETDEVICES) { 269 synchronized (sDeviceCacheLock) { 270 sSingletonDefaultAdapter.mDevicesForAttrCache = 271 new ConcurrentHashMap<>(AudioSystem.getNumStreamTypes()); 272 sSingletonDefaultAdapter.mMethodCacheHit = new int[NB_MEASUREMENTS]; 273 } 274 } 275 if (ENABLE_GETDEVICES_STATS) { 276 sSingletonDefaultAdapter.mMethodCallCounter = new int[NB_MEASUREMENTS]; 277 sSingletonDefaultAdapter.mMethodTimeNs = new long[NB_MEASUREMENTS]; 278 } 279 } 280 return sSingletonDefaultAdapter; 281 } 282 invalidateRoutingCache()283 private void invalidateRoutingCache() { 284 if (DEBUG_CACHE) { 285 Log.d(TAG, "---- clearing cache ----------"); 286 } 287 synchronized (sDeviceCacheLock) { 288 if (mDevicesForAttrCache != null) { 289 mDevicesForAttributesCacheClearTimeMs = System.currentTimeMillis(); 290 // Save latest cache to determine routing updates 291 mLastDevicesForAttr.putAll(mDevicesForAttrCache); 292 293 mDevicesForAttrCache.clear(); 294 } 295 } 296 } 297 298 /** 299 * Same as {@link AudioSystem#getDevicesForAttributes(AudioAttributes)} 300 * @param attributes the attributes for which the routing is queried 301 * @return the devices that the stream with the given attributes would be routed to 302 */ getDevicesForAttributes( @onNull AudioAttributes attributes, boolean forVolume)303 public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributes( 304 @NonNull AudioAttributes attributes, boolean forVolume) { 305 if (!ENABLE_GETDEVICES_STATS) { 306 return getDevicesForAttributesImpl(attributes, forVolume); 307 } 308 mMethodCallCounter[METHOD_GETDEVICESFORATTRIBUTES]++; 309 final long startTime = SystemClock.uptimeNanos(); 310 final ArrayList<AudioDeviceAttributes> res = getDevicesForAttributesImpl( 311 attributes, forVolume); 312 mMethodTimeNs[METHOD_GETDEVICESFORATTRIBUTES] += SystemClock.uptimeNanos() - startTime; 313 return res; 314 } 315 getDevicesForAttributesImpl( @onNull AudioAttributes attributes, boolean forVolume)316 private @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributesImpl( 317 @NonNull AudioAttributes attributes, boolean forVolume) { 318 if (USE_CACHE_FOR_GETDEVICES) { 319 ArrayList<AudioDeviceAttributes> res; 320 final Pair<AudioAttributes, Boolean> key = new Pair(attributes, forVolume); 321 synchronized (sDeviceCacheLock) { 322 res = mDevicesForAttrCache.get(key); 323 if (res == null) { 324 res = AudioSystem.getDevicesForAttributes(attributes, forVolume); 325 mDevicesForAttrCache.put(key, res); 326 if (DEBUG_CACHE) { 327 Log.d(TAG, mMethodNames[METHOD_GETDEVICESFORATTRIBUTES] 328 + attrDeviceToDebugString(attributes, res)); 329 } 330 return res; 331 } 332 // cache hit 333 mMethodCacheHit[METHOD_GETDEVICESFORATTRIBUTES]++; 334 if (DEBUG_CACHE) { 335 final ArrayList<AudioDeviceAttributes> real = 336 AudioSystem.getDevicesForAttributes(attributes, forVolume); 337 if (res.equals(real)) { 338 Log.d(TAG, mMethodNames[METHOD_GETDEVICESFORATTRIBUTES] 339 + attrDeviceToDebugString(attributes, res) + " CACHE"); 340 } else { 341 Log.e(TAG, mMethodNames[METHOD_GETDEVICESFORATTRIBUTES] 342 + attrDeviceToDebugString(attributes, res) 343 + " CACHE ERROR real:" + attrDeviceToDebugString(attributes, real)); 344 } 345 } 346 } 347 return res; 348 } 349 // not using cache 350 return AudioSystem.getDevicesForAttributes(attributes, forVolume); 351 } 352 attrDeviceToDebugString(@onNull AudioAttributes attr, @NonNull List<AudioDeviceAttributes> devices)353 private static String attrDeviceToDebugString(@NonNull AudioAttributes attr, 354 @NonNull List<AudioDeviceAttributes> devices) { 355 return " attrUsage=" + attr.getSystemUsage() + " " 356 + AudioSystem.deviceSetToString(AudioSystem.generateAudioDeviceTypesSet(devices)); 357 } 358 359 /** 360 * Same as {@link AudioSystem#setDeviceConnectionState(AudioDeviceAttributes, int, int)} 361 * @param attributes 362 * @param state 363 * @param codecFormat 364 * @return 365 */ setDeviceConnectionState(AudioDeviceAttributes attributes, int state, int codecFormat)366 public int setDeviceConnectionState(AudioDeviceAttributes attributes, int state, 367 int codecFormat) { 368 invalidateRoutingCache(); 369 return AudioSystem.setDeviceConnectionState(attributes, state, codecFormat); 370 } 371 372 /** 373 * Same as {@link AudioSystem#getDeviceConnectionState(int, String)} 374 * @param device 375 * @param deviceAddress 376 * @return 377 */ getDeviceConnectionState(int device, String deviceAddress)378 public int getDeviceConnectionState(int device, String deviceAddress) { 379 return AudioSystem.getDeviceConnectionState(device, deviceAddress); 380 } 381 382 /** 383 * Same as {@link AudioSystem#handleDeviceConfigChange(int, String, String, int)} 384 * @param device 385 * @param deviceAddress 386 * @param deviceName 387 * @param codecFormat 388 * @return 389 */ handleDeviceConfigChange(int device, String deviceAddress, String deviceName, int codecFormat)390 public int handleDeviceConfigChange(int device, String deviceAddress, 391 String deviceName, int codecFormat) { 392 invalidateRoutingCache(); 393 return AudioSystem.handleDeviceConfigChange(device, deviceAddress, deviceName, 394 codecFormat); 395 } 396 397 /** 398 * Same as {@link AudioSystem#setDevicesRoleForStrategy(int, int, List)} 399 * @param strategy 400 * @param role 401 * @param devices 402 * @return 403 */ setDevicesRoleForStrategy(int strategy, int role, @NonNull List<AudioDeviceAttributes> devices)404 public int setDevicesRoleForStrategy(int strategy, int role, 405 @NonNull List<AudioDeviceAttributes> devices) { 406 invalidateRoutingCache(); 407 return AudioSystem.setDevicesRoleForStrategy(strategy, role, devices); 408 } 409 410 /** 411 * Same as {@link AudioSystem#removeDevicesRoleForStrategy(int, int, List)} 412 * @param strategy 413 * @param role 414 * @param devices 415 * @return 416 */ removeDevicesRoleForStrategy(int strategy, int role, @NonNull List<AudioDeviceAttributes> devices)417 public int removeDevicesRoleForStrategy(int strategy, int role, 418 @NonNull List<AudioDeviceAttributes> devices) { 419 invalidateRoutingCache(); 420 return AudioSystem.removeDevicesRoleForStrategy(strategy, role, devices); 421 } 422 423 /** 424 * Same as {@link AudioSystem#clearDevicesRoleForStrategy(int, int)} 425 * @param strategy 426 * @param role 427 * @return 428 */ clearDevicesRoleForStrategy(int strategy, int role)429 public int clearDevicesRoleForStrategy(int strategy, int role) { 430 invalidateRoutingCache(); 431 return AudioSystem.clearDevicesRoleForStrategy(strategy, role); 432 } 433 434 /** 435 * Same as (@link AudioSystem#setDevicesRoleForCapturePreset(int, List)) 436 * @param capturePreset 437 * @param role 438 * @param devices 439 * @return 440 */ setDevicesRoleForCapturePreset(int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices)441 public int setDevicesRoleForCapturePreset(int capturePreset, int role, 442 @NonNull List<AudioDeviceAttributes> devices) { 443 invalidateRoutingCache(); 444 return AudioSystem.setDevicesRoleForCapturePreset(capturePreset, role, devices); 445 } 446 447 /** 448 * Same as {@link AudioSystem#removeDevicesRoleForCapturePreset(int, int, List)} 449 * @param capturePreset 450 * @param role 451 * @param devicesToRemove 452 * @return 453 */ removeDevicesRoleForCapturePreset( int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devicesToRemove)454 public int removeDevicesRoleForCapturePreset( 455 int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devicesToRemove) { 456 invalidateRoutingCache(); 457 return AudioSystem.removeDevicesRoleForCapturePreset(capturePreset, role, devicesToRemove); 458 } 459 460 /** 461 * Same as {@link AudioSystem#addDevicesRoleForCapturePreset(int, int, List)} 462 * @param capturePreset the capture preset to configure 463 * @param role the role of the devices 464 * @param devices the list of devices to be added as role for the given capture preset 465 * @return {@link #SUCCESS} if successfully added 466 */ addDevicesRoleForCapturePreset( int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices)467 public int addDevicesRoleForCapturePreset( 468 int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) { 469 invalidateRoutingCache(); 470 return AudioSystem.addDevicesRoleForCapturePreset(capturePreset, role, devices); 471 } 472 473 /** 474 * Same as {@link AudioSystem#} 475 * @param capturePreset 476 * @param role 477 * @return 478 */ clearDevicesRoleForCapturePreset(int capturePreset, int role)479 public int clearDevicesRoleForCapturePreset(int capturePreset, int role) { 480 invalidateRoutingCache(); 481 return AudioSystem.clearDevicesRoleForCapturePreset(capturePreset, role); 482 } 483 484 /** 485 * Same as {@link AudioSystem#setParameters(String)} 486 * @param keyValuePairs 487 * @return 488 */ setParameters(String keyValuePairs)489 public int setParameters(String keyValuePairs) { 490 invalidateRoutingCache(); 491 return AudioSystem.setParameters(keyValuePairs); 492 } 493 494 /** 495 * Same as {@link AudioSystem#isMicrophoneMuted()}} 496 * Checks whether the microphone mute is on or off. 497 * @return true if microphone is muted, false if it's not 498 */ isMicrophoneMuted()499 public boolean isMicrophoneMuted() { 500 return AudioSystem.isMicrophoneMuted(); 501 } 502 503 /** 504 * Same as {@link AudioSystem#muteMicrophone(boolean)} 505 * Sets the microphone mute on or off. 506 * 507 * @param on set <var>true</var> to mute the microphone; 508 * <var>false</var> to turn mute off 509 * @return command completion status see AUDIO_STATUS_OK, see AUDIO_STATUS_ERROR 510 */ muteMicrophone(boolean on)511 public int muteMicrophone(boolean on) { 512 return AudioSystem.muteMicrophone(on); 513 } 514 515 /** 516 * Same as {@link AudioSystem#setCurrentImeUid(int)} 517 * Communicate UID of current InputMethodService to audio policy service. 518 */ setCurrentImeUid(int uid)519 public int setCurrentImeUid(int uid) { 520 return AudioSystem.setCurrentImeUid(uid); 521 } 522 523 /** 524 * Same as {@link AudioSystem#isStreamActive(int, int)} 525 */ isStreamActive(int stream, int inPastMs)526 public boolean isStreamActive(int stream, int inPastMs) { 527 return AudioSystem.isStreamActive(stream, inPastMs); 528 } 529 530 /** 531 * Same as {@link AudioSystem#isStreamActiveRemotely(int, int)} 532 * @param stream 533 * @param inPastMs 534 * @return 535 */ isStreamActiveRemotely(int stream, int inPastMs)536 public boolean isStreamActiveRemotely(int stream, int inPastMs) { 537 return AudioSystem.isStreamActiveRemotely(stream, inPastMs); 538 } 539 540 /** 541 * Same as {@link AudioSystem#setStreamVolumeIndexAS(int, int, int)} 542 * @param stream 543 * @param index 544 * @param device 545 * @return 546 */ setStreamVolumeIndexAS(int stream, int index, int device)547 public int setStreamVolumeIndexAS(int stream, int index, int device) { 548 return AudioSystem.setStreamVolumeIndexAS(stream, index, device); 549 } 550 551 /** 552 * Same as {@link AudioSystem#setPhoneState(int, int)} 553 * @param state 554 * @param uid 555 * @return 556 */ setPhoneState(int state, int uid)557 public int setPhoneState(int state, int uid) { 558 invalidateRoutingCache(); 559 return AudioSystem.setPhoneState(state, uid); 560 } 561 562 /** 563 * Same as {@link AudioSystem#setAllowedCapturePolicy(int, int)} 564 * @param uid 565 * @param flags 566 * @return 567 */ setAllowedCapturePolicy(int uid, int flags)568 public int setAllowedCapturePolicy(int uid, int flags) { 569 return AudioSystem.setAllowedCapturePolicy(uid, flags); 570 } 571 572 /** 573 * Same as {@link AudioSystem#setForceUse(int, int)} 574 * @param usage 575 * @param config 576 * @return 577 */ setForceUse(int usage, int config)578 public int setForceUse(int usage, int config) { 579 invalidateRoutingCache(); 580 return AudioSystem.setForceUse(usage, config); 581 } 582 583 /** 584 * Same as {@link AudioSystem#getForceUse(int)} 585 * @param usage 586 * @return 587 */ getForceUse(int usage)588 public int getForceUse(int usage) { 589 return AudioSystem.getForceUse(usage); 590 } 591 592 /** 593 * Same as {@link AudioSystem#registerPolicyMixes(ArrayList, boolean)} 594 * @param mixes 595 * @param register 596 * @return 597 */ registerPolicyMixes(ArrayList<AudioMix> mixes, boolean register)598 public int registerPolicyMixes(ArrayList<AudioMix> mixes, boolean register) { 599 invalidateRoutingCache(); 600 return AudioSystem.registerPolicyMixes(mixes, register); 601 } 602 603 /** 604 * Same as {@link AudioSystem#setUidDeviceAffinities(int, int[], String[])} 605 * @param uid 606 * @param types 607 * @param addresses 608 * @return 609 */ setUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses)610 public int setUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses) { 611 invalidateRoutingCache(); 612 return AudioSystem.setUidDeviceAffinities(uid, types, addresses); 613 } 614 615 /** 616 * Same as {@link AudioSystem#removeUidDeviceAffinities(int)} 617 * @param uid 618 * @return 619 */ removeUidDeviceAffinities(int uid)620 public int removeUidDeviceAffinities(int uid) { 621 invalidateRoutingCache(); 622 return AudioSystem.removeUidDeviceAffinities(uid); 623 } 624 625 /** 626 * Same as {@link AudioSystem#setUserIdDeviceAffinities(int, int[], String[])} 627 * @param userId 628 * @param types 629 * @param addresses 630 * @return 631 */ setUserIdDeviceAffinities(int userId, @NonNull int[] types, @NonNull String[] addresses)632 public int setUserIdDeviceAffinities(int userId, @NonNull int[] types, 633 @NonNull String[] addresses) { 634 invalidateRoutingCache(); 635 return AudioSystem.setUserIdDeviceAffinities(userId, types, addresses); 636 } 637 638 /** 639 * Same as {@link AudioSystem#removeUserIdDeviceAffinities(int)} 640 * @param userId 641 * @return 642 */ removeUserIdDeviceAffinities(int userId)643 public int removeUserIdDeviceAffinities(int userId) { 644 invalidateRoutingCache(); 645 return AudioSystem.removeUserIdDeviceAffinities(userId); 646 } 647 648 /** 649 * Same as {@link AudioSystem#getSoundDoseInterface(ISoundDoseCallback)} 650 * @param callback 651 * @return 652 */ getSoundDoseInterface(ISoundDoseCallback callback)653 public ISoundDose getSoundDoseInterface(ISoundDoseCallback callback) { 654 return AudioSystem.getSoundDoseInterface(callback); 655 } 656 657 /** 658 * Same as 659 * {@link AudioSystem#setPreferredMixerAttributes( 660 * AudioAttributes, int, int, AudioMixerAttributes)} 661 * @param attributes 662 * @param mixerAttributes 663 * @param uid 664 * @param portId 665 * @return 666 */ setPreferredMixerAttributes( @onNull AudioAttributes attributes, int portId, int uid, @NonNull AudioMixerAttributes mixerAttributes)667 public int setPreferredMixerAttributes( 668 @NonNull AudioAttributes attributes, 669 int portId, 670 int uid, 671 @NonNull AudioMixerAttributes mixerAttributes) { 672 return AudioSystem.setPreferredMixerAttributes(attributes, portId, uid, mixerAttributes); 673 } 674 675 /** 676 * Same as {@link AudioSystem#clearPreferredMixerAttributes(AudioAttributes, int, int)} 677 * @param attributes 678 * @param uid 679 * @param portId 680 * @return 681 */ clearPreferredMixerAttributes( @onNull AudioAttributes attributes, int portId, int uid)682 public int clearPreferredMixerAttributes( 683 @NonNull AudioAttributes attributes, int portId, int uid) { 684 return AudioSystem.clearPreferredMixerAttributes(attributes, portId, uid); 685 } 686 687 /** 688 * Part of AudioService dump 689 * @param pw 690 */ dump(PrintWriter pw)691 public void dump(PrintWriter pw) { 692 pw.println("\nAudioSystemAdapter:"); 693 final DateTimeFormatter formatter = DateTimeFormatter 694 .ofPattern("MM-dd HH:mm:ss:SSS") 695 .withLocale(Locale.US) 696 .withZone(ZoneId.systemDefault()); 697 synchronized (sDeviceCacheLock) { 698 pw.println(" last cache clear time: " + formatter.format( 699 Instant.ofEpochMilli(mDevicesForAttributesCacheClearTimeMs))); 700 pw.println(" mDevicesForAttrCache:"); 701 if (mDevicesForAttrCache != null) { 702 for (Map.Entry<Pair<AudioAttributes, Boolean>, ArrayList<AudioDeviceAttributes>> 703 entry : mDevicesForAttrCache.entrySet()) { 704 final AudioAttributes attributes = entry.getKey().first; 705 try { 706 final int stream = attributes.getVolumeControlStream(); 707 pw.println("\t" + attributes + " forVolume: " + entry.getKey().second 708 + " stream: " 709 + AudioSystem.STREAM_NAMES[stream] + "(" + stream + ")"); 710 for (AudioDeviceAttributes devAttr : entry.getValue()) { 711 pw.println("\t\t" + devAttr); 712 } 713 } catch (IllegalArgumentException e) { 714 // dump could fail if attributes do not map to a stream. 715 pw.println("\t dump failed for attributes: " + attributes); 716 Log.e(TAG, "dump failed", e); 717 } 718 } 719 } 720 } 721 722 if (!ENABLE_GETDEVICES_STATS) { 723 // only stats in the rest of this dump 724 return; 725 } 726 for (int i = 0; i < NB_MEASUREMENTS; i++) { 727 pw.println(mMethodNames[i] 728 + ": counter=" + mMethodCallCounter[i] 729 + " time(ms)=" + (mMethodTimeNs[i] / 1E6) 730 + (USE_CACHE_FOR_GETDEVICES 731 ? (" FScacheHit=" + mMethodCacheHit[METHOD_GETDEVICESFORATTRIBUTES]) 732 : "")); 733 } 734 pw.println("\n"); 735 } 736 } 737