Home | History | Annotate | Download | only in audiopolicy
      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 */
     97     public AudioPolicyConfig getConfig() { return mConfig; }
     98     /** @hide */
     99     public boolean hasFocusListener() { return mFocusListener != null; }
    100     /** @hide */
    101     public boolean isFocusPolicy() { return mIsFocusPolicy; }
    102 
    103     /**
    104      * The parameter is guaranteed non-null through the Builder
    105      */
    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
    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
    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
    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
    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
    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
    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
    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 
    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 
    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 
    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
    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
    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
    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
    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
    424     public int getStatus() {
    425         return mStatus;
    426     }
    427 
    428     @SystemApi
    429     public static abstract class AudioPolicyStatusListener {
    430         public void onStatusChange() {}
    431         public void onMixStateUpdate(AudioMix mix) {}
    432     }
    433 
    434     @SystemApi
    435     public static abstract class AudioPolicyFocusListener {
    436         public void onAudioFocusGrant(AudioFocusInfo afi, int requestResult) {}
    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          */
    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          */
    455         public void onAudioFocusAbandon(AudioFocusInfo afi) {}
    456     }
    457 
    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 */
    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 {
    533         public EventHandler(AudioPolicy ap, Looper looper) {
    534             super(looper);
    535         }
    536 
    537         @Override
    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
    582     private static String addressForTag(AudioMix mix) {
    583         return "addr=" + mix.getRegistration();
    584     }
    585 
    586     private void sendMsg(int msg) {
    587         if (mEventHandler != null) {
    588             mEventHandler.sendEmptyMessage(msg);
    589         }
    590     }
    591 
    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 
    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 
    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