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