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