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.AudioSystem; 24 import android.media.audiopolicy.AudioMix; 25 import android.os.SystemClock; 26 import android.util.Log; 27 28 import com.android.internal.annotations.GuardedBy; 29 30 import java.io.PrintWriter; 31 import java.util.ArrayList; 32 import java.util.List; 33 import java.util.concurrent.ConcurrentHashMap; 34 35 /** 36 * Provides an adapter to access functionality of the android.media.AudioSystem class for device 37 * related functionality. 38 * Use the "real" AudioSystem through the default adapter. 39 * Use the "always ok" adapter to avoid dealing with the APM behaviors during a test. 40 */ 41 public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback { 42 43 private static final String TAG = "AudioSystemAdapter"; 44 45 // initialized in factory getDefaultAdapter() 46 private static AudioSystemAdapter sSingletonDefaultAdapter; 47 48 /** 49 * should be false by default unless enabling measurements of method call counts and time spent 50 * in measured methods 51 */ 52 private static final boolean ENABLE_GETDEVICES_STATS = false; 53 private static final int NB_MEASUREMENTS = 2; 54 private static final int METHOD_GETDEVICESFORSTREAM = 0; 55 private static final int METHOD_GETDEVICESFORATTRIBUTES = 1; 56 private long[] mMethodTimeNs; 57 private int[] mMethodCallCounter; 58 private String[] mMethodNames = {"getDevicesForStream", "getDevicesForAttributes"}; 59 60 private static final boolean USE_CACHE_FOR_GETDEVICES = true; 61 private ConcurrentHashMap<Integer, Integer> mDevicesForStreamCache; 62 private ConcurrentHashMap<AudioAttributes, ArrayList<AudioDeviceAttributes>> 63 mDevicesForAttrCache; 64 private int[] mMethodCacheHit; 65 private static final Object sRoutingListenerLock = new Object(); 66 @GuardedBy("sRoutingListenerLock") 67 private static @Nullable OnRoutingUpdatedListener sRoutingListener; 68 69 /** 70 * should be false except when trying to debug caching errors. When true, the value retrieved 71 * from the cache will be compared against the real queried value, which defeats the purpose of 72 * the cache in terms of performance. 73 */ 74 private static final boolean DEBUG_CACHE = false; 75 76 /** 77 * Implementation of AudioSystem.RoutingUpdateCallback 78 */ 79 @Override onRoutingUpdated()80 public void onRoutingUpdated() { 81 if (DEBUG_CACHE) { 82 Log.d(TAG, "---- onRoutingUpdated (from native) ----------"); 83 } 84 invalidateRoutingCache(); 85 final OnRoutingUpdatedListener listener; 86 synchronized (sRoutingListenerLock) { 87 listener = sRoutingListener; 88 } 89 if (listener != null) { 90 listener.onRoutingUpdatedFromNative(); 91 } 92 } 93 94 interface OnRoutingUpdatedListener { onRoutingUpdatedFromNative()95 void onRoutingUpdatedFromNative(); 96 } 97 setRoutingListener(@ullable OnRoutingUpdatedListener listener)98 static void setRoutingListener(@Nullable OnRoutingUpdatedListener listener) { 99 synchronized (sRoutingListenerLock) { 100 sRoutingListener = listener; 101 } 102 } 103 104 /** 105 * Create a wrapper around the {@link AudioSystem} static methods, all functions are directly 106 * forwarded to the AudioSystem class. 107 * @return an adapter around AudioSystem 108 */ getDefaultAdapter()109 static final synchronized @NonNull AudioSystemAdapter getDefaultAdapter() { 110 if (sSingletonDefaultAdapter == null) { 111 sSingletonDefaultAdapter = new AudioSystemAdapter(); 112 AudioSystem.setRoutingCallback(sSingletonDefaultAdapter); 113 if (USE_CACHE_FOR_GETDEVICES) { 114 sSingletonDefaultAdapter.mDevicesForStreamCache = 115 new ConcurrentHashMap<>(AudioSystem.getNumStreamTypes()); 116 sSingletonDefaultAdapter.mDevicesForAttrCache = 117 new ConcurrentHashMap<>(AudioSystem.getNumStreamTypes()); 118 sSingletonDefaultAdapter.mMethodCacheHit = new int[NB_MEASUREMENTS]; 119 } 120 if (ENABLE_GETDEVICES_STATS) { 121 sSingletonDefaultAdapter.mMethodCallCounter = new int[NB_MEASUREMENTS]; 122 sSingletonDefaultAdapter.mMethodTimeNs = new long[NB_MEASUREMENTS]; 123 } 124 } 125 return sSingletonDefaultAdapter; 126 } 127 invalidateRoutingCache()128 private void invalidateRoutingCache() { 129 if (DEBUG_CACHE) { 130 Log.d(TAG, "---- clearing cache ----------"); 131 } 132 if (mDevicesForStreamCache != null) { 133 synchronized (mDevicesForStreamCache) { 134 mDevicesForStreamCache.clear(); 135 } 136 } 137 if (mDevicesForAttrCache != null) { 138 synchronized (mDevicesForAttrCache) { 139 mDevicesForAttrCache.clear(); 140 } 141 } 142 } 143 144 /** 145 * Same as {@link AudioSystem#getDevicesForStream(int)} 146 * @param stream a valid stream type 147 * @return a mask of device types 148 */ getDevicesForStream(int stream)149 public int getDevicesForStream(int stream) { 150 if (!ENABLE_GETDEVICES_STATS) { 151 return getDevicesForStreamImpl(stream); 152 } 153 mMethodCallCounter[METHOD_GETDEVICESFORSTREAM]++; 154 final long startTime = SystemClock.uptimeNanos(); 155 final int res = getDevicesForStreamImpl(stream); 156 mMethodTimeNs[METHOD_GETDEVICESFORSTREAM] += SystemClock.uptimeNanos() - startTime; 157 return res; 158 } 159 getDevicesForStreamImpl(int stream)160 private int getDevicesForStreamImpl(int stream) { 161 if (USE_CACHE_FOR_GETDEVICES) { 162 Integer res; 163 synchronized (mDevicesForStreamCache) { 164 res = mDevicesForStreamCache.get(stream); 165 if (res == null) { 166 res = AudioSystem.getDevicesForStream(stream); 167 mDevicesForStreamCache.put(stream, res); 168 if (DEBUG_CACHE) { 169 Log.d(TAG, mMethodNames[METHOD_GETDEVICESFORSTREAM] 170 + streamDeviceToDebugString(stream, res)); 171 } 172 return res; 173 } 174 // cache hit 175 mMethodCacheHit[METHOD_GETDEVICESFORSTREAM]++; 176 if (DEBUG_CACHE) { 177 final int real = AudioSystem.getDevicesForStream(stream); 178 if (res == real) { 179 Log.d(TAG, mMethodNames[METHOD_GETDEVICESFORSTREAM] 180 + streamDeviceToDebugString(stream, res) + " CACHE"); 181 } else { 182 Log.e(TAG, mMethodNames[METHOD_GETDEVICESFORSTREAM] 183 + streamDeviceToDebugString(stream, res) 184 + " CACHE ERROR real dev=0x" + Integer.toHexString(real)); 185 } 186 } 187 } 188 return res; 189 } 190 // not using cache 191 return AudioSystem.getDevicesForStream(stream); 192 } 193 streamDeviceToDebugString(int stream, int dev)194 private static String streamDeviceToDebugString(int stream, int dev) { 195 return " stream=" + stream + " dev=0x" + Integer.toHexString(dev); 196 } 197 198 /** 199 * Same as {@link AudioSystem#getDevicesForAttributes(AudioAttributes)} 200 * @param attributes the attributes for which the routing is queried 201 * @return the devices that the stream with the given attributes would be routed to 202 */ getDevicesForAttributes( @onNull AudioAttributes attributes)203 public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributes( 204 @NonNull AudioAttributes attributes) { 205 if (!ENABLE_GETDEVICES_STATS) { 206 return getDevicesForAttributesImpl(attributes); 207 } 208 mMethodCallCounter[METHOD_GETDEVICESFORATTRIBUTES]++; 209 final long startTime = SystemClock.uptimeNanos(); 210 final ArrayList<AudioDeviceAttributes> res = getDevicesForAttributesImpl(attributes); 211 mMethodTimeNs[METHOD_GETDEVICESFORATTRIBUTES] += SystemClock.uptimeNanos() - startTime; 212 return res; 213 } 214 getDevicesForAttributesImpl( @onNull AudioAttributes attributes)215 private @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributesImpl( 216 @NonNull AudioAttributes attributes) { 217 if (USE_CACHE_FOR_GETDEVICES) { 218 ArrayList<AudioDeviceAttributes> res; 219 synchronized (mDevicesForAttrCache) { 220 res = mDevicesForAttrCache.get(attributes); 221 if (res == null) { 222 res = AudioSystem.getDevicesForAttributes(attributes); 223 mDevicesForAttrCache.put(attributes, res); 224 if (DEBUG_CACHE) { 225 Log.d(TAG, mMethodNames[METHOD_GETDEVICESFORATTRIBUTES] 226 + attrDeviceToDebugString(attributes, res)); 227 } 228 return res; 229 } 230 // cache hit 231 mMethodCacheHit[METHOD_GETDEVICESFORATTRIBUTES]++; 232 if (DEBUG_CACHE) { 233 final ArrayList<AudioDeviceAttributes> real = 234 AudioSystem.getDevicesForAttributes(attributes); 235 if (res.equals(real)) { 236 Log.d(TAG, mMethodNames[METHOD_GETDEVICESFORATTRIBUTES] 237 + attrDeviceToDebugString(attributes, res) + " CACHE"); 238 } else { 239 Log.e(TAG, mMethodNames[METHOD_GETDEVICESFORATTRIBUTES] 240 + attrDeviceToDebugString(attributes, res) 241 + " CACHE ERROR real:" + attrDeviceToDebugString(attributes, real)); 242 } 243 } 244 } 245 return res; 246 } 247 // not using cache 248 return AudioSystem.getDevicesForAttributes(attributes); 249 } 250 attrDeviceToDebugString(@onNull AudioAttributes attr, @NonNull ArrayList<AudioDeviceAttributes> devices)251 private static String attrDeviceToDebugString(@NonNull AudioAttributes attr, 252 @NonNull ArrayList<AudioDeviceAttributes> devices) { 253 String ds = " attrUsage=" + attr.getSystemUsage(); 254 for (AudioDeviceAttributes ada : devices) { 255 ds = ds.concat(" dev=0x" + Integer.toHexString(ada.getInternalType())); 256 } 257 return ds; 258 } 259 260 /** 261 * Same as {@link AudioSystem#setDeviceConnectionState(int, int, String, String, int)} 262 * @param device 263 * @param state 264 * @param deviceAddress 265 * @param deviceName 266 * @param codecFormat 267 * @return 268 */ setDeviceConnectionState(int device, int state, String deviceAddress, String deviceName, int codecFormat)269 public int setDeviceConnectionState(int device, int state, String deviceAddress, 270 String deviceName, int codecFormat) { 271 invalidateRoutingCache(); 272 return AudioSystem.setDeviceConnectionState(device, state, deviceAddress, deviceName, 273 codecFormat); 274 } 275 276 /** 277 * Same as {@link AudioSystem#getDeviceConnectionState(int, String)} 278 * @param device 279 * @param deviceAddress 280 * @return 281 */ getDeviceConnectionState(int device, String deviceAddress)282 public int getDeviceConnectionState(int device, String deviceAddress) { 283 return AudioSystem.getDeviceConnectionState(device, deviceAddress); 284 } 285 286 /** 287 * Same as {@link AudioSystem#handleDeviceConfigChange(int, String, String, int)} 288 * @param device 289 * @param deviceAddress 290 * @param deviceName 291 * @param codecFormat 292 * @return 293 */ handleDeviceConfigChange(int device, String deviceAddress, String deviceName, int codecFormat)294 public int handleDeviceConfigChange(int device, String deviceAddress, 295 String deviceName, int codecFormat) { 296 invalidateRoutingCache(); 297 return AudioSystem.handleDeviceConfigChange(device, deviceAddress, deviceName, 298 codecFormat); 299 } 300 301 /** 302 * Same as {@link AudioSystem#setDevicesRoleForStrategy(int, int, List)} 303 * @param strategy 304 * @param role 305 * @param devices 306 * @return 307 */ setDevicesRoleForStrategy(int strategy, int role, @NonNull List<AudioDeviceAttributes> devices)308 public int setDevicesRoleForStrategy(int strategy, int role, 309 @NonNull List<AudioDeviceAttributes> devices) { 310 invalidateRoutingCache(); 311 return AudioSystem.setDevicesRoleForStrategy(strategy, role, devices); 312 } 313 314 /** 315 * Same as {@link AudioSystem#removeDevicesRoleForStrategy(int, int)} 316 * @param strategy 317 * @param role 318 * @return 319 */ removeDevicesRoleForStrategy(int strategy, int role)320 public int removeDevicesRoleForStrategy(int strategy, int role) { 321 invalidateRoutingCache(); 322 return AudioSystem.removeDevicesRoleForStrategy(strategy, role); 323 } 324 325 /** 326 * Same as (@link AudioSystem#setDevicesRoleForCapturePreset(int, List)) 327 * @param capturePreset 328 * @param role 329 * @param devices 330 * @return 331 */ setDevicesRoleForCapturePreset(int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices)332 public int setDevicesRoleForCapturePreset(int capturePreset, int role, 333 @NonNull List<AudioDeviceAttributes> devices) { 334 invalidateRoutingCache(); 335 return AudioSystem.setDevicesRoleForCapturePreset(capturePreset, role, devices); 336 } 337 338 /** 339 * Same as {@link AudioSystem#removeDevicesRoleForCapturePreset(int, int, int[], String[])} 340 * @param capturePreset 341 * @param role 342 * @param devicesToRemove 343 * @return 344 */ removeDevicesRoleForCapturePreset( int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devicesToRemove)345 public int removeDevicesRoleForCapturePreset( 346 int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devicesToRemove) { 347 invalidateRoutingCache(); 348 return AudioSystem.removeDevicesRoleForCapturePreset(capturePreset, role, devicesToRemove); 349 } 350 351 /** 352 * Same as {@link AudioSystem#} 353 * @param capturePreset 354 * @param role 355 * @return 356 */ clearDevicesRoleForCapturePreset(int capturePreset, int role)357 public int clearDevicesRoleForCapturePreset(int capturePreset, int role) { 358 invalidateRoutingCache(); 359 return AudioSystem.clearDevicesRoleForCapturePreset(capturePreset, role); 360 } 361 362 /** 363 * Same as {@link AudioSystem#setParameters(String)} 364 * @param keyValuePairs 365 * @return 366 */ setParameters(String keyValuePairs)367 public int setParameters(String keyValuePairs) { 368 return AudioSystem.setParameters(keyValuePairs); 369 } 370 371 /** 372 * Same as {@link AudioSystem#isMicrophoneMuted()}} 373 * Checks whether the microphone mute is on or off. 374 * @return true if microphone is muted, false if it's not 375 */ isMicrophoneMuted()376 public boolean isMicrophoneMuted() { 377 return AudioSystem.isMicrophoneMuted(); 378 } 379 380 /** 381 * Same as {@link AudioSystem#muteMicrophone(boolean)} 382 * Sets the microphone mute on or off. 383 * 384 * @param on set <var>true</var> to mute the microphone; 385 * <var>false</var> to turn mute off 386 * @return command completion status see AUDIO_STATUS_OK, see AUDIO_STATUS_ERROR 387 */ muteMicrophone(boolean on)388 public int muteMicrophone(boolean on) { 389 return AudioSystem.muteMicrophone(on); 390 } 391 392 /** 393 * Same as {@link AudioSystem#setHotwordDetectionServiceUid(int)} 394 * Communicate UID of current HotwordDetectionService to audio policy service. 395 */ setHotwordDetectionServiceUid(int uid)396 public int setHotwordDetectionServiceUid(int uid) { 397 return AudioSystem.setHotwordDetectionServiceUid(uid); 398 } 399 400 /** 401 * Same as {@link AudioSystem#setCurrentImeUid(int)} 402 * Communicate UID of current InputMethodService to audio policy service. 403 */ setCurrentImeUid(int uid)404 public int setCurrentImeUid(int uid) { 405 return AudioSystem.setCurrentImeUid(uid); 406 } 407 408 /** 409 * Same as {@link AudioSystem#isStreamActive(int, int)} 410 */ isStreamActive(int stream, int inPastMs)411 public boolean isStreamActive(int stream, int inPastMs) { 412 return AudioSystem.isStreamActive(stream, inPastMs); 413 } 414 415 /** 416 * Same as {@link AudioSystem#isStreamActiveRemotely(int, int)} 417 * @param stream 418 * @param inPastMs 419 * @return 420 */ isStreamActiveRemotely(int stream, int inPastMs)421 public boolean isStreamActiveRemotely(int stream, int inPastMs) { 422 return AudioSystem.isStreamActiveRemotely(stream, inPastMs); 423 } 424 425 /** 426 * Same as {@link AudioSystem#setPhoneState(int, int)} 427 * @param state 428 * @param uid 429 * @return 430 */ setPhoneState(int state, int uid)431 public int setPhoneState(int state, int uid) { 432 invalidateRoutingCache(); 433 return AudioSystem.setPhoneState(state, uid); 434 } 435 436 /** 437 * Same as {@link AudioSystem#setAllowedCapturePolicy(int, int)} 438 * @param uid 439 * @param flags 440 * @return 441 */ setAllowedCapturePolicy(int uid, int flags)442 public int setAllowedCapturePolicy(int uid, int flags) { 443 return AudioSystem.setAllowedCapturePolicy(uid, flags); 444 } 445 446 /** 447 * Same as {@link AudioSystem#setForceUse(int, int)} 448 * @param usage 449 * @param config 450 * @return 451 */ setForceUse(int usage, int config)452 public int setForceUse(int usage, int config) { 453 invalidateRoutingCache(); 454 return AudioSystem.setForceUse(usage, config); 455 } 456 457 /** 458 * Same as {@link AudioSystem#getForceUse(int)} 459 * @param usage 460 * @return 461 */ getForceUse(int usage)462 public int getForceUse(int usage) { 463 return AudioSystem.getForceUse(usage); 464 } 465 466 /** 467 * Same as {@link AudioSystem#registerPolicyMixes(ArrayList, boolean)} 468 * @param mixes 469 * @param register 470 * @return 471 */ registerPolicyMixes(ArrayList<AudioMix> mixes, boolean register)472 public int registerPolicyMixes(ArrayList<AudioMix> mixes, boolean register) { 473 invalidateRoutingCache(); 474 return AudioSystem.registerPolicyMixes(mixes, register); 475 } 476 477 /** 478 * Same as {@link AudioSystem#setUidDeviceAffinities(int, int[], String[])} 479 * @param uid 480 * @param types 481 * @param addresses 482 * @return 483 */ setUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses)484 public int setUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses) { 485 invalidateRoutingCache(); 486 return AudioSystem.setUidDeviceAffinities(uid, types, addresses); 487 } 488 489 /** 490 * Same as {@link AudioSystem#removeUidDeviceAffinities(int)} 491 * @param uid 492 * @return 493 */ removeUidDeviceAffinities(int uid)494 public int removeUidDeviceAffinities(int uid) { 495 invalidateRoutingCache(); 496 return AudioSystem.removeUidDeviceAffinities(uid); 497 } 498 499 /** 500 * Same as {@link AudioSystem#setUserIdDeviceAffinities(int, int[], String[])} 501 * @param userId 502 * @param types 503 * @param addresses 504 * @return 505 */ setUserIdDeviceAffinities(int userId, @NonNull int[] types, @NonNull String[] addresses)506 public int setUserIdDeviceAffinities(int userId, @NonNull int[] types, 507 @NonNull String[] addresses) { 508 invalidateRoutingCache(); 509 return AudioSystem.setUserIdDeviceAffinities(userId, types, addresses); 510 } 511 512 /** 513 * Same as {@link AudioSystem#removeUserIdDeviceAffinities(int)} 514 * @param userId 515 * @return 516 */ removeUserIdDeviceAffinities(int userId)517 public int removeUserIdDeviceAffinities(int userId) { 518 invalidateRoutingCache(); 519 return AudioSystem.removeUserIdDeviceAffinities(userId); 520 } 521 522 /** 523 * Part of AudioService dump 524 * @param pw 525 */ dump(PrintWriter pw)526 public void dump(PrintWriter pw) { 527 pw.println("\nAudioSystemAdapter:"); 528 pw.println(" mDevicesForStreamCache:"); 529 if (mDevicesForStreamCache != null) { 530 for (Integer stream : mDevicesForStreamCache.keySet()) { 531 pw.println("\t stream:" + stream + " device:" 532 + AudioSystem.getOutputDeviceName(mDevicesForStreamCache.get(stream))); 533 } 534 } 535 pw.println(" mDevicesForAttrCache:"); 536 if (mDevicesForAttrCache != null) { 537 for (AudioAttributes attr : mDevicesForAttrCache.keySet()) { 538 pw.println("\t" + attr); 539 for (AudioDeviceAttributes devAttr : mDevicesForAttrCache.get(attr)) { 540 pw.println("\t\t" + devAttr); 541 } 542 } 543 } 544 545 if (!ENABLE_GETDEVICES_STATS) { 546 // only stats in the rest of this dump 547 return; 548 } 549 for (int i = 0; i < NB_MEASUREMENTS; i++) { 550 pw.println(mMethodNames[i] 551 + ": counter=" + mMethodCallCounter[i] 552 + " time(ms)=" + (mMethodTimeNs[i] / 1E6) 553 + (USE_CACHE_FOR_GETDEVICES 554 ? (" FScacheHit=" + mMethodCacheHit[METHOD_GETDEVICESFORSTREAM]) : "")); 555 } 556 pw.println("\n"); 557 } 558 } 559