• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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 android.media.audiopolicy;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.RequiresPermission;
23 import android.annotation.SystemApi;
24 import android.annotation.TestApi;
25 import android.annotation.UserIdInt;
26 import android.app.ActivityManager;
27 import android.content.Context;
28 import android.content.pm.PackageManager;
29 import android.media.AudioAttributes;
30 import android.media.AudioDeviceInfo;
31 import android.media.AudioFocusInfo;
32 import android.media.AudioFormat;
33 import android.media.AudioManager;
34 import android.media.AudioRecord;
35 import android.media.AudioTrack;
36 import android.media.IAudioService;
37 import android.media.MediaRecorder;
38 import android.media.projection.MediaProjection;
39 import android.os.Binder;
40 import android.os.Handler;
41 import android.os.IBinder;
42 import android.os.Looper;
43 import android.os.Message;
44 import android.os.RemoteException;
45 import android.os.ServiceManager;
46 import android.util.Log;
47 import android.util.Slog;
48 
49 import com.android.internal.annotations.GuardedBy;
50 
51 import java.lang.annotation.Retention;
52 import java.lang.annotation.RetentionPolicy;
53 import java.lang.ref.WeakReference;
54 import java.util.ArrayList;
55 import java.util.List;
56 import java.util.Objects;
57 
58 /**
59  * @hide
60  * AudioPolicy provides access to the management of audio routing and audio focus.
61  */
62 @SystemApi
63 public class AudioPolicy {
64 
65     private static final String TAG = "AudioPolicy";
66     private static final boolean DEBUG = false;
67     private final Object mLock = new Object();
68 
69     /**
70      * The status of an audio policy that is valid but cannot be used because it is not registered.
71      */
72     public static final int POLICY_STATUS_UNREGISTERED = 1;
73     /**
74      * The status of an audio policy that is valid, successfully registered and thus active.
75      */
76     public static final int POLICY_STATUS_REGISTERED = 2;
77 
78     private int mStatus;
79     private String mRegistrationId;
80     private AudioPolicyStatusListener mStatusListener;
81     private boolean mIsFocusPolicy;
82     private boolean mIsTestFocusPolicy;
83 
84     /**
85      * The list of AudioTrack instances created to inject audio into the associated mixes
86      * Lazy initialization in {@link #createAudioTrackSource(AudioMix)}
87      */
88     @GuardedBy("mLock")
89     @Nullable private ArrayList<WeakReference<AudioTrack>> mInjectors;
90     /**
91      * The list AudioRecord instances created to capture audio from the associated mixes
92      * Lazy initialization in {@link #createAudioRecordSink(AudioMix)}
93      */
94     @GuardedBy("mLock")
95     @Nullable private ArrayList<WeakReference<AudioRecord>> mCaptors;
96 
97     /**
98      * The behavior of a policy with regards to audio focus where it relies on the application
99      * to do the ducking, the is the legacy and default behavior.
100      */
101     public static final int FOCUS_POLICY_DUCKING_IN_APP = 0;
102     public static final int FOCUS_POLICY_DUCKING_DEFAULT = FOCUS_POLICY_DUCKING_IN_APP;
103     /**
104      * The behavior of a policy with regards to audio focus where it handles ducking instead
105      * of the application losing focus and being signaled it can duck (as communicated by
106      * {@link android.media.AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}).
107      * <br>Can only be used after having set a listener with
108      * {@link AudioPolicy#setAudioPolicyFocusListener(AudioPolicyFocusListener)}.
109      */
110     public static final int FOCUS_POLICY_DUCKING_IN_POLICY = 1;
111 
112     private AudioPolicyFocusListener mFocusListener;
113 
114     private final AudioPolicyVolumeCallback mVolCb;
115 
116     private Context mContext;
117 
118     private AudioPolicyConfig mConfig;
119 
120     private final MediaProjection mProjection;
121 
122     /** @hide */
getConfig()123     public AudioPolicyConfig getConfig() { return mConfig; }
124     /** @hide */
hasFocusListener()125     public boolean hasFocusListener() { return mFocusListener != null; }
126     /** @hide */
isFocusPolicy()127     public boolean isFocusPolicy() { return mIsFocusPolicy; }
128     /** @hide */
isTestFocusPolicy()129     public boolean isTestFocusPolicy() {
130         return mIsTestFocusPolicy;
131     }
132     /** @hide */
isVolumeController()133     public boolean isVolumeController() { return mVolCb != null; }
134     /** @hide */
getMediaProjection()135     public @Nullable MediaProjection getMediaProjection() {
136         return mProjection;
137     }
138 
139     /**
140      * The parameters are guaranteed non-null through the Builder
141      */
AudioPolicy(AudioPolicyConfig config, Context context, Looper looper, AudioPolicyFocusListener fl, AudioPolicyStatusListener sl, boolean isFocusPolicy, boolean isTestFocusPolicy, AudioPolicyVolumeCallback vc, @Nullable MediaProjection projection)142     private AudioPolicy(AudioPolicyConfig config, Context context, Looper looper,
143             AudioPolicyFocusListener fl, AudioPolicyStatusListener sl,
144             boolean isFocusPolicy, boolean isTestFocusPolicy,
145             AudioPolicyVolumeCallback vc, @Nullable MediaProjection projection) {
146         mConfig = config;
147         mStatus = POLICY_STATUS_UNREGISTERED;
148         mContext = context;
149         if (looper == null) {
150             looper = Looper.getMainLooper();
151         }
152         if (looper != null) {
153             mEventHandler = new EventHandler(this, looper);
154         } else {
155             mEventHandler = null;
156             Log.e(TAG, "No event handler due to looper without a thread");
157         }
158         mFocusListener = fl;
159         mStatusListener = sl;
160         mIsFocusPolicy = isFocusPolicy;
161         mIsTestFocusPolicy = isTestFocusPolicy;
162         mVolCb = vc;
163         mProjection = projection;
164     }
165 
166     /**
167      * Builder class for {@link AudioPolicy} objects.
168      * By default the policy to be created doesn't govern audio focus decisions.
169      */
170     public static class Builder {
171         private ArrayList<AudioMix> mMixes;
172         private Context mContext;
173         private Looper mLooper;
174         private AudioPolicyFocusListener mFocusListener;
175         private AudioPolicyStatusListener mStatusListener;
176         private boolean mIsFocusPolicy = false;
177         private boolean mIsTestFocusPolicy = false;
178         private AudioPolicyVolumeCallback mVolCb;
179         private MediaProjection mProjection;
180 
181         /**
182          * Constructs a new Builder with no audio mixes.
183          * @param context the context for the policy
184          */
Builder(Context context)185         public Builder(Context context) {
186             mMixes = new ArrayList<AudioMix>();
187             mContext = context;
188         }
189 
190         /**
191          * Add an {@link AudioMix} to be part of the audio policy being built.
192          * @param mix a non-null {@link AudioMix} to be part of the audio policy.
193          * @return the same Builder instance.
194          * @throws IllegalArgumentException
195          */
196         @NonNull
addMix(@onNull AudioMix mix)197         public Builder addMix(@NonNull AudioMix mix) throws IllegalArgumentException {
198             if (mix == null) {
199                 throw new IllegalArgumentException("Illegal null AudioMix argument");
200             }
201             mMixes.add(mix);
202             return this;
203         }
204 
205         /**
206          * Sets the {@link Looper} on which to run the event loop.
207          * @param looper a non-null specific Looper.
208          * @return the same Builder instance.
209          * @throws IllegalArgumentException
210          */
211         @NonNull
setLooper(@onNull Looper looper)212         public Builder setLooper(@NonNull Looper looper) throws IllegalArgumentException {
213             if (looper == null) {
214                 throw new IllegalArgumentException("Illegal null Looper argument");
215             }
216             mLooper = looper;
217             return this;
218         }
219 
220         /**
221          * Sets the audio focus listener for the policy.
222          * @param l a {@link AudioPolicy.AudioPolicyFocusListener}
223          */
setAudioPolicyFocusListener(AudioPolicyFocusListener l)224         public void setAudioPolicyFocusListener(AudioPolicyFocusListener l) {
225             mFocusListener = l;
226         }
227 
228         /**
229          * Declares whether this policy will grant and deny audio focus through
230          * the {@link AudioPolicy.AudioPolicyFocusListener}.
231          * If set to {@code true}, it is mandatory to set an
232          * {@link AudioPolicy.AudioPolicyFocusListener} in order to successfully build
233          * an {@code AudioPolicy} instance.
234          * @param isFocusPolicy true if the policy will govern audio focus decisions.
235          * @return the same Builder instance.
236          */
237         @NonNull
setIsAudioFocusPolicy(boolean isFocusPolicy)238         public Builder setIsAudioFocusPolicy(boolean isFocusPolicy) {
239             mIsFocusPolicy = isFocusPolicy;
240             return this;
241         }
242 
243         /**
244          * @hide
245          * Test method to declare whether this audio focus policy is for test purposes only.
246          * Having a test policy registered will disable the current focus policy and replace it
247          * with this test policy. When unregistered, the previous focus policy will be restored.
248          * <p>A value of <code>true</code> will be ignored if the AudioPolicy is not also
249          * focus policy.
250          * @param isTestFocusPolicy true if the focus policy to register is for testing purposes.
251          * @return the same Builder instance
252          */
253         @TestApi
254         @NonNull
setIsTestFocusPolicy(boolean isTestFocusPolicy)255         public Builder setIsTestFocusPolicy(boolean isTestFocusPolicy) {
256             mIsTestFocusPolicy = isTestFocusPolicy;
257             return this;
258         }
259 
260         /**
261          * Sets the audio policy status listener.
262          * @param l a {@link AudioPolicy.AudioPolicyStatusListener}
263          */
setAudioPolicyStatusListener(AudioPolicyStatusListener l)264         public void setAudioPolicyStatusListener(AudioPolicyStatusListener l) {
265             mStatusListener = l;
266         }
267 
268         /**
269          * Sets the callback to receive all volume key-related events.
270          * The callback will only be called if the device is configured to handle volume events
271          * in the PhoneWindowManager (see config_handleVolumeKeysInWindowManager)
272          * @param vc
273          * @return the same Builder instance.
274          */
275         @NonNull
setAudioPolicyVolumeCallback(@onNull AudioPolicyVolumeCallback vc)276         public Builder setAudioPolicyVolumeCallback(@NonNull AudioPolicyVolumeCallback vc) {
277             if (vc == null) {
278                 throw new IllegalArgumentException("Invalid null volume callback");
279             }
280             mVolCb = vc;
281             return this;
282         }
283 
284         /**
285          * Set a media projection obtained through createMediaProjection().
286          *
287          * A MediaProjection that can project audio allows to register an audio
288          * policy LOOPBACK|RENDER without the MODIFY_AUDIO_ROUTING permission.
289          *
290          * @hide
291          */
292         @NonNull
setMediaProjection(@onNull MediaProjection projection)293         public Builder setMediaProjection(@NonNull MediaProjection projection) {
294             if (projection == null) {
295                 throw new IllegalArgumentException("Invalid null volume callback");
296             }
297             mProjection = projection;
298             return this;
299 
300         }
301 
302         /**
303          * Combines all of the attributes that have been set on this {@code Builder} and returns a
304          * new {@link AudioPolicy} object.
305          * @return a new {@code AudioPolicy} object.
306          * @throws IllegalStateException if there is no
307          *     {@link AudioPolicy.AudioPolicyStatusListener} but the policy was configured
308          *     as an audio focus policy with {@link #setIsAudioFocusPolicy(boolean)}.
309          */
310         @NonNull
build()311         public AudioPolicy build() {
312             if (mStatusListener != null) {
313                 // the AudioPolicy status listener includes updates on each mix activity state
314                 for (AudioMix mix : mMixes) {
315                     mix.mCallbackFlags |= AudioMix.CALLBACK_FLAG_NOTIFY_ACTIVITY;
316                 }
317             }
318             if (mIsFocusPolicy && mFocusListener == null) {
319                 throw new IllegalStateException("Cannot be a focus policy without "
320                         + "an AudioPolicyFocusListener");
321             }
322             return new AudioPolicy(new AudioPolicyConfig(mMixes), mContext, mLooper,
323                     mFocusListener, mStatusListener, mIsFocusPolicy, mIsTestFocusPolicy,
324                     mVolCb, mProjection);
325         }
326     }
327 
328     /**
329      * Update the current configuration of the set of audio mixes by adding new ones, while
330      * keeping the policy registered.
331      * This method can only be called on a registered policy.
332      * @param mixes the list of {@link AudioMix} to add
333      * @return {@link AudioManager#SUCCESS} if the change was successful, {@link AudioManager#ERROR}
334      *    otherwise.
335      */
attachMixes(@onNull List<AudioMix> mixes)336     public int attachMixes(@NonNull List<AudioMix> mixes) {
337         if (mixes == null) {
338             throw new IllegalArgumentException("Illegal null list of AudioMix");
339         }
340         synchronized (mLock) {
341             if (mStatus != POLICY_STATUS_REGISTERED) {
342                 throw new IllegalStateException("Cannot alter unregistered AudioPolicy");
343             }
344             final ArrayList<AudioMix> zeMixes = new ArrayList<AudioMix>(mixes.size());
345             for (AudioMix mix : mixes) {
346                 if (mix == null) {
347                     throw new IllegalArgumentException("Illegal null AudioMix in attachMixes");
348                 } else {
349                     zeMixes.add(mix);
350                 }
351             }
352             final AudioPolicyConfig cfg = new AudioPolicyConfig(zeMixes);
353             IAudioService service = getService();
354             try {
355                 final int status = service.addMixForPolicy(cfg, this.cb());
356                 if (status == AudioManager.SUCCESS) {
357                     mConfig.add(zeMixes);
358                 }
359                 return status;
360             } catch (RemoteException e) {
361                 Log.e(TAG, "Dead object in attachMixes", e);
362                 return AudioManager.ERROR;
363             }
364         }
365     }
366 
367     /**
368      * Update the current configuration of the set of audio mixes by removing some, while
369      * keeping the policy registered.
370      * This method can only be called on a registered policy.
371      * @param mixes the list of {@link AudioMix} to remove
372      * @return {@link AudioManager#SUCCESS} if the change was successful, {@link AudioManager#ERROR}
373      *    otherwise.
374      */
detachMixes(@onNull List<AudioMix> mixes)375     public int detachMixes(@NonNull List<AudioMix> mixes) {
376         if (mixes == null) {
377             throw new IllegalArgumentException("Illegal null list of AudioMix");
378         }
379         synchronized (mLock) {
380             if (mStatus != POLICY_STATUS_REGISTERED) {
381                 throw new IllegalStateException("Cannot alter unregistered AudioPolicy");
382             }
383             final ArrayList<AudioMix> zeMixes = new ArrayList<AudioMix>(mixes.size());
384             for (AudioMix mix : mixes) {
385                 if (mix == null) {
386                     throw new IllegalArgumentException("Illegal null AudioMix in detachMixes");
387                     // TODO also check mix is currently contained in list of mixes
388                 } else {
389                     zeMixes.add(mix);
390                 }
391             }
392             final AudioPolicyConfig cfg = new AudioPolicyConfig(zeMixes);
393             IAudioService service = getService();
394             try {
395                 final int status = service.removeMixForPolicy(cfg, this.cb());
396                 if (status == AudioManager.SUCCESS) {
397                     mConfig.remove(zeMixes);
398                 }
399                 return status;
400             } catch (RemoteException e) {
401                 Log.e(TAG, "Dead object in detachMixes", e);
402                 return AudioManager.ERROR;
403             }
404         }
405     }
406 
407     /**
408      * @hide
409      * Configures the audio framework so that all audio streams originating from the given UID
410      * can only come from a set of audio devices.
411      * For this routing to be operational, a number of {@link AudioMix} instances must have been
412      * previously registered on this policy, and routed to a super-set of the given audio devices
413      * with {@link AudioMix.Builder#setDevice(android.media.AudioDeviceInfo)}. Note that having
414      * multiple devices in the list doesn't imply the signals will be duplicated on the different
415      * audio devices, final routing will depend on the {@link AudioAttributes} of the sounds being
416      * played.
417      * @param uid UID of the application to affect.
418      * @param devices list of devices to which the audio stream of the application may be routed.
419      * @return true if the change was successful, false otherwise.
420      */
421     @SystemApi
setUidDeviceAffinity(int uid, @NonNull List<AudioDeviceInfo> devices)422     public boolean setUidDeviceAffinity(int uid, @NonNull List<AudioDeviceInfo> devices) {
423         if (devices == null) {
424             throw new IllegalArgumentException("Illegal null list of audio devices");
425         }
426         synchronized (mLock) {
427             if (mStatus != POLICY_STATUS_REGISTERED) {
428                 throw new IllegalStateException("Cannot use unregistered AudioPolicy");
429             }
430             final int[] deviceTypes = new int[devices.size()];
431             final String[] deviceAdresses = new String[devices.size()];
432             int i = 0;
433             for (AudioDeviceInfo device : devices) {
434                 if (device == null) {
435                     throw new IllegalArgumentException(
436                             "Illegal null AudioDeviceInfo in setUidDeviceAffinity");
437                 }
438                 deviceTypes[i] =
439                         AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType());
440                 deviceAdresses[i] = device.getAddress();
441                 i++;
442             }
443             final IAudioService service = getService();
444             try {
445                 final int status = service.setUidDeviceAffinity(this.cb(),
446                         uid, deviceTypes, deviceAdresses);
447                 return (status == AudioManager.SUCCESS);
448             } catch (RemoteException e) {
449                 Log.e(TAG, "Dead object in setUidDeviceAffinity", e);
450                 return false;
451             }
452         }
453     }
454 
455     /**
456      * @hide
457      * Removes audio device affinity previously set by
458      * {@link #setUidDeviceAffinity(int, java.util.List)}.
459      * @param uid UID of the application affected.
460      * @return true if the change was successful, false otherwise.
461      */
462     @SystemApi
removeUidDeviceAffinity(int uid)463     public boolean removeUidDeviceAffinity(int uid) {
464         synchronized (mLock) {
465             if (mStatus != POLICY_STATUS_REGISTERED) {
466                 throw new IllegalStateException("Cannot use unregistered AudioPolicy");
467             }
468             final IAudioService service = getService();
469             try {
470                 final int status = service.removeUidDeviceAffinity(this.cb(), uid);
471                 return (status == AudioManager.SUCCESS);
472             } catch (RemoteException e) {
473                 Log.e(TAG, "Dead object in removeUidDeviceAffinity", e);
474                 return false;
475             }
476         }
477     }
478 
479     /**
480      * @hide
481      * Removes audio device affinity previously set by
482      * {@link #setUserIdDeviceAffinity(int, java.util.List)}.
483      * @param userId userId of the application affected, as obtained via
484      * {@link UserHandle#getIdentifier}. Not to be confused with application uid.
485      * @return true if the change was successful, false otherwise.
486      */
487     @SystemApi
removeUserIdDeviceAffinity(@serIdInt int userId)488     public boolean removeUserIdDeviceAffinity(@UserIdInt int userId) {
489         synchronized (mLock) {
490             if (mStatus != POLICY_STATUS_REGISTERED) {
491                 throw new IllegalStateException("Cannot use unregistered AudioPolicy");
492             }
493             final IAudioService service = getService();
494             try {
495                 final int status = service.removeUserIdDeviceAffinity(this.cb(), userId);
496                 return (status == AudioManager.SUCCESS);
497             } catch (RemoteException e) {
498                 Log.e(TAG, "Dead object in removeUserIdDeviceAffinity", e);
499                 return false;
500             }
501         }
502     }
503 
504     /**
505      * @hide
506      * Configures the audio framework so that all audio streams originating from the given user
507      * can only come from a set of audio devices.
508      * For this routing to be operational, a number of {@link AudioMix} instances must have been
509      * previously registered on this policy, and routed to a super-set of the given audio devices
510      * with {@link AudioMix.Builder#setDevice(android.media.AudioDeviceInfo)}. Note that having
511      * multiple devices in the list doesn't imply the signals will be duplicated on the different
512      * audio devices, final routing will depend on the {@link AudioAttributes} of the sounds being
513      * played.
514      * @param userId userId of the application affected, as obtained via
515      * {@link UserHandle#getIdentifier}. Not to be confused with application uid.
516      * @param devices list of devices to which the audio stream of the application may be routed.
517      * @return true if the change was successful, false otherwise.
518      */
519     @SystemApi
setUserIdDeviceAffinity(@serIdInt int userId, @NonNull List<AudioDeviceInfo> devices)520     public boolean setUserIdDeviceAffinity(@UserIdInt int userId,
521             @NonNull List<AudioDeviceInfo> devices) {
522         Objects.requireNonNull(devices, "Illegal null list of audio devices");
523         synchronized (mLock) {
524             if (mStatus != POLICY_STATUS_REGISTERED) {
525                 throw new IllegalStateException("Cannot use unregistered AudioPolicy");
526             }
527             final int[] deviceTypes = new int[devices.size()];
528             final String[] deviceAddresses = new String[devices.size()];
529             int i = 0;
530             for (AudioDeviceInfo device : devices) {
531                 if (device == null) {
532                     throw new IllegalArgumentException(
533                             "Illegal null AudioDeviceInfo in setUserIdDeviceAffinity");
534                 }
535                 deviceTypes[i] =
536                         AudioDeviceInfo.convertDeviceTypeToInternalDevice(device.getType());
537                 deviceAddresses[i] = device.getAddress();
538                 i++;
539             }
540             final IAudioService service = getService();
541             try {
542                 final int status = service.setUserIdDeviceAffinity(this.cb(),
543                         userId, deviceTypes, deviceAddresses);
544                 return (status == AudioManager.SUCCESS);
545             } catch (RemoteException e) {
546                 Log.e(TAG, "Dead object in setUserIdDeviceAffinity", e);
547                 return false;
548             }
549         }
550     }
551 
552     /** @hide */
reset()553     public void reset() {
554         setRegistration(null);
555         mConfig.reset();
556     }
557 
setRegistration(String regId)558     public void setRegistration(String regId) {
559         synchronized (mLock) {
560             mRegistrationId = regId;
561             mConfig.setRegistration(regId);
562             if (regId != null) {
563                 mStatus = POLICY_STATUS_REGISTERED;
564             } else {
565                 mStatus = POLICY_STATUS_UNREGISTERED;
566             }
567         }
568         sendMsg(MSG_POLICY_STATUS_CHANGE);
569     }
570 
571     /**@hide*/
getRegistration()572     public String getRegistration() {
573         return mRegistrationId;
574     }
575 
policyReadyToUse()576     private boolean policyReadyToUse() {
577         synchronized (mLock) {
578             if (mStatus != POLICY_STATUS_REGISTERED) {
579                 Log.e(TAG, "Cannot use unregistered AudioPolicy");
580                 return false;
581             }
582             if (mRegistrationId == null) {
583                 Log.e(TAG, "Cannot use unregistered AudioPolicy");
584                 return false;
585             }
586         }
587 
588         // Loopback|capture only need an audio projection, everything else need MODIFY_AUDIO_ROUTING
589         boolean canModifyAudioRouting = PackageManager.PERMISSION_GRANTED
590                 == checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING);
591 
592         boolean canInterceptCallAudio = PackageManager.PERMISSION_GRANTED
593                 == checkCallingOrSelfPermission(
594                         android.Manifest.permission.CALL_AUDIO_INTERCEPTION);
595 
596         boolean canProjectAudio;
597         try {
598             canProjectAudio = mProjection != null && mProjection.getProjection().canProjectAudio();
599         } catch (RemoteException e) {
600             Log.e(TAG, "Failed to check if MediaProjection#canProjectAudio");
601             throw e.rethrowFromSystemServer();
602         }
603 
604         if (!((isLoopbackRenderPolicy() && canProjectAudio)
605                 || (isCallRedirectionPolicy() && canInterceptCallAudio)
606                 || canModifyAudioRouting)) {
607             Slog.w(TAG, "Cannot use AudioPolicy for pid " + Binder.getCallingPid() + " / uid "
608                     + Binder.getCallingUid() + ", needs MODIFY_AUDIO_ROUTING or "
609                     + "MediaProjection that can project audio.");
610             return false;
611         }
612         return true;
613     }
614 
isLoopbackRenderPolicy()615     private boolean isLoopbackRenderPolicy() {
616         synchronized (mLock) {
617             return mConfig.mMixes.stream().allMatch(mix -> mix.getRouteFlags()
618                     == (mix.ROUTE_FLAG_RENDER | mix.ROUTE_FLAG_LOOP_BACK));
619         }
620     }
621 
isCallRedirectionPolicy()622     private boolean isCallRedirectionPolicy() {
623         synchronized (mLock) {
624             for (AudioMix mix : mConfig.mMixes) {
625                 if (mix.isForCallRedirection()) {
626                     return true;
627                 }
628             }
629             return false;
630         }
631     }
632 
633     /**
634      * Returns {@link PackageManager#PERMISSION_GRANTED} if the caller has the given permission.
635      */
checkCallingOrSelfPermission(String permission)636     private @PackageManager.PermissionResult int checkCallingOrSelfPermission(String permission) {
637         if (mContext != null) {
638             return mContext.checkCallingOrSelfPermission(permission);
639         }
640         Slog.v(TAG, "Null context, checking permission via ActivityManager");
641         int pid = Binder.getCallingPid();
642         int uid = Binder.getCallingUid();
643         try {
644             return ActivityManager.getService().checkPermission(permission, pid, uid);
645         } catch (RemoteException e) {
646             throw e.rethrowFromSystemServer();
647         }
648     }
649 
checkMixReadyToUse(AudioMix mix, boolean forTrack)650     private void checkMixReadyToUse(AudioMix mix, boolean forTrack)
651             throws IllegalArgumentException{
652         if (mix == null) {
653             String msg = forTrack ? "Invalid null AudioMix for AudioTrack creation"
654                     : "Invalid null AudioMix for AudioRecord creation";
655             throw new IllegalArgumentException(msg);
656         }
657         if (!mConfig.mMixes.contains(mix)) {
658             throw new IllegalArgumentException("Invalid mix: not part of this policy");
659         }
660         if ((mix.getRouteFlags() & AudioMix.ROUTE_FLAG_LOOP_BACK) != AudioMix.ROUTE_FLAG_LOOP_BACK)
661         {
662             throw new IllegalArgumentException("Invalid AudioMix: not defined for loop back");
663         }
664         if (forTrack && (mix.getMixType() != AudioMix.MIX_TYPE_RECORDERS)) {
665             throw new IllegalArgumentException(
666                     "Invalid AudioMix: not defined for being a recording source");
667         }
668         if (!forTrack && (mix.getMixType() != AudioMix.MIX_TYPE_PLAYERS)) {
669             throw new IllegalArgumentException(
670                     "Invalid AudioMix: not defined for capturing playback");
671         }
672     }
673 
674     /**
675      * Returns the current behavior for audio focus-related ducking.
676      * @return {@link #FOCUS_POLICY_DUCKING_IN_APP} or {@link #FOCUS_POLICY_DUCKING_IN_POLICY}
677      */
getFocusDuckingBehavior()678     public int getFocusDuckingBehavior() {
679         return mConfig.mDuckingPolicy;
680     }
681 
682     // Note on implementation: not part of the Builder as there can be only one registered policy
683     // that handles ducking but there can be multiple policies
684     /**
685      * Sets the behavior for audio focus-related ducking.
686      * There must be a focus listener if this policy is to handle ducking.
687      * @param behavior {@link #FOCUS_POLICY_DUCKING_IN_APP} or
688      *     {@link #FOCUS_POLICY_DUCKING_IN_POLICY}
689      * @return {@link AudioManager#SUCCESS} or {@link AudioManager#ERROR} (for instance if there
690      *     is already an audio policy that handles ducking).
691      * @throws IllegalArgumentException
692      * @throws IllegalStateException
693      */
setFocusDuckingBehavior(int behavior)694     public int setFocusDuckingBehavior(int behavior)
695             throws IllegalArgumentException, IllegalStateException {
696         if ((behavior != FOCUS_POLICY_DUCKING_IN_APP)
697                 && (behavior != FOCUS_POLICY_DUCKING_IN_POLICY)) {
698             throw new IllegalArgumentException("Invalid ducking behavior " + behavior);
699         }
700         synchronized (mLock) {
701             if (mStatus != POLICY_STATUS_REGISTERED) {
702                 throw new IllegalStateException(
703                         "Cannot change ducking behavior for unregistered policy");
704             }
705             if ((behavior == FOCUS_POLICY_DUCKING_IN_POLICY)
706                     && (mFocusListener == null)) {
707                 // there must be a focus listener if the policy handles ducking
708                 throw new IllegalStateException(
709                         "Cannot handle ducking without an audio focus listener");
710             }
711             IAudioService service = getService();
712             try {
713                 final int status = service.setFocusPropertiesForPolicy(behavior /*duckingBehavior*/,
714                         this.cb());
715                 if (status == AudioManager.SUCCESS) {
716                     mConfig.mDuckingPolicy = behavior;
717                 }
718                 return status;
719             } catch (RemoteException e) {
720                 Log.e(TAG, "Dead object in setFocusPropertiesForPolicy for behavior", e);
721                 return AudioManager.ERROR;
722             }
723         }
724     }
725 
726     /**
727      * Returns the list of entries in the focus stack.
728      * The list is ordered with increasing rank of focus ownership, where the last entry is at the
729      * top of the focus stack and is the current focus owner.
730      * @return the ordered list of focus owners
731      * @see AudioManager#registerAudioPolicy(AudioPolicy)
732      */
733     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
getFocusStack()734     public @NonNull List<AudioFocusInfo> getFocusStack() {
735         try {
736             return getService().getFocusStack();
737         } catch (RemoteException e) {
738             throw e.rethrowFromSystemServer();
739         }
740     }
741 
742     /**
743      * Send AUDIOFOCUS_LOSS to a specific stack entry, causing it to be notified of the focus
744      * loss, and for it to exit the focus stack (its focus listener will not be invoked after that).
745      * This operation is only valid for a registered policy (with
746      * {@link AudioManager#registerAudioPolicy(AudioPolicy)}) that is also set as the policy focus
747      * listener (with {@link Builder#setAudioPolicyFocusListener(AudioPolicyFocusListener)}.
748      * @param focusLoser the stack entry that is exiting the stack through a focus loss
749      * @return false if the focusLoser wasn't found in the stack, true otherwise
750      * @throws IllegalStateException if used on an unregistered policy, or a registered policy
751      *     with no {@link AudioPolicyFocusListener} set
752      * @see AudioManager#registerAudioPolicy(AudioPolicy)
753      * @see Builder#setAudioPolicyStatusListener(AudioPolicyStatusListener)
754      */
755     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
sendFocusLoss(@onNull AudioFocusInfo focusLoser)756     public boolean sendFocusLoss(@NonNull AudioFocusInfo focusLoser) throws IllegalStateException {
757         Objects.requireNonNull(focusLoser);
758         try {
759             return getService().sendFocusLoss(focusLoser, cb());
760         } catch (RemoteException e) {
761             throw e.rethrowFromSystemServer();
762         }
763     }
764 
765     /**
766      * Create an {@link AudioRecord} instance that is associated with the given {@link AudioMix}.
767      * Audio buffers recorded through the created instance will contain the mix of the audio
768      * streams that fed the given mixer.
769      * @param mix a non-null {@link AudioMix} instance whose routing flags was defined with
770      *     {@link AudioMix#ROUTE_FLAG_LOOP_BACK}, previously added to this policy.
771      * @return a new {@link AudioRecord} instance whose data format is the one defined in the
772      *     {@link AudioMix}, or null if this policy was not successfully registered
773      *     with {@link AudioManager#registerAudioPolicy(AudioPolicy)}.
774      * @throws IllegalArgumentException
775      */
createAudioRecordSink(AudioMix mix)776     public AudioRecord createAudioRecordSink(AudioMix mix) throws IllegalArgumentException {
777         if (!policyReadyToUse()) {
778             Log.e(TAG, "Cannot create AudioRecord sink for AudioMix");
779             return null;
780         }
781         checkMixReadyToUse(mix, false/*not for an AudioTrack*/);
782         // create an AudioFormat from the mix format compatible with recording, as the mix
783         // was defined for playback
784         AudioFormat mixFormat = new AudioFormat.Builder(mix.getFormat())
785                 .setChannelMask(AudioFormat.inChannelMaskFromOutChannelMask(
786                         mix.getFormat().getChannelMask()))
787                 .build();
788 
789         AudioAttributes.Builder ab = new AudioAttributes.Builder()
790                 .setInternalCapturePreset(MediaRecorder.AudioSource.REMOTE_SUBMIX)
791                 .addTag(addressForTag(mix))
792                 .addTag(AudioRecord.SUBMIX_FIXED_VOLUME);
793         if (mix.isForCallRedirection()) {
794             ab.setForCallRedirection();
795         }
796         // create the AudioRecord, configured for loop back, using the same format as the mix
797         AudioRecord ar = new AudioRecord(ab.build(),
798                 mixFormat,
799                 AudioRecord.getMinBufferSize(mix.getFormat().getSampleRate(),
800                         // using stereo for buffer size to avoid the current poor support for masks
801                         AudioFormat.CHANNEL_IN_STEREO, mix.getFormat().getEncoding()),
802                 AudioManager.AUDIO_SESSION_ID_GENERATE
803                 );
804         synchronized (mLock) {
805             if (mCaptors == null) {
806                 mCaptors = new ArrayList<>(1);
807             }
808             mCaptors.add(new WeakReference<AudioRecord>(ar));
809         }
810         return ar;
811     }
812 
813     /**
814      * Create an {@link AudioTrack} instance that is associated with the given {@link AudioMix}.
815      * Audio buffers played through the created instance will be sent to the given mix
816      * to be recorded through the recording APIs.
817      * @param mix a non-null {@link AudioMix} instance whose routing flags was defined with
818      *     {@link AudioMix#ROUTE_FLAG_LOOP_BACK}, previously added to this policy.
819      * @return a new {@link AudioTrack} instance whose data format is the one defined in the
820      *     {@link AudioMix}, or null if this policy was not successfully registered
821      *     with {@link AudioManager#registerAudioPolicy(AudioPolicy)}.
822      * @throws IllegalArgumentException
823      */
createAudioTrackSource(AudioMix mix)824     public AudioTrack createAudioTrackSource(AudioMix mix) throws IllegalArgumentException {
825         if (!policyReadyToUse()) {
826             Log.e(TAG, "Cannot create AudioTrack source for AudioMix");
827             return null;
828         }
829         checkMixReadyToUse(mix, true/*for an AudioTrack*/);
830         // create the AudioTrack, configured for loop back, using the same format as the mix
831         AudioAttributes.Builder ab = new AudioAttributes.Builder()
832                 .setUsage(AudioAttributes.USAGE_VIRTUAL_SOURCE)
833                 .addTag(addressForTag(mix));
834         if (mix.isForCallRedirection()) {
835             ab.setForCallRedirection();
836         }
837         AudioTrack at = new AudioTrack(ab.build(),
838                 mix.getFormat(),
839                 AudioTrack.getMinBufferSize(mix.getFormat().getSampleRate(),
840                         mix.getFormat().getChannelMask(), mix.getFormat().getEncoding()),
841                 AudioTrack.MODE_STREAM,
842                 AudioManager.AUDIO_SESSION_ID_GENERATE
843                 );
844         synchronized (mLock) {
845             if (mInjectors == null) {
846                 mInjectors = new ArrayList<>(1);
847             }
848             mInjectors.add(new WeakReference<AudioTrack>(at));
849         }
850         return at;
851     }
852 
853     /**
854      * @hide
855      */
invalidateCaptorsAndInjectors()856     public void invalidateCaptorsAndInjectors() {
857         if (!policyReadyToUse()) {
858             return;
859         }
860         synchronized (mLock) {
861             if (mInjectors != null) {
862                 for (final WeakReference<AudioTrack> weakTrack : mInjectors) {
863                     final AudioTrack track = weakTrack.get();
864                     if (track == null) {
865                         break;
866                     }
867                     try {
868                         // TODO: add synchronous versions
869                         track.stop();
870                         track.flush();
871                     } catch (IllegalStateException e) {
872                         // ignore exception, AudioTrack could have already been stopped or
873                         // released by the user of the AudioPolicy
874                     }
875                 }
876             }
877             if (mCaptors != null) {
878                 for (final WeakReference<AudioRecord> weakRecord : mCaptors) {
879                     final AudioRecord record = weakRecord.get();
880                     if (record == null) {
881                         break;
882                     }
883                     try {
884                         // TODO: if needed: implement an invalidate method
885                         record.stop();
886                     } catch (IllegalStateException e) {
887                         // ignore exception, AudioRecord could have already been stopped or
888                         // released by the user of the AudioPolicy
889                     }
890                 }
891             }
892         }
893     }
894 
getStatus()895     public int getStatus() {
896         return mStatus;
897     }
898 
899     public static abstract class AudioPolicyStatusListener {
onStatusChange()900         public void onStatusChange() {}
onMixStateUpdate(AudioMix mix)901         public void onMixStateUpdate(AudioMix mix) {}
902     }
903 
904     public static abstract class AudioPolicyFocusListener {
onAudioFocusGrant(AudioFocusInfo afi, int requestResult)905         public void onAudioFocusGrant(AudioFocusInfo afi, int requestResult) {}
onAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified)906         public void onAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified) {}
907         /**
908          * Called whenever an application requests audio focus.
909          * Only ever called if the {@link AudioPolicy} was built with
910          * {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to {@code true}.
911          * @param afi information about the focus request and the requester
912          * @param requestResult deprecated after the addition of
913          *     {@link AudioManager#setFocusRequestResult(AudioFocusInfo, int, AudioPolicy)}
914          *     in Android P, always equal to {@link #AUDIOFOCUS_REQUEST_GRANTED}.
915          */
onAudioFocusRequest(AudioFocusInfo afi, int requestResult)916         public void onAudioFocusRequest(AudioFocusInfo afi, int requestResult) {}
917         /**
918          * Called whenever an application abandons audio focus.
919          * Only ever called if the {@link AudioPolicy} was built with
920          * {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to {@code true}.
921          * @param afi information about the focus request being abandoned and the original
922          *     requester.
923          */
onAudioFocusAbandon(AudioFocusInfo afi)924         public void onAudioFocusAbandon(AudioFocusInfo afi) {}
925     }
926 
927     /**
928      * Callback class to receive volume change-related events.
929      * See {@link #Builder.setAudioPolicyVolumeCallback(AudioPolicyCallback)} to configure the
930      * {@link AudioPolicy} to receive those events.
931      *
932      */
933     public static abstract class AudioPolicyVolumeCallback {
AudioPolicyVolumeCallback()934         public AudioPolicyVolumeCallback() {}
935         /**
936          * Called when volume key-related changes are triggered, on the key down event.
937          * @param adjustment the type of volume adjustment for the key.
938          */
onVolumeAdjustment(@udioManager.VolumeAdjustment int adjustment)939         public void onVolumeAdjustment(@AudioManager.VolumeAdjustment int adjustment) {}
940     }
941 
onPolicyStatusChange()942     private void onPolicyStatusChange() {
943         AudioPolicyStatusListener l;
944         synchronized (mLock) {
945             if (mStatusListener == null) {
946                 return;
947             }
948             l = mStatusListener;
949         }
950         l.onStatusChange();
951     }
952 
953     //==================================================
954     // Callback interface
955 
956     /** @hide */
cb()957     public IAudioPolicyCallback cb() { return mPolicyCb; }
958 
959     private final IAudioPolicyCallback mPolicyCb = new IAudioPolicyCallback.Stub() {
960 
961         public void notifyAudioFocusGrant(AudioFocusInfo afi, int requestResult) {
962             sendMsg(MSG_FOCUS_GRANT, afi, requestResult);
963             if (DEBUG) {
964                 Log.v(TAG, "notifyAudioFocusGrant: pack=" + afi.getPackageName() + " client="
965                         + afi.getClientId() + "reqRes=" + requestResult);
966             }
967         }
968 
969         public void notifyAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified) {
970             sendMsg(MSG_FOCUS_LOSS, afi, wasNotified ? 1 : 0);
971             if (DEBUG) {
972                 Log.v(TAG, "notifyAudioFocusLoss: pack=" + afi.getPackageName() + " client="
973                         + afi.getClientId() + "wasNotified=" + wasNotified);
974             }
975         }
976 
977         public void notifyAudioFocusRequest(AudioFocusInfo afi, int requestResult) {
978             sendMsg(MSG_FOCUS_REQUEST, afi, requestResult);
979             if (DEBUG) {
980                 Log.v(TAG, "notifyAudioFocusRequest: pack=" + afi.getPackageName() + " client="
981                         + afi.getClientId() + " gen=" + afi.getGen());
982             }
983         }
984 
985         public void notifyAudioFocusAbandon(AudioFocusInfo afi) {
986             sendMsg(MSG_FOCUS_ABANDON, afi, 0 /* ignored */);
987             if (DEBUG) {
988                 Log.v(TAG, "notifyAudioFocusAbandon: pack=" + afi.getPackageName() + " client="
989                         + afi.getClientId());
990             }
991         }
992 
993         public void notifyMixStateUpdate(String regId, int state) {
994             for (AudioMix mix : mConfig.getMixes()) {
995                 if (mix.getRegistration().equals(regId)) {
996                     mix.mMixState = state;
997                     sendMsg(MSG_MIX_STATE_UPDATE, mix, 0/*ignored*/);
998                     if (DEBUG) {
999                         Log.v(TAG, "notifyMixStateUpdate: regId=" + regId + " state=" + state);
1000                     }
1001                 }
1002             }
1003         }
1004 
1005         public void notifyVolumeAdjust(int adjustment) {
1006             sendMsg(MSG_VOL_ADJUST, null /* ignored */, adjustment);
1007             if (DEBUG) {
1008                 Log.v(TAG, "notifyVolumeAdjust: " + adjustment);
1009             }
1010         }
1011 
1012         public void notifyUnregistration() {
1013             setRegistration(null);
1014         }
1015     };
1016 
1017     //==================================================
1018     // Event handling
1019     private final EventHandler mEventHandler;
1020     private final static int MSG_POLICY_STATUS_CHANGE = 0;
1021     private final static int MSG_FOCUS_GRANT = 1;
1022     private final static int MSG_FOCUS_LOSS = 2;
1023     private final static int MSG_MIX_STATE_UPDATE = 3;
1024     private final static int MSG_FOCUS_REQUEST = 4;
1025     private final static int MSG_FOCUS_ABANDON = 5;
1026     private final static int MSG_VOL_ADJUST = 6;
1027 
1028     private class EventHandler extends Handler {
EventHandler(AudioPolicy ap, Looper looper)1029         public EventHandler(AudioPolicy ap, Looper looper) {
1030             super(looper);
1031         }
1032 
1033         @Override
handleMessage(Message msg)1034         public void handleMessage(Message msg) {
1035             switch(msg.what) {
1036                 case MSG_POLICY_STATUS_CHANGE:
1037                     onPolicyStatusChange();
1038                     break;
1039                 case MSG_FOCUS_GRANT:
1040                     if (mFocusListener != null) {
1041                         mFocusListener.onAudioFocusGrant(
1042                                 (AudioFocusInfo) msg.obj, msg.arg1);
1043                     }
1044                     break;
1045                 case MSG_FOCUS_LOSS:
1046                     if (mFocusListener != null) {
1047                         mFocusListener.onAudioFocusLoss(
1048                                 (AudioFocusInfo) msg.obj, msg.arg1 != 0);
1049                     }
1050                     break;
1051                 case MSG_MIX_STATE_UPDATE:
1052                     if (mStatusListener != null) {
1053                         mStatusListener.onMixStateUpdate((AudioMix) msg.obj);
1054                     }
1055                     break;
1056                 case MSG_FOCUS_REQUEST:
1057                     if (mFocusListener != null) {
1058                         mFocusListener.onAudioFocusRequest((AudioFocusInfo) msg.obj, msg.arg1);
1059                     } else { // should never be null, but don't crash
1060                         Log.e(TAG, "Invalid null focus listener for focus request event");
1061                     }
1062                     break;
1063                 case MSG_FOCUS_ABANDON:
1064                     if (mFocusListener != null) { // should never be null
1065                         mFocusListener.onAudioFocusAbandon((AudioFocusInfo) msg.obj);
1066                     } else { // should never be null, but don't crash
1067                         Log.e(TAG, "Invalid null focus listener for focus abandon event");
1068                     }
1069                     break;
1070                 case MSG_VOL_ADJUST:
1071                     if (mVolCb != null) {
1072                         mVolCb.onVolumeAdjustment(msg.arg1);
1073                     } else { // should never be null, but don't crash
1074                         Log.e(TAG, "Invalid null volume event");
1075                     }
1076                     break;
1077                 default:
1078                     Log.e(TAG, "Unknown event " + msg.what);
1079             }
1080         }
1081     }
1082 
1083     //==========================================================
1084     // Utils
addressForTag(AudioMix mix)1085     private static String addressForTag(AudioMix mix) {
1086         return "addr=" + mix.getRegistration();
1087     }
1088 
sendMsg(int msg)1089     private void sendMsg(int msg) {
1090         if (mEventHandler != null) {
1091             mEventHandler.sendEmptyMessage(msg);
1092         }
1093     }
1094 
sendMsg(int msg, Object obj, int i)1095     private void sendMsg(int msg, Object obj, int i) {
1096         if (mEventHandler != null) {
1097             mEventHandler.sendMessage(
1098                     mEventHandler.obtainMessage(msg, i /*arg1*/, 0 /*arg2, ignored*/, obj));
1099         }
1100     }
1101 
1102     private static IAudioService sService;
1103 
getService()1104     private static IAudioService getService()
1105     {
1106         if (sService != null) {
1107             return sService;
1108         }
1109         IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
1110         sService = IAudioService.Stub.asInterface(b);
1111         return sService;
1112     }
1113 
toLogFriendlyString()1114     public String toLogFriendlyString() {
1115         String textDump = new String("android.media.audiopolicy.AudioPolicy:\n");
1116         textDump += "config=" + mConfig.toLogFriendlyString();
1117         return (textDump);
1118     }
1119 
1120     /** @hide */
1121     @IntDef({
1122         POLICY_STATUS_REGISTERED,
1123         POLICY_STATUS_UNREGISTERED
1124     })
1125     @Retention(RetentionPolicy.SOURCE)
1126     public @interface PolicyStatus {}
1127 }
1128