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