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