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 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 */
    100     public AudioPolicyConfig getConfig() { return mConfig; }
    101     /** @hide */
    102     public boolean hasFocusListener() { return mFocusListener != null; }
    103     /** @hide */
    104     public boolean isFocusPolicy() { return mIsFocusPolicy; }
    105     /** @hide */
    106     public boolean isVolumeController() { return mVolCb != null; }
    107 
    108     /**
    109      * The parameter is guaranteed non-null through the Builder
    110      */
    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
    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
    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
    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
    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
    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
    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          */
    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
    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
    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
    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 
    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 
    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 
    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
    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
    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
    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
    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
    532     public int getStatus() {
    533         return mStatus;
    534     }
    535 
    536     @SystemApi
    537     public static abstract class AudioPolicyStatusListener {
    538         public void onStatusChange() {}
    539         public void onMixStateUpdate(AudioMix mix) {}
    540     }
    541 
    542     @SystemApi
    543     public static abstract class AudioPolicyFocusListener {
    544         public void onAudioFocusGrant(AudioFocusInfo afi, int requestResult) {}
    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          */
    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          */
    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 */
    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          */
    580         public void onVolumeAdjustment(@AudioManager.VolumeAdjustment int adjustment) {}
    581     }
    582 
    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 */
    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 {
    666         public EventHandler(AudioPolicy ap, Looper looper) {
    667             super(looper);
    668         }
    669 
    670         @Override
    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
    722     private static String addressForTag(AudioMix mix) {
    723         return "addr=" + mix.getRegistration();
    724     }
    725 
    726     private void sendMsg(int msg) {
    727         if (mEventHandler != null) {
    728             mEventHandler.sendEmptyMessage(msg);
    729         }
    730     }
    731 
    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 
    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 
    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