Home | History | Annotate | Download | only in audio
      1 /*
      2  * Copyright (C) 2013 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 com.android.server.audio;
     18 
     19 import android.annotation.NonNull;
     20 import android.annotation.Nullable;
     21 import android.media.AudioAttributes;
     22 import android.media.AudioFocusInfo;
     23 import android.media.AudioManager;
     24 import android.media.IAudioFocusDispatcher;
     25 import android.os.IBinder;
     26 import android.util.Log;
     27 
     28 import com.android.server.audio.MediaFocusControl.AudioFocusDeathHandler;
     29 
     30 import java.io.PrintWriter;
     31 
     32 /**
     33  * @hide
     34  * Class to handle all the information about a user of audio focus. The lifecycle of each
     35  * instance is managed by android.media.MediaFocusControl, from its addition to the audio focus
     36  * stack, or the map of focus owners for an external focus policy, to its release.
     37  */
     38 public class FocusRequester {
     39 
     40     // on purpose not using this classe's name, as it will only be used from MediaFocusControl
     41     private static final String TAG = "MediaFocusControl";
     42     private static final boolean DEBUG = false;
     43 
     44     private AudioFocusDeathHandler mDeathHandler; // may be null
     45     private IAudioFocusDispatcher mFocusDispatcher; // may be null
     46     private final IBinder mSourceRef; // may be null
     47     private final String mClientId;
     48     private final String mPackageName;
     49     private final int mCallingUid;
     50     private final MediaFocusControl mFocusController; // never null
     51     private final int mSdkTarget;
     52 
     53     /**
     54      * the audio focus gain request that caused the addition of this object in the focus stack.
     55      */
     56     private final int mFocusGainRequest;
     57     /**
     58      * the flags associated with the gain request that qualify the type of grant (e.g. accepting
     59      * delay vs grant must be immediate)
     60      */
     61     private final int mGrantFlags;
     62     /**
     63      * the audio focus loss received my mFocusDispatcher, is AudioManager.AUDIOFOCUS_NONE if
     64      *  it never lost focus.
     65      */
     66     private int mFocusLossReceived;
     67     /**
     68      * whether this focus owner listener was notified when it lost focus
     69      */
     70     private boolean mFocusLossWasNotified;
     71     /**
     72      * the audio attributes associated with the focus request
     73      */
     74     private final AudioAttributes mAttributes;
     75 
     76     /**
     77      * Class constructor
     78      * @param aa
     79      * @param focusRequest
     80      * @param grantFlags
     81      * @param afl
     82      * @param source
     83      * @param id
     84      * @param hdlr
     85      * @param pn
     86      * @param uid
     87      * @param ctlr cannot be null
     88      */
     89     FocusRequester(AudioAttributes aa, int focusRequest, int grantFlags,
     90             IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr,
     91             String pn, int uid, @NonNull MediaFocusControl ctlr, int sdk) {
     92         mAttributes = aa;
     93         mFocusDispatcher = afl;
     94         mSourceRef = source;
     95         mClientId = id;
     96         mDeathHandler = hdlr;
     97         mPackageName = pn;
     98         mCallingUid = uid;
     99         mFocusGainRequest = focusRequest;
    100         mGrantFlags = grantFlags;
    101         mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE;
    102         mFocusLossWasNotified = true;
    103         mFocusController = ctlr;
    104         mSdkTarget = sdk;
    105     }
    106 
    107     FocusRequester(AudioFocusInfo afi, IAudioFocusDispatcher afl,
    108              IBinder source, AudioFocusDeathHandler hdlr, @NonNull MediaFocusControl ctlr) {
    109         mAttributes = afi.getAttributes();
    110         mClientId = afi.getClientId();
    111         mPackageName = afi.getPackageName();
    112         mCallingUid = afi.getClientUid();
    113         mFocusGainRequest = afi.getGainRequest();
    114         mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE;
    115         mFocusLossWasNotified = true;
    116         mGrantFlags = afi.getFlags();
    117         mSdkTarget = afi.getSdkTarget();
    118 
    119         mFocusDispatcher = afl;
    120         mSourceRef = source;
    121         mDeathHandler = hdlr;
    122         mFocusController = ctlr;
    123     }
    124 
    125     boolean hasSameClient(String otherClient) {
    126         try {
    127             return mClientId.compareTo(otherClient) == 0;
    128         } catch (NullPointerException e) {
    129             return false;
    130         }
    131     }
    132 
    133     boolean isLockedFocusOwner() {
    134         return ((mGrantFlags & AudioManager.AUDIOFOCUS_FLAG_LOCK) != 0);
    135     }
    136 
    137     boolean hasSameBinder(IBinder ib) {
    138         return (mSourceRef != null) && mSourceRef.equals(ib);
    139     }
    140 
    141     boolean hasSameDispatcher(IAudioFocusDispatcher fd) {
    142         return (mFocusDispatcher != null) && mFocusDispatcher.equals(fd);
    143     }
    144 
    145     boolean hasSamePackage(String pack) {
    146         try {
    147             return mPackageName.compareTo(pack) == 0;
    148         } catch (NullPointerException e) {
    149             return false;
    150         }
    151     }
    152 
    153     boolean hasSameUid(int uid) {
    154         return mCallingUid == uid;
    155     }
    156 
    157     int getClientUid() {
    158         return mCallingUid;
    159     }
    160 
    161     String getClientId() {
    162         return mClientId;
    163     }
    164 
    165     int getGainRequest() {
    166         return mFocusGainRequest;
    167     }
    168 
    169     int getGrantFlags() {
    170         return mGrantFlags;
    171     }
    172 
    173     AudioAttributes getAudioAttributes() {
    174         return mAttributes;
    175     }
    176 
    177     int getSdkTarget() {
    178         return mSdkTarget;
    179     }
    180 
    181     private static String focusChangeToString(int focus) {
    182         switch(focus) {
    183             case AudioManager.AUDIOFOCUS_NONE:
    184                 return "none";
    185             case AudioManager.AUDIOFOCUS_GAIN:
    186                 return "GAIN";
    187             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
    188                 return "GAIN_TRANSIENT";
    189             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
    190                 return "GAIN_TRANSIENT_MAY_DUCK";
    191             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:
    192                 return "GAIN_TRANSIENT_EXCLUSIVE";
    193             case AudioManager.AUDIOFOCUS_LOSS:
    194                 return "LOSS";
    195             case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
    196                 return "LOSS_TRANSIENT";
    197             case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
    198                 return "LOSS_TRANSIENT_CAN_DUCK";
    199             default:
    200                 return "[invalid focus change" + focus + "]";
    201         }
    202     }
    203 
    204     private String focusGainToString() {
    205         return focusChangeToString(mFocusGainRequest);
    206     }
    207 
    208     private String focusLossToString() {
    209         return focusChangeToString(mFocusLossReceived);
    210     }
    211 
    212     private static String flagsToString(int flags) {
    213         String msg = new String();
    214         if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) != 0) {
    215             msg += "DELAY_OK";
    216         }
    217         if ((flags & AudioManager.AUDIOFOCUS_FLAG_LOCK) != 0)     {
    218             if (!msg.isEmpty()) { msg += "|"; }
    219             msg += "LOCK";
    220         }
    221         if ((flags & AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) != 0) {
    222             if (!msg.isEmpty()) { msg += "|"; }
    223             msg += "PAUSES_ON_DUCKABLE_LOSS";
    224         }
    225         return msg;
    226     }
    227 
    228     void dump(PrintWriter pw) {
    229         pw.println("  source:" + mSourceRef
    230                 + " -- pack: " + mPackageName
    231                 + " -- client: " + mClientId
    232                 + " -- gain: " + focusGainToString()
    233                 + " -- flags: " + flagsToString(mGrantFlags)
    234                 + " -- loss: " + focusLossToString()
    235                 + " -- notified: " + mFocusLossWasNotified
    236                 + " -- uid: " + mCallingUid
    237                 + " -- attr: " + mAttributes
    238                 + " -- sdk:" + mSdkTarget);
    239     }
    240 
    241 
    242     void release() {
    243         try {
    244             if (mSourceRef != null && mDeathHandler != null) {
    245                 mSourceRef.unlinkToDeath(mDeathHandler, 0);
    246                 mDeathHandler = null;
    247                 mFocusDispatcher = null;
    248             }
    249         } catch (java.util.NoSuchElementException e) {
    250             Log.e(TAG, "FocusRequester.release() hit ", e);
    251         }
    252     }
    253 
    254     @Override
    255     protected void finalize() throws Throwable {
    256         release();
    257         super.finalize();
    258     }
    259 
    260     /**
    261      * For a given audio focus gain request, return the audio focus loss type that will result
    262      * from it, taking into account any previous focus loss.
    263      * @param gainRequest
    264      * @return the audio focus loss type that matches the gain request
    265      */
    266     private int focusLossForGainRequest(int gainRequest) {
    267         switch(gainRequest) {
    268             case AudioManager.AUDIOFOCUS_GAIN:
    269                 switch(mFocusLossReceived) {
    270                     case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
    271                     case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
    272                     case AudioManager.AUDIOFOCUS_LOSS:
    273                     case AudioManager.AUDIOFOCUS_NONE:
    274                         return AudioManager.AUDIOFOCUS_LOSS;
    275                 }
    276             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:
    277             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
    278                 switch(mFocusLossReceived) {
    279                     case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
    280                     case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
    281                     case AudioManager.AUDIOFOCUS_NONE:
    282                         return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
    283                     case AudioManager.AUDIOFOCUS_LOSS:
    284                         return AudioManager.AUDIOFOCUS_LOSS;
    285                 }
    286             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
    287                 switch(mFocusLossReceived) {
    288                     case AudioManager.AUDIOFOCUS_NONE:
    289                     case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
    290                         return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK;
    291                     case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
    292                         return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
    293                     case AudioManager.AUDIOFOCUS_LOSS:
    294                         return AudioManager.AUDIOFOCUS_LOSS;
    295                 }
    296             default:
    297                 Log.e(TAG, "focusLossForGainRequest() for invalid focus request "+ gainRequest);
    298                         return AudioManager.AUDIOFOCUS_NONE;
    299         }
    300     }
    301 
    302     /**
    303      * Called synchronized on MediaFocusControl.mAudioFocusLock
    304      */
    305     void handleExternalFocusGain(int focusGain, final FocusRequester fr) {
    306         int focusLoss = focusLossForGainRequest(focusGain);
    307         handleFocusLoss(focusLoss, fr);
    308     }
    309 
    310     /**
    311      * Called synchronized on MediaFocusControl.mAudioFocusLock
    312      */
    313     void handleFocusGain(int focusGain) {
    314         try {
    315             mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE;
    316             mFocusController.notifyExtPolicyFocusGrant_syncAf(toAudioFocusInfo(),
    317                     AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
    318             final IAudioFocusDispatcher fd = mFocusDispatcher;
    319             if (fd != null) {
    320                 if (DEBUG) {
    321                     Log.v(TAG, "dispatching " + focusChangeToString(focusGain) + " to "
    322                         + mClientId);
    323                 }
    324                 if (mFocusLossWasNotified) {
    325                     fd.dispatchAudioFocusChange(focusGain, mClientId);
    326                 }
    327             }
    328             mFocusController.unduckPlayers(this);
    329         } catch (android.os.RemoteException e) {
    330             Log.e(TAG, "Failure to signal gain of audio focus due to: ", e);
    331         }
    332     }
    333 
    334     /**
    335      * Called synchronized on MediaFocusControl.mAudioFocusLock
    336      */
    337     void handleFocusGainFromRequest(int focusRequestResult) {
    338         if (focusRequestResult == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
    339             mFocusController.unduckPlayers(this);
    340         }
    341     }
    342 
    343     /**
    344      * Called synchronized on MediaFocusControl.mAudioFocusLock
    345      */
    346     void handleFocusLoss(int focusLoss, @Nullable final FocusRequester fr) {
    347         try {
    348             if (focusLoss != mFocusLossReceived) {
    349                 mFocusLossReceived = focusLoss;
    350                 mFocusLossWasNotified = false;
    351                 // before dispatching a focus loss, check if the following conditions are met:
    352                 // 1/ the framework is not supposed to notify the focus loser on a DUCK loss
    353                 //    (i.e. it has a focus controller that implements a ducking policy)
    354                 // 2/ it is a DUCK loss
    355                 // 3/ the focus loser isn't flagged as pausing in a DUCK loss
    356                 // if they are, do not notify the focus loser
    357                 if (!mFocusController.mustNotifyFocusOwnerOnDuck()
    358                         && mFocusLossReceived == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
    359                         && (mGrantFlags
    360                                 & AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) == 0) {
    361                     if (DEBUG) {
    362                         Log.v(TAG, "NOT dispatching " + focusChangeToString(mFocusLossReceived)
    363                                 + " to " + mClientId + ", to be handled externally");
    364                     }
    365                     mFocusController.notifyExtPolicyFocusLoss_syncAf(
    366                             toAudioFocusInfo(), false /* wasDispatched */);
    367                     return;
    368                 }
    369 
    370                 // check enforcement by the framework
    371                 boolean handled = false;
    372                 if (focusLoss == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
    373                         && MediaFocusControl.ENFORCE_DUCKING
    374                         && fr != null) {
    375                     // candidate for enforcement by the framework
    376                     if (fr.mCallingUid != this.mCallingUid) {
    377                         if ((mGrantFlags
    378                                 & AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) != 0) {
    379                             // the focus loser declared it would pause instead of duck, let it
    380                             // handle it (the framework doesn't pause for apps)
    381                             handled = false;
    382                             Log.v(TAG, "not ducking uid " + this.mCallingUid + " - flags");
    383                         } else if (MediaFocusControl.ENFORCE_DUCKING_FOR_NEW &&
    384                                 this.getSdkTarget() <= MediaFocusControl.DUCKING_IN_APP_SDK_LEVEL) {
    385                             // legacy behavior, apps used to be notified when they should be ducking
    386                             handled = false;
    387                             Log.v(TAG, "not ducking uid " + this.mCallingUid + " - old SDK");
    388                         } else {
    389                             handled = mFocusController.duckPlayers(fr, this);
    390                         }
    391                     } // else: the focus change is within the same app, so let the dispatching
    392                       //       happen as if the framework was not involved.
    393                 }
    394 
    395                 if (handled) {
    396                     if (DEBUG) {
    397                         Log.v(TAG, "NOT dispatching " + focusChangeToString(mFocusLossReceived)
    398                             + " to " + mClientId + ", ducking implemented by framework");
    399                     }
    400                     mFocusController.notifyExtPolicyFocusLoss_syncAf(
    401                             toAudioFocusInfo(), false /* wasDispatched */);
    402                     return; // with mFocusLossWasNotified = false
    403                 }
    404 
    405                 final IAudioFocusDispatcher fd = mFocusDispatcher;
    406                 if (fd != null) {
    407                     if (DEBUG) {
    408                         Log.v(TAG, "dispatching " + focusChangeToString(mFocusLossReceived) + " to "
    409                             + mClientId);
    410                     }
    411                     mFocusController.notifyExtPolicyFocusLoss_syncAf(
    412                             toAudioFocusInfo(), true /* wasDispatched */);
    413                     mFocusLossWasNotified = true;
    414                     fd.dispatchAudioFocusChange(mFocusLossReceived, mClientId);
    415                 }
    416             }
    417         } catch (android.os.RemoteException e) {
    418             Log.e(TAG, "Failure to signal loss of audio focus due to:", e);
    419         }
    420     }
    421 
    422     int dispatchFocusChange(int focusChange) {
    423         if (mFocusDispatcher == null) {
    424             if (MediaFocusControl.DEBUG) { Log.v(TAG, "dispatchFocusChange: no focus dispatcher"); }
    425             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
    426         }
    427         if (focusChange == AudioManager.AUDIOFOCUS_NONE) {
    428             if (MediaFocusControl.DEBUG) { Log.v(TAG, "dispatchFocusChange: AUDIOFOCUS_NONE"); }
    429             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
    430         } else if ((focusChange == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
    431                 || focusChange == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
    432                 || focusChange == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT
    433                 || focusChange == AudioManager.AUDIOFOCUS_GAIN)
    434                 && (mFocusGainRequest != focusChange)){
    435             Log.w(TAG, "focus gain was requested with " + mFocusGainRequest
    436                     + ", dispatching " + focusChange);
    437         } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
    438                 || focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT
    439                 || focusChange == AudioManager.AUDIOFOCUS_LOSS) {
    440             mFocusLossReceived = focusChange;
    441         }
    442         try {
    443             mFocusDispatcher.dispatchAudioFocusChange(focusChange, mClientId);
    444         } catch (android.os.RemoteException e) {
    445             Log.v(TAG, "dispatchFocusChange: error talking to focus listener", e);
    446             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
    447         }
    448         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
    449     }
    450 
    451     AudioFocusInfo toAudioFocusInfo() {
    452         return new AudioFocusInfo(mAttributes, mCallingUid, mClientId, mPackageName,
    453                 mFocusGainRequest, mFocusLossReceived, mGrantFlags, mSdkTarget);
    454     }
    455 }
    456