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