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