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.SystemApi;
22 import android.content.Context;
23 import android.content.pm.PackageManager;
24 import android.media.AudioAttributes;
25 import android.media.AudioFocusInfo;
26 import android.media.AudioFormat;
27 import android.media.AudioManager;
28 import android.media.AudioRecord;
29 import android.media.AudioTrack;
30 import android.media.IAudioService;
31 import android.media.MediaRecorder;
32 import android.os.Binder;
33 import android.os.Handler;
34 import android.os.IBinder;
35 import android.os.Looper;
36 import android.os.Message;
37 import android.os.RemoteException;
38 import android.os.ServiceManager;
39 import android.util.Log;
40 import android.util.Slog;
41 
42 import java.lang.annotation.Retention;
43 import java.lang.annotation.RetentionPolicy;
44 import java.util.ArrayList;
45 import java.util.List;
46 
47 /**
48  * @hide
49  * AudioPolicy provides access to the management of audio routing and audio focus.
50  */
51 @SystemApi
52 public class AudioPolicy {
53 
54     private static final String TAG = "AudioPolicy";
55     private static final boolean DEBUG = false;
56     private final Object mLock = new Object();
57 
58     /**
59      * The status of an audio policy that is valid but cannot be used because it is not registered.
60      */
61     @SystemApi
62     public static final int POLICY_STATUS_UNREGISTERED = 1;
63     /**
64      * The status of an audio policy that is valid, successfully registered and thus active.
65      */
66     @SystemApi
67     public static final int POLICY_STATUS_REGISTERED = 2;
68 
69     private int mStatus;
70     private String mRegistrationId;
71     private AudioPolicyStatusListener mStatusListener;
72     private boolean mIsFocusPolicy;
73 
74     /**
75      * The behavior of a policy with regards to audio focus where it relies on the application
76      * to do the ducking, the is the legacy and default behavior.
77      */
78     @SystemApi
79     public static final int FOCUS_POLICY_DUCKING_IN_APP = 0;
80     public static final int FOCUS_POLICY_DUCKING_DEFAULT = FOCUS_POLICY_DUCKING_IN_APP;
81     /**
82      * The behavior of a policy with regards to audio focus where it handles ducking instead
83      * of the application losing focus and being signaled it can duck (as communicated by
84      * {@link android.media.AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}).
85      * <br>Can only be used after having set a listener with
86      * {@link AudioPolicy#setAudioPolicyFocusListener(AudioPolicyFocusListener)}.
87      */
88     @SystemApi
89     public static final int FOCUS_POLICY_DUCKING_IN_POLICY = 1;
90 
91     private AudioPolicyFocusListener mFocusListener;
92 
93     private final AudioPolicyVolumeCallback mVolCb;
94 
95     private Context mContext;
96 
97     private AudioPolicyConfig mConfig;
98 
99     /** @hide */
getConfig()100     public AudioPolicyConfig getConfig() { return mConfig; }
101     /** @hide */
hasFocusListener()102     public boolean hasFocusListener() { return mFocusListener != null; }
103     /** @hide */
isFocusPolicy()104     public boolean isFocusPolicy() { return mIsFocusPolicy; }
105     /** @hide */
isVolumeController()106     public boolean isVolumeController() { return mVolCb != null; }
107 
108     /**
109      * The parameter is guaranteed non-null through the Builder
110      */
AudioPolicy(AudioPolicyConfig config, Context context, Looper looper, AudioPolicyFocusListener fl, AudioPolicyStatusListener sl, boolean isFocusPolicy, AudioPolicyVolumeCallback vc)111     private AudioPolicy(AudioPolicyConfig config, Context context, Looper looper,
112             AudioPolicyFocusListener fl, AudioPolicyStatusListener sl, boolean isFocusPolicy,
113             AudioPolicyVolumeCallback vc) {
114         mConfig = config;
115         mStatus = POLICY_STATUS_UNREGISTERED;
116         mContext = context;
117         if (looper == null) {
118             looper = Looper.getMainLooper();
119         }
120         if (looper != null) {
121             mEventHandler = new EventHandler(this, looper);
122         } else {
123             mEventHandler = null;
124             Log.e(TAG, "No event handler due to looper without a thread");
125         }
126         mFocusListener = fl;
127         mStatusListener = sl;
128         mIsFocusPolicy = isFocusPolicy;
129         mVolCb = vc;
130     }
131 
132     /**
133      * Builder class for {@link AudioPolicy} objects.
134      * By default the policy to be created doesn't govern audio focus decisions.
135      */
136     @SystemApi
137     public static class Builder {
138         private ArrayList<AudioMix> mMixes;
139         private Context mContext;
140         private Looper mLooper;
141         private AudioPolicyFocusListener mFocusListener;
142         private AudioPolicyStatusListener mStatusListener;
143         private boolean mIsFocusPolicy = false;
144         private AudioPolicyVolumeCallback mVolCb;
145 
146         /**
147          * Constructs a new Builder with no audio mixes.
148          * @param context the context for the policy
149          */
150         @SystemApi
Builder(Context context)151         public Builder(Context context) {
152             mMixes = new ArrayList<AudioMix>();
153             mContext = context;
154         }
155 
156         /**
157          * Add an {@link AudioMix} to be part of the audio policy being built.
158          * @param mix a non-null {@link AudioMix} to be part of the audio policy.
159          * @return the same Builder instance.
160          * @throws IllegalArgumentException
161          */
162         @SystemApi
addMix(@onNull AudioMix mix)163         public Builder addMix(@NonNull AudioMix mix) throws IllegalArgumentException {
164             if (mix == null) {
165                 throw new IllegalArgumentException("Illegal null AudioMix argument");
166             }
167             mMixes.add(mix);
168             return this;
169         }
170 
171         /**
172          * Sets the {@link Looper} on which to run the event loop.
173          * @param looper a non-null specific Looper.
174          * @return the same Builder instance.
175          * @throws IllegalArgumentException
176          */
177         @SystemApi
setLooper(@onNull Looper looper)178         public Builder setLooper(@NonNull Looper looper) throws IllegalArgumentException {
179             if (looper == null) {
180                 throw new IllegalArgumentException("Illegal null Looper argument");
181             }
182             mLooper = looper;
183             return this;
184         }
185 
186         /**
187          * Sets the audio focus listener for the policy.
188          * @param l a {@link AudioPolicy.AudioPolicyFocusListener}
189          */
190         @SystemApi
setAudioPolicyFocusListener(AudioPolicyFocusListener l)191         public void setAudioPolicyFocusListener(AudioPolicyFocusListener l) {
192             mFocusListener = l;
193         }
194 
195         /**
196          * Declares whether this policy will grant and deny audio focus through
197          * the {@link AudioPolicy.AudioPolicyFocusListener}.
198          * If set to {@code true}, it is mandatory to set an
199          * {@link AudioPolicy.AudioPolicyFocusListener} in order to successfully build
200          * an {@code AudioPolicy} instance.
201          * @param enforce true if the policy will govern audio focus decisions.
202          * @return the same Builder instance.
203          */
204         @SystemApi
setIsAudioFocusPolicy(boolean isFocusPolicy)205         public Builder setIsAudioFocusPolicy(boolean isFocusPolicy) {
206             mIsFocusPolicy = isFocusPolicy;
207             return this;
208         }
209 
210         /**
211          * Sets the audio policy status listener.
212          * @param l a {@link AudioPolicy.AudioPolicyStatusListener}
213          */
214         @SystemApi
setAudioPolicyStatusListener(AudioPolicyStatusListener l)215         public void setAudioPolicyStatusListener(AudioPolicyStatusListener l) {
216             mStatusListener = l;
217         }
218 
219         @SystemApi
220         /**
221          * Sets the callback to receive all volume key-related events.
222          * The callback will only be called if the device is configured to handle volume events
223          * in the PhoneWindowManager (see config_handleVolumeKeysInWindowManager)
224          * @param vc
225          * @return the same Builder instance.
226          */
setAudioPolicyVolumeCallback(@onNull AudioPolicyVolumeCallback vc)227         public Builder setAudioPolicyVolumeCallback(@NonNull AudioPolicyVolumeCallback vc) {
228             if (vc == null) {
229                 throw new IllegalArgumentException("Invalid null volume callback");
230             }
231             mVolCb = vc;
232             return this;
233         }
234 
235         /**
236          * Combines all of the attributes that have been set on this {@code Builder} and returns a
237          * new {@link AudioPolicy} object.
238          * @return a new {@code AudioPolicy} object.
239          * @throws IllegalStateException if there is no
240          *     {@link AudioPolicy.AudioPolicyStatusListener} but the policy was configured
241          *     as an audio focus policy with {@link #setIsAudioFocusPolicy(boolean)}.
242          */
243         @SystemApi
build()244         public AudioPolicy build() {
245             if (mStatusListener != null) {
246                 // the AudioPolicy status listener includes updates on each mix activity state
247                 for (AudioMix mix : mMixes) {
248                     mix.mCallbackFlags |= AudioMix.CALLBACK_FLAG_NOTIFY_ACTIVITY;
249                 }
250             }
251             if (mIsFocusPolicy && mFocusListener == null) {
252                 throw new IllegalStateException("Cannot be a focus policy without "
253                         + "an AudioPolicyFocusListener");
254             }
255             return new AudioPolicy(new AudioPolicyConfig(mMixes), mContext, mLooper,
256                     mFocusListener, mStatusListener, mIsFocusPolicy, mVolCb);
257         }
258     }
259 
260     /**
261      * @hide
262      * Update the current configuration of the set of audio mixes by adding new ones, while
263      * keeping the policy registered.
264      * This method can only be called on a registered policy.
265      * @param mixes the list of {@link AudioMix} to add
266      * @return {@link AudioManager#SUCCESS} if the change was successful, {@link AudioManager#ERROR}
267      *    otherwise.
268      */
269     @SystemApi
attachMixes(@onNull List<AudioMix> mixes)270     public int attachMixes(@NonNull List<AudioMix> mixes) {
271         if (mixes == null) {
272             throw new IllegalArgumentException("Illegal null list of AudioMix");
273         }
274         synchronized (mLock) {
275             if (mStatus != POLICY_STATUS_REGISTERED) {
276                 throw new IllegalStateException("Cannot alter unregistered AudioPolicy");
277             }
278             final ArrayList<AudioMix> zeMixes = new ArrayList<AudioMix>(mixes.size());
279             for (AudioMix mix : mixes) {
280                 if (mix == null) {
281                     throw new IllegalArgumentException("Illegal null AudioMix in attachMixes");
282                 } else {
283                     zeMixes.add(mix);
284                 }
285             }
286             final AudioPolicyConfig cfg = new AudioPolicyConfig(zeMixes);
287             IAudioService service = getService();
288             try {
289                 final int status = service.addMixForPolicy(cfg, this.cb());
290                 if (status == AudioManager.SUCCESS) {
291                     mConfig.add(zeMixes);
292                 }
293                 return status;
294             } catch (RemoteException e) {
295                 Log.e(TAG, "Dead object in attachMixes", e);
296                 return AudioManager.ERROR;
297             }
298         }
299     }
300 
301     /**
302      * @hide
303      * Update the current configuration of the set of audio mixes by removing some, while
304      * keeping the policy registered.
305      * This method can only be called on a registered policy.
306      * @param mixes the list of {@link AudioMix} to remove
307      * @return {@link AudioManager#SUCCESS} if the change was successful, {@link AudioManager#ERROR}
308      *    otherwise.
309      */
310     @SystemApi
detachMixes(@onNull List<AudioMix> mixes)311     public int detachMixes(@NonNull List<AudioMix> mixes) {
312         if (mixes == null) {
313             throw new IllegalArgumentException("Illegal null list of AudioMix");
314         }
315         synchronized (mLock) {
316             if (mStatus != POLICY_STATUS_REGISTERED) {
317                 throw new IllegalStateException("Cannot alter unregistered AudioPolicy");
318             }
319             final ArrayList<AudioMix> zeMixes = new ArrayList<AudioMix>(mixes.size());
320             for (AudioMix mix : mixes) {
321                 if (mix == null) {
322                     throw new IllegalArgumentException("Illegal null AudioMix in detachMixes");
323                     // TODO also check mix is currently contained in list of mixes
324                 } else {
325                     zeMixes.add(mix);
326                 }
327             }
328             final AudioPolicyConfig cfg = new AudioPolicyConfig(zeMixes);
329             IAudioService service = getService();
330             try {
331                 final int status = service.removeMixForPolicy(cfg, this.cb());
332                 if (status == AudioManager.SUCCESS) {
333                     mConfig.remove(zeMixes);
334                 }
335                 return status;
336             } catch (RemoteException e) {
337                 Log.e(TAG, "Dead object in detachMixes", e);
338                 return AudioManager.ERROR;
339             }
340         }
341     }
342 
setRegistration(String regId)343     public void setRegistration(String regId) {
344         synchronized (mLock) {
345             mRegistrationId = regId;
346             mConfig.setRegistration(regId);
347             if (regId != null) {
348                 mStatus = POLICY_STATUS_REGISTERED;
349             } else {
350                 mStatus = POLICY_STATUS_UNREGISTERED;
351             }
352         }
353         sendMsg(MSG_POLICY_STATUS_CHANGE);
354     }
355 
policyReadyToUse()356     private boolean policyReadyToUse() {
357         synchronized (mLock) {
358             if (mStatus != POLICY_STATUS_REGISTERED) {
359                 Log.e(TAG, "Cannot use unregistered AudioPolicy");
360                 return false;
361             }
362             if (mContext == null) {
363                 Log.e(TAG, "Cannot use AudioPolicy without context");
364                 return false;
365             }
366             if (mRegistrationId == null) {
367                 Log.e(TAG, "Cannot use unregistered AudioPolicy");
368                 return false;
369             }
370         }
371         if (!(PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
372                         android.Manifest.permission.MODIFY_AUDIO_ROUTING))) {
373             Slog.w(TAG, "Cannot use AudioPolicy for pid " + Binder.getCallingPid() + " / uid "
374                     + Binder.getCallingUid() + ", needs MODIFY_AUDIO_ROUTING");
375             return false;
376         }
377         return true;
378     }
379 
checkMixReadyToUse(AudioMix mix, boolean forTrack)380     private void checkMixReadyToUse(AudioMix mix, boolean forTrack)
381             throws IllegalArgumentException{
382         if (mix == null) {
383             String msg = forTrack ? "Invalid null AudioMix for AudioTrack creation"
384                     : "Invalid null AudioMix for AudioRecord creation";
385             throw new IllegalArgumentException(msg);
386         }
387         if (!mConfig.mMixes.contains(mix)) {
388             throw new IllegalArgumentException("Invalid mix: not part of this policy");
389         }
390         if ((mix.getRouteFlags() & AudioMix.ROUTE_FLAG_LOOP_BACK) != AudioMix.ROUTE_FLAG_LOOP_BACK)
391         {
392             throw new IllegalArgumentException("Invalid AudioMix: not defined for loop back");
393         }
394         if (forTrack && (mix.getMixType() != AudioMix.MIX_TYPE_RECORDERS)) {
395             throw new IllegalArgumentException(
396                     "Invalid AudioMix: not defined for being a recording source");
397         }
398         if (!forTrack && (mix.getMixType() != AudioMix.MIX_TYPE_PLAYERS)) {
399             throw new IllegalArgumentException(
400                     "Invalid AudioMix: not defined for capturing playback");
401         }
402     }
403 
404     /**
405      * Returns the current behavior for audio focus-related ducking.
406      * @return {@link #FOCUS_POLICY_DUCKING_IN_APP} or {@link #FOCUS_POLICY_DUCKING_IN_POLICY}
407      */
408     @SystemApi
getFocusDuckingBehavior()409     public int getFocusDuckingBehavior() {
410         return mConfig.mDuckingPolicy;
411     }
412 
413     // Note on implementation: not part of the Builder as there can be only one registered policy
414     // that handles ducking but there can be multiple policies
415     /**
416      * Sets the behavior for audio focus-related ducking.
417      * There must be a focus listener if this policy is to handle ducking.
418      * @param behavior {@link #FOCUS_POLICY_DUCKING_IN_APP} or
419      *     {@link #FOCUS_POLICY_DUCKING_IN_POLICY}
420      * @return {@link AudioManager#SUCCESS} or {@link AudioManager#ERROR} (for instance if there
421      *     is already an audio policy that handles ducking).
422      * @throws IllegalArgumentException
423      * @throws IllegalStateException
424      */
425     @SystemApi
setFocusDuckingBehavior(int behavior)426     public int setFocusDuckingBehavior(int behavior)
427             throws IllegalArgumentException, IllegalStateException {
428         if ((behavior != FOCUS_POLICY_DUCKING_IN_APP)
429                 && (behavior != FOCUS_POLICY_DUCKING_IN_POLICY)) {
430             throw new IllegalArgumentException("Invalid ducking behavior " + behavior);
431         }
432         synchronized (mLock) {
433             if (mStatus != POLICY_STATUS_REGISTERED) {
434                 throw new IllegalStateException(
435                         "Cannot change ducking behavior for unregistered policy");
436             }
437             if ((behavior == FOCUS_POLICY_DUCKING_IN_POLICY)
438                     && (mFocusListener == null)) {
439                 // there must be a focus listener if the policy handles ducking
440                 throw new IllegalStateException(
441                         "Cannot handle ducking without an audio focus listener");
442             }
443             IAudioService service = getService();
444             try {
445                 final int status = service.setFocusPropertiesForPolicy(behavior /*duckingBehavior*/,
446                         this.cb());
447                 if (status == AudioManager.SUCCESS) {
448                     mConfig.mDuckingPolicy = behavior;
449                 }
450                 return status;
451             } catch (RemoteException e) {
452                 Log.e(TAG, "Dead object in setFocusPropertiesForPolicy for behavior", e);
453                 return AudioManager.ERROR;
454             }
455         }
456     }
457 
458     /**
459      * Create an {@link AudioRecord} instance that is associated with the given {@link AudioMix}.
460      * Audio buffers recorded through the created instance will contain the mix of the audio
461      * streams that fed the given mixer.
462      * @param mix a non-null {@link AudioMix} instance whose routing flags was defined with
463      *     {@link AudioMix#ROUTE_FLAG_LOOP_BACK}, previously added to this policy.
464      * @return a new {@link AudioRecord} instance whose data format is the one defined in the
465      *     {@link AudioMix}, or null if this policy was not successfully registered
466      *     with {@link AudioManager#registerAudioPolicy(AudioPolicy)}.
467      * @throws IllegalArgumentException
468      */
469     @SystemApi
createAudioRecordSink(AudioMix mix)470     public AudioRecord createAudioRecordSink(AudioMix mix) throws IllegalArgumentException {
471         if (!policyReadyToUse()) {
472             Log.e(TAG, "Cannot create AudioRecord sink for AudioMix");
473             return null;
474         }
475         checkMixReadyToUse(mix, false/*not for an AudioTrack*/);
476         // create an AudioFormat from the mix format compatible with recording, as the mix
477         // was defined for playback
478         AudioFormat mixFormat = new AudioFormat.Builder(mix.getFormat())
479                 .setChannelMask(AudioFormat.inChannelMaskFromOutChannelMask(
480                         mix.getFormat().getChannelMask()))
481                 .build();
482         // create the AudioRecord, configured for loop back, using the same format as the mix
483         AudioRecord ar = new AudioRecord(
484                 new AudioAttributes.Builder()
485                         .setInternalCapturePreset(MediaRecorder.AudioSource.REMOTE_SUBMIX)
486                         .addTag(addressForTag(mix))
487                         .addTag(AudioRecord.SUBMIX_FIXED_VOLUME)
488                         .build(),
489                 mixFormat,
490                 AudioRecord.getMinBufferSize(mix.getFormat().getSampleRate(),
491                         // using stereo for buffer size to avoid the current poor support for masks
492                         AudioFormat.CHANNEL_IN_STEREO, mix.getFormat().getEncoding()),
493                 AudioManager.AUDIO_SESSION_ID_GENERATE
494                 );
495         return ar;
496     }
497 
498     /**
499      * Create an {@link AudioTrack} instance that is associated with the given {@link AudioMix}.
500      * Audio buffers played through the created instance will be sent to the given mix
501      * to be recorded through the recording APIs.
502      * @param mix a non-null {@link AudioMix} instance whose routing flags was defined with
503      *     {@link AudioMix#ROUTE_FLAG_LOOP_BACK}, previously added to this policy.
504      * @return a new {@link AudioTrack} instance whose data format is the one defined in the
505      *     {@link AudioMix}, or null if this policy was not successfully registered
506      *     with {@link AudioManager#registerAudioPolicy(AudioPolicy)}.
507      * @throws IllegalArgumentException
508      */
509     @SystemApi
createAudioTrackSource(AudioMix mix)510     public AudioTrack createAudioTrackSource(AudioMix mix) throws IllegalArgumentException {
511         if (!policyReadyToUse()) {
512             Log.e(TAG, "Cannot create AudioTrack source for AudioMix");
513             return null;
514         }
515         checkMixReadyToUse(mix, true/*for an AudioTrack*/);
516         // create the AudioTrack, configured for loop back, using the same format as the mix
517         AudioTrack at = new AudioTrack(
518                 new AudioAttributes.Builder()
519                         .setUsage(AudioAttributes.USAGE_VIRTUAL_SOURCE)
520                         .addTag(addressForTag(mix))
521                         .build(),
522                 mix.getFormat(),
523                 AudioTrack.getMinBufferSize(mix.getFormat().getSampleRate(),
524                         mix.getFormat().getChannelMask(), mix.getFormat().getEncoding()),
525                 AudioTrack.MODE_STREAM,
526                 AudioManager.AUDIO_SESSION_ID_GENERATE
527                 );
528         return at;
529     }
530 
531     @SystemApi
getStatus()532     public int getStatus() {
533         return mStatus;
534     }
535 
536     @SystemApi
537     public static abstract class AudioPolicyStatusListener {
onStatusChange()538         public void onStatusChange() {}
onMixStateUpdate(AudioMix mix)539         public void onMixStateUpdate(AudioMix mix) {}
540     }
541 
542     @SystemApi
543     public static abstract class AudioPolicyFocusListener {
onAudioFocusGrant(AudioFocusInfo afi, int requestResult)544         public void onAudioFocusGrant(AudioFocusInfo afi, int requestResult) {}
onAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified)545         public void onAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified) {}
546         /**
547          * Called whenever an application requests audio focus.
548          * Only ever called if the {@link AudioPolicy} was built with
549          * {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to {@code true}.
550          * @param afi information about the focus request and the requester
551          * @param requestResult deprecated after the addition of
552          *     {@link AudioManager#setFocusRequestResult(AudioFocusInfo, int, AudioPolicy)}
553          *     in Android P, always equal to {@link #AUDIOFOCUS_REQUEST_GRANTED}.
554          */
onAudioFocusRequest(AudioFocusInfo afi, int requestResult)555         public void onAudioFocusRequest(AudioFocusInfo afi, int requestResult) {}
556         /**
557          * Called whenever an application abandons audio focus.
558          * Only ever called if the {@link AudioPolicy} was built with
559          * {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to {@code true}.
560          * @param afi information about the focus request being abandoned and the original
561          *     requester.
562          */
onAudioFocusAbandon(AudioFocusInfo afi)563         public void onAudioFocusAbandon(AudioFocusInfo afi) {}
564     }
565 
566     @SystemApi
567     /**
568      * Callback class to receive volume change-related events.
569      * See {@link #Builder.setAudioPolicyVolumeCallback(AudioPolicyCallback)} to configure the
570      * {@link AudioPolicy} to receive those events.
571      *
572      */
573     public static abstract class AudioPolicyVolumeCallback {
574         /** @hide */
AudioPolicyVolumeCallback()575         public AudioPolicyVolumeCallback() {}
576         /**
577          * Called when volume key-related changes are triggered, on the key down event.
578          * @param adjustment the type of volume adjustment for the key.
579          */
onVolumeAdjustment(@udioManager.VolumeAdjustment int adjustment)580         public void onVolumeAdjustment(@AudioManager.VolumeAdjustment int adjustment) {}
581     }
582 
onPolicyStatusChange()583     private void onPolicyStatusChange() {
584         AudioPolicyStatusListener l;
585         synchronized (mLock) {
586             if (mStatusListener == null) {
587                 return;
588             }
589             l = mStatusListener;
590         }
591         l.onStatusChange();
592     }
593 
594     //==================================================
595     // Callback interface
596 
597     /** @hide */
cb()598     public IAudioPolicyCallback cb() { return mPolicyCb; }
599 
600     private final IAudioPolicyCallback mPolicyCb = new IAudioPolicyCallback.Stub() {
601 
602         public void notifyAudioFocusGrant(AudioFocusInfo afi, int requestResult) {
603             sendMsg(MSG_FOCUS_GRANT, afi, requestResult);
604             if (DEBUG) {
605                 Log.v(TAG, "notifyAudioFocusGrant: pack=" + afi.getPackageName() + " client="
606                         + afi.getClientId() + "reqRes=" + requestResult);
607             }
608         }
609 
610         public void notifyAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified) {
611             sendMsg(MSG_FOCUS_LOSS, afi, wasNotified ? 1 : 0);
612             if (DEBUG) {
613                 Log.v(TAG, "notifyAudioFocusLoss: pack=" + afi.getPackageName() + " client="
614                         + afi.getClientId() + "wasNotified=" + wasNotified);
615             }
616         }
617 
618         public void notifyAudioFocusRequest(AudioFocusInfo afi, int requestResult) {
619             sendMsg(MSG_FOCUS_REQUEST, afi, requestResult);
620             if (DEBUG) {
621                 Log.v(TAG, "notifyAudioFocusRequest: pack=" + afi.getPackageName() + " client="
622                         + afi.getClientId() + " gen=" + afi.getGen());
623             }
624         }
625 
626         public void notifyAudioFocusAbandon(AudioFocusInfo afi) {
627             sendMsg(MSG_FOCUS_ABANDON, afi, 0 /* ignored */);
628             if (DEBUG) {
629                 Log.v(TAG, "notifyAudioFocusAbandon: pack=" + afi.getPackageName() + " client="
630                         + afi.getClientId());
631             }
632         }
633 
634         public void notifyMixStateUpdate(String regId, int state) {
635             for (AudioMix mix : mConfig.getMixes()) {
636                 if (mix.getRegistration().equals(regId)) {
637                     mix.mMixState = state;
638                     sendMsg(MSG_MIX_STATE_UPDATE, mix, 0/*ignored*/);
639                     if (DEBUG) {
640                         Log.v(TAG, "notifyMixStateUpdate: regId=" + regId + " state=" + state);
641                     }
642                 }
643             }
644         }
645 
646         public void notifyVolumeAdjust(int adjustment) {
647             sendMsg(MSG_VOL_ADJUST, null /* ignored */, adjustment);
648             if (DEBUG) {
649                 Log.v(TAG, "notifyVolumeAdjust: " + adjustment);
650             }
651         }
652     };
653 
654     //==================================================
655     // Event handling
656     private final EventHandler mEventHandler;
657     private final static int MSG_POLICY_STATUS_CHANGE = 0;
658     private final static int MSG_FOCUS_GRANT = 1;
659     private final static int MSG_FOCUS_LOSS = 2;
660     private final static int MSG_MIX_STATE_UPDATE = 3;
661     private final static int MSG_FOCUS_REQUEST = 4;
662     private final static int MSG_FOCUS_ABANDON = 5;
663     private final static int MSG_VOL_ADJUST = 6;
664 
665     private class EventHandler extends Handler {
EventHandler(AudioPolicy ap, Looper looper)666         public EventHandler(AudioPolicy ap, Looper looper) {
667             super(looper);
668         }
669 
670         @Override
handleMessage(Message msg)671         public void handleMessage(Message msg) {
672             switch(msg.what) {
673                 case MSG_POLICY_STATUS_CHANGE:
674                     onPolicyStatusChange();
675                     break;
676                 case MSG_FOCUS_GRANT:
677                     if (mFocusListener != null) {
678                         mFocusListener.onAudioFocusGrant(
679                                 (AudioFocusInfo) msg.obj, msg.arg1);
680                     }
681                     break;
682                 case MSG_FOCUS_LOSS:
683                     if (mFocusListener != null) {
684                         mFocusListener.onAudioFocusLoss(
685                                 (AudioFocusInfo) msg.obj, msg.arg1 != 0);
686                     }
687                     break;
688                 case MSG_MIX_STATE_UPDATE:
689                     if (mStatusListener != null) {
690                         mStatusListener.onMixStateUpdate((AudioMix) msg.obj);
691                     }
692                     break;
693                 case MSG_FOCUS_REQUEST:
694                     if (mFocusListener != null) {
695                         mFocusListener.onAudioFocusRequest((AudioFocusInfo) msg.obj, msg.arg1);
696                     } else { // should never be null, but don't crash
697                         Log.e(TAG, "Invalid null focus listener for focus request event");
698                     }
699                     break;
700                 case MSG_FOCUS_ABANDON:
701                     if (mFocusListener != null) { // should never be null
702                         mFocusListener.onAudioFocusAbandon((AudioFocusInfo) msg.obj);
703                     } else { // should never be null, but don't crash
704                         Log.e(TAG, "Invalid null focus listener for focus abandon event");
705                     }
706                     break;
707                 case MSG_VOL_ADJUST:
708                     if (mVolCb != null) {
709                         mVolCb.onVolumeAdjustment(msg.arg1);
710                     } else { // should never be null, but don't crash
711                         Log.e(TAG, "Invalid null volume event");
712                     }
713                     break;
714                 default:
715                     Log.e(TAG, "Unknown event " + msg.what);
716             }
717         }
718     }
719 
720     //==========================================================
721     // Utils
addressForTag(AudioMix mix)722     private static String addressForTag(AudioMix mix) {
723         return "addr=" + mix.getRegistration();
724     }
725 
sendMsg(int msg)726     private void sendMsg(int msg) {
727         if (mEventHandler != null) {
728             mEventHandler.sendEmptyMessage(msg);
729         }
730     }
731 
sendMsg(int msg, Object obj, int i)732     private void sendMsg(int msg, Object obj, int i) {
733         if (mEventHandler != null) {
734             mEventHandler.sendMessage(
735                     mEventHandler.obtainMessage(msg, i /*arg1*/, 0 /*arg2, ignored*/, obj));
736         }
737     }
738 
739     private static IAudioService sService;
740 
getService()741     private static IAudioService getService()
742     {
743         if (sService != null) {
744             return sService;
745         }
746         IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
747         sService = IAudioService.Stub.asInterface(b);
748         return sService;
749     }
750 
toLogFriendlyString()751     public String toLogFriendlyString() {
752         String textDump = new String("android.media.audiopolicy.AudioPolicy:\n");
753         textDump += "config=" + mConfig.toLogFriendlyString();
754         return (textDump);
755     }
756 
757     /** @hide */
758     @IntDef({
759         POLICY_STATUS_REGISTERED,
760         POLICY_STATUS_UNREGISTERED
761     })
762     @Retention(RetentionPolicy.SOURCE)
763     public @interface PolicyStatus {}
764 }
765