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