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