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.app.AppOpsManager;
     21 import android.content.Context;
     22 import android.media.AudioAttributes;
     23 import android.media.AudioFocusInfo;
     24 import android.media.AudioManager;
     25 import android.media.AudioSystem;
     26 import android.media.IAudioFocusDispatcher;
     27 import android.media.audiopolicy.AudioPolicy;
     28 import android.media.audiopolicy.IAudioPolicyCallback;
     29 import android.os.Binder;
     30 import android.os.Build;
     31 import android.os.IBinder;
     32 import android.os.RemoteException;
     33 import android.util.Log;
     34 
     35 import java.io.PrintWriter;
     36 import java.util.ArrayList;
     37 import java.util.Date;
     38 import java.util.HashMap;
     39 import java.util.Iterator;
     40 import java.util.Map.Entry;
     41 import java.util.Set;
     42 import java.util.Stack;
     43 import java.text.DateFormat;
     44 
     45 /**
     46  * @hide
     47  *
     48  */
     49 public class MediaFocusControl implements PlayerFocusEnforcer {
     50 
     51     private static final String TAG = "MediaFocusControl";
     52     static final boolean DEBUG = false;
     53 
     54     /**
     55      * set to true so the framework enforces ducking itself, without communicating to apps
     56      * that they lost focus for most use cases.
     57      */
     58     static final boolean ENFORCE_DUCKING = true;
     59     /**
     60      * set to true to the framework enforces ducking itself only with apps above a given SDK
     61      * target level. Is ignored if ENFORCE_DUCKING is false.
     62      */
     63     static final boolean ENFORCE_DUCKING_FOR_NEW = true;
     64     /**
     65      * the SDK level (included) up to which the framework doesn't enforce ducking itself. Is ignored
     66      * if ENFORCE_DUCKING_FOR_NEW is false;
     67      */
     68     // automatic ducking was introduced for Android O
     69     static final int DUCKING_IN_APP_SDK_LEVEL = Build.VERSION_CODES.N_MR1;
     70     /**
     71      * set to true so the framework enforces muting media/game itself when the device is ringing
     72      * or in a call.
     73      */
     74     static final boolean ENFORCE_MUTING_FOR_RING_OR_CALL = true;
     75 
     76     private final Context mContext;
     77     private final AppOpsManager mAppOps;
     78     private PlayerFocusEnforcer mFocusEnforcer; // never null
     79 
     80     private boolean mRingOrCallActive = false;
     81 
     82     protected MediaFocusControl(Context cntxt, PlayerFocusEnforcer pfe) {
     83         mContext = cntxt;
     84         mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);
     85         mFocusEnforcer = pfe;
     86     }
     87 
     88     protected void dump(PrintWriter pw) {
     89         pw.println("\nMediaFocusControl dump time: "
     90                 + DateFormat.getTimeInstance().format(new Date()));
     91         dumpFocusStack(pw);
     92     }
     93 
     94     //=================================================================
     95     // PlayerFocusEnforcer implementation
     96     @Override
     97     public boolean duckPlayers(FocusRequester winner, FocusRequester loser) {
     98         return mFocusEnforcer.duckPlayers(winner, loser);
     99     }
    100 
    101     @Override
    102     public void unduckPlayers(FocusRequester winner) {
    103         mFocusEnforcer.unduckPlayers(winner);
    104     }
    105 
    106     @Override
    107     public void mutePlayersForCall(int[] usagesToMute) {
    108         mFocusEnforcer.mutePlayersForCall(usagesToMute);
    109     }
    110 
    111     @Override
    112     public void unmutePlayersForCall() {
    113         mFocusEnforcer.unmutePlayersForCall();
    114     }
    115 
    116     //==========================================================================================
    117     // AudioFocus
    118     //==========================================================================================
    119 
    120     private final static Object mAudioFocusLock = new Object();
    121 
    122     /**
    123      * Discard the current audio focus owner.
    124      * Notify top of audio focus stack that it lost focus (regardless of possibility to reassign
    125      * focus), remove it from the stack, and clear the remote control display.
    126      */
    127     protected void discardAudioFocusOwner() {
    128         synchronized(mAudioFocusLock) {
    129             if (!mFocusStack.empty()) {
    130                 // notify the current focus owner it lost focus after removing it from stack
    131                 final FocusRequester exFocusOwner = mFocusStack.pop();
    132                 exFocusOwner.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null);
    133                 exFocusOwner.release();
    134             }
    135         }
    136     }
    137 
    138     /**
    139      * Called synchronized on mAudioFocusLock
    140      */
    141     private void notifyTopOfAudioFocusStack() {
    142         // notify the top of the stack it gained focus
    143         if (!mFocusStack.empty()) {
    144             if (canReassignAudioFocus()) {
    145                 mFocusStack.peek().handleFocusGain(AudioManager.AUDIOFOCUS_GAIN);
    146             }
    147         }
    148     }
    149 
    150     /**
    151      * Focus is requested, propagate the associated loss throughout the stack.
    152      * @param focusGain the new focus gain that will later be added at the top of the stack
    153      */
    154     private void propagateFocusLossFromGain_syncAf(int focusGain, final FocusRequester fr) {
    155         // going through the audio focus stack to signal new focus, traversing order doesn't
    156         // matter as all entries respond to the same external focus gain
    157         Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
    158         while(stackIterator.hasNext()) {
    159             stackIterator.next().handleExternalFocusGain(focusGain, fr);
    160         }
    161     }
    162 
    163     private final Stack<FocusRequester> mFocusStack = new Stack<FocusRequester>();
    164 
    165     /**
    166      * Helper function:
    167      * Display in the log the current entries in the audio focus stack
    168      */
    169     private void dumpFocusStack(PrintWriter pw) {
    170         pw.println("\nAudio Focus stack entries (last is top of stack):");
    171         synchronized(mAudioFocusLock) {
    172             Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
    173             while(stackIterator.hasNext()) {
    174                 stackIterator.next().dump(pw);
    175             }
    176             pw.println("\n");
    177             if (mFocusPolicy == null) {
    178                 pw.println("No external focus policy\n");
    179             } else {
    180                 pw.println("External focus policy: "+ mFocusPolicy + ", focus owners:\n");
    181                 dumpExtFocusPolicyFocusOwners(pw);
    182             }
    183         }
    184         pw.println("\n");
    185         pw.println(" Notify on duck:  " + mNotifyFocusOwnerOnDuck + "\n");
    186         pw.println(" In ring or call: " + mRingOrCallActive + "\n");
    187     }
    188 
    189     /**
    190      * Helper function:
    191      * Called synchronized on mAudioFocusLock
    192      * Remove a focus listener from the focus stack.
    193      * @param clientToRemove the focus listener
    194      * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
    195      *   focus, notify the next item in the stack it gained focus.
    196      */
    197     private void removeFocusStackEntry(String clientToRemove, boolean signal,
    198             boolean notifyFocusFollowers) {
    199         // is the current top of the focus stack abandoning focus? (because of request, not death)
    200         if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientToRemove))
    201         {
    202             //Log.i(TAG, "   removeFocusStackEntry() removing top of stack");
    203             FocusRequester fr = mFocusStack.pop();
    204             fr.release();
    205             if (notifyFocusFollowers) {
    206                 final AudioFocusInfo afi = fr.toAudioFocusInfo();
    207                 afi.clearLossReceived();
    208                 notifyExtPolicyFocusLoss_syncAf(afi, false);
    209             }
    210             if (signal) {
    211                 // notify the new top of the stack it gained focus
    212                 notifyTopOfAudioFocusStack();
    213             }
    214         } else {
    215             // focus is abandoned by a client that's not at the top of the stack,
    216             // no need to update focus.
    217             // (using an iterator on the stack so we can safely remove an entry after having
    218             //  evaluated it, traversal order doesn't matter here)
    219             Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
    220             while(stackIterator.hasNext()) {
    221                 FocusRequester fr = stackIterator.next();
    222                 if(fr.hasSameClient(clientToRemove)) {
    223                     Log.i(TAG, "AudioFocus  removeFocusStackEntry(): removing entry for "
    224                             + clientToRemove);
    225                     stackIterator.remove();
    226                     // stack entry not used anymore, clear references
    227                     fr.release();
    228                 }
    229             }
    230         }
    231     }
    232 
    233     /**
    234      * Helper function:
    235      * Called synchronized on mAudioFocusLock
    236      * Remove focus listeners from the focus stack for a particular client when it has died.
    237      */
    238     private void removeFocusStackEntryOnDeath(IBinder cb) {
    239         // is the owner of the audio focus part of the client to remove?
    240         boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
    241                 mFocusStack.peek().hasSameBinder(cb);
    242         // (using an iterator on the stack so we can safely remove an entry after having
    243         //  evaluated it, traversal order doesn't matter here)
    244         Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
    245         while(stackIterator.hasNext()) {
    246             FocusRequester fr = stackIterator.next();
    247             if(fr.hasSameBinder(cb)) {
    248                 Log.i(TAG, "AudioFocus  removeFocusStackEntryOnDeath(): removing entry for " + cb);
    249                 stackIterator.remove();
    250                 // stack entry not used anymore, clear references
    251                 fr.release();
    252             }
    253         }
    254         if (isTopOfStackForClientToRemove) {
    255             // we removed an entry at the top of the stack:
    256             //  notify the new top of the stack it gained focus.
    257             notifyTopOfAudioFocusStack();
    258         }
    259     }
    260 
    261     /**
    262      * Helper function for external focus policy:
    263      * Called synchronized on mAudioFocusLock
    264      * Remove focus listeners from the list of potential focus owners for a particular client when
    265      * it has died.
    266      */
    267     private void removeFocusEntryForExtPolicy(IBinder cb) {
    268         if (mFocusOwnersForFocusPolicy.isEmpty()) {
    269             return;
    270         }
    271         boolean released = false;
    272         final Set<Entry<String, FocusRequester>> owners = mFocusOwnersForFocusPolicy.entrySet();
    273         final Iterator<Entry<String, FocusRequester>> ownerIterator = owners.iterator();
    274         while (ownerIterator.hasNext()) {
    275             final Entry<String, FocusRequester> owner = ownerIterator.next();
    276             final FocusRequester fr = owner.getValue();
    277             if (fr.hasSameBinder(cb)) {
    278                 ownerIterator.remove();
    279                 fr.release();
    280                 notifyExtFocusPolicyFocusAbandon_syncAf(fr.toAudioFocusInfo());
    281                 break;
    282             }
    283         }
    284     }
    285 
    286     /**
    287      * Helper function:
    288      * Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
    289      * The implementation guarantees that a state where focus cannot be immediately reassigned
    290      * implies that an "locked" focus owner is at the top of the focus stack.
    291      * Modifications to the implementation that break this assumption will cause focus requests to
    292      * misbehave when honoring the AudioManager.AUDIOFOCUS_FLAG_DELAY_OK flag.
    293      */
    294     private boolean canReassignAudioFocus() {
    295         // focus requests are rejected during a phone call or when the phone is ringing
    296         // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus
    297         if (!mFocusStack.isEmpty() && isLockedFocusOwner(mFocusStack.peek())) {
    298             return false;
    299         }
    300         return true;
    301     }
    302 
    303     private boolean isLockedFocusOwner(FocusRequester fr) {
    304         return (fr.hasSameClient(AudioSystem.IN_VOICE_COMM_FOCUS_ID) || fr.isLockedFocusOwner());
    305     }
    306 
    307     /**
    308      * Helper function
    309      * Pre-conditions: focus stack is not empty, there is one or more locked focus owner
    310      *                 at the top of the focus stack
    311      * Push the focus requester onto the audio focus stack at the first position immediately
    312      * following the locked focus owners.
    313      * @return {@link AudioManager#AUDIOFOCUS_REQUEST_GRANTED} or
    314      *     {@link AudioManager#AUDIOFOCUS_REQUEST_DELAYED}
    315      */
    316     private int pushBelowLockedFocusOwners(FocusRequester nfr) {
    317         int lastLockedFocusOwnerIndex = mFocusStack.size();
    318         for (int index = mFocusStack.size()-1; index >= 0; index--) {
    319             if (isLockedFocusOwner(mFocusStack.elementAt(index))) {
    320                 lastLockedFocusOwnerIndex = index;
    321             }
    322         }
    323         if (lastLockedFocusOwnerIndex == mFocusStack.size()) {
    324             // this should not happen, but handle it and log an error
    325             Log.e(TAG, "No exclusive focus owner found in propagateFocusLossFromGain_syncAf()",
    326                     new Exception());
    327             // no exclusive owner, push at top of stack, focus is granted, propagate change
    328             propagateFocusLossFromGain_syncAf(nfr.getGainRequest(), nfr);
    329             mFocusStack.push(nfr);
    330             return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
    331         } else {
    332             mFocusStack.insertElementAt(nfr, lastLockedFocusOwnerIndex);
    333             return AudioManager.AUDIOFOCUS_REQUEST_DELAYED;
    334         }
    335     }
    336 
    337     /**
    338      * Inner class to monitor audio focus client deaths, and remove them from the audio focus
    339      * stack if necessary.
    340      */
    341     protected class AudioFocusDeathHandler implements IBinder.DeathRecipient {
    342         private IBinder mCb; // To be notified of client's death
    343 
    344         AudioFocusDeathHandler(IBinder cb) {
    345             mCb = cb;
    346         }
    347 
    348         public void binderDied() {
    349             synchronized(mAudioFocusLock) {
    350                 if (mFocusPolicy != null) {
    351                     removeFocusEntryForExtPolicy(mCb);
    352                 } else {
    353                     removeFocusStackEntryOnDeath(mCb);
    354                 }
    355             }
    356         }
    357     }
    358 
    359     /**
    360      * Indicates whether to notify an audio focus owner when it loses focus
    361      * with {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK} if it will only duck.
    362      * This variable being false indicates an AudioPolicy has been registered and has signaled
    363      * it will handle audio ducking.
    364      */
    365     private boolean mNotifyFocusOwnerOnDuck = true;
    366 
    367     protected void setDuckingInExtPolicyAvailable(boolean available) {
    368         mNotifyFocusOwnerOnDuck = !available;
    369     }
    370 
    371     boolean mustNotifyFocusOwnerOnDuck() { return mNotifyFocusOwnerOnDuck; }
    372 
    373     private ArrayList<IAudioPolicyCallback> mFocusFollowers = new ArrayList<IAudioPolicyCallback>();
    374 
    375     void addFocusFollower(IAudioPolicyCallback ff) {
    376         if (ff == null) {
    377             return;
    378         }
    379         synchronized(mAudioFocusLock) {
    380             boolean found = false;
    381             for (IAudioPolicyCallback pcb : mFocusFollowers) {
    382                 if (pcb.asBinder().equals(ff.asBinder())) {
    383                     found = true;
    384                     break;
    385                 }
    386             }
    387             if (found) {
    388                 return;
    389             } else {
    390                 mFocusFollowers.add(ff);
    391                 notifyExtPolicyCurrentFocusAsync(ff);
    392             }
    393         }
    394     }
    395 
    396     void removeFocusFollower(IAudioPolicyCallback ff) {
    397         if (ff == null) {
    398             return;
    399         }
    400         synchronized(mAudioFocusLock) {
    401             for (IAudioPolicyCallback pcb : mFocusFollowers) {
    402                 if (pcb.asBinder().equals(ff.asBinder())) {
    403                     mFocusFollowers.remove(pcb);
    404                     break;
    405                 }
    406             }
    407         }
    408     }
    409 
    410     private IAudioPolicyCallback mFocusPolicy = null;
    411 
    412     // Since we don't have a stack of focus owners when using an external focus policy, we keep
    413     // track of all the focus requesters in this map, with their clientId as the key. This is
    414     // used both for focus dispatch and death handling
    415     private HashMap<String, FocusRequester> mFocusOwnersForFocusPolicy =
    416             new HashMap<String, FocusRequester>();
    417 
    418     void setFocusPolicy(IAudioPolicyCallback policy) {
    419         if (policy == null) {
    420             return;
    421         }
    422         synchronized (mAudioFocusLock) {
    423             mFocusPolicy = policy;
    424         }
    425     }
    426 
    427     void unsetFocusPolicy(IAudioPolicyCallback policy) {
    428         if (policy == null) {
    429             return;
    430         }
    431         synchronized (mAudioFocusLock) {
    432             if (mFocusPolicy == policy) {
    433                 mFocusPolicy = null;
    434             }
    435         }
    436     }
    437 
    438     /**
    439      * @param pcb non null
    440      */
    441     void notifyExtPolicyCurrentFocusAsync(IAudioPolicyCallback pcb) {
    442         final IAudioPolicyCallback pcb2 = pcb;
    443         final Thread thread = new Thread() {
    444             @Override
    445             public void run() {
    446                 synchronized(mAudioFocusLock) {
    447                     if (mFocusStack.isEmpty()) {
    448                         return;
    449                     }
    450                     try {
    451                         pcb2.notifyAudioFocusGrant(mFocusStack.peek().toAudioFocusInfo(),
    452                                 // top of focus stack always has focus
    453                                 AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
    454                     } catch (RemoteException e) {
    455                         Log.e(TAG, "Can't call notifyAudioFocusGrant() on IAudioPolicyCallback "
    456                                 + pcb2.asBinder(), e);
    457                     }
    458                 }
    459             }
    460         };
    461         thread.start();
    462     }
    463 
    464     /**
    465      * Called synchronized on mAudioFocusLock
    466      */
    467     void notifyExtPolicyFocusGrant_syncAf(AudioFocusInfo afi, int requestResult) {
    468         for (IAudioPolicyCallback pcb : mFocusFollowers) {
    469             try {
    470                 // oneway
    471                 pcb.notifyAudioFocusGrant(afi, requestResult);
    472             } catch (RemoteException e) {
    473                 Log.e(TAG, "Can't call notifyAudioFocusGrant() on IAudioPolicyCallback "
    474                         + pcb.asBinder(), e);
    475             }
    476         }
    477     }
    478 
    479     /**
    480      * Called synchronized on mAudioFocusLock
    481      */
    482     void notifyExtPolicyFocusLoss_syncAf(AudioFocusInfo afi, boolean wasDispatched) {
    483         for (IAudioPolicyCallback pcb : mFocusFollowers) {
    484             try {
    485                 // oneway
    486                 pcb.notifyAudioFocusLoss(afi, wasDispatched);
    487             } catch (RemoteException e) {
    488                 Log.e(TAG, "Can't call notifyAudioFocusLoss() on IAudioPolicyCallback "
    489                         + pcb.asBinder(), e);
    490             }
    491         }
    492     }
    493 
    494     /**
    495      * Called synchronized on mAudioFocusLock
    496      * @param afi
    497      * @param requestResult
    498      * @return true if the external audio focus policy (if any) is handling the focus request
    499      */
    500     boolean notifyExtFocusPolicyFocusRequest_syncAf(AudioFocusInfo afi, int requestResult,
    501             IAudioFocusDispatcher fd, IBinder cb) {
    502         if (mFocusPolicy == null) {
    503             return false;
    504         }
    505         if (DEBUG) {
    506             Log.v(TAG, "notifyExtFocusPolicyFocusRequest client="+afi.getClientId()
    507             + " dispatcher=" + fd);
    508         }
    509         final FocusRequester existingFr = mFocusOwnersForFocusPolicy.get(afi.getClientId());
    510         if (existingFr != null) {
    511             if (!existingFr.hasSameDispatcher(fd)) {
    512                 existingFr.release();
    513                 final AudioFocusDeathHandler hdlr = new AudioFocusDeathHandler(cb);
    514                 mFocusOwnersForFocusPolicy.put(afi.getClientId(),
    515                         new FocusRequester(afi, fd, cb, hdlr, this));
    516             }
    517         } else if (requestResult == AudioManager.AUDIOFOCUS_REQUEST_GRANTED
    518                  || requestResult == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) {
    519             // new focus (future) focus owner to keep track of
    520             final AudioFocusDeathHandler hdlr = new AudioFocusDeathHandler(cb);
    521             mFocusOwnersForFocusPolicy.put(afi.getClientId(),
    522                     new FocusRequester(afi, fd, cb, hdlr, this));
    523         }
    524         try {
    525             //oneway
    526             mFocusPolicy.notifyAudioFocusRequest(afi, requestResult);
    527         } catch (RemoteException e) {
    528             Log.e(TAG, "Can't call notifyAudioFocusRequest() on IAudioPolicyCallback "
    529                     + mFocusPolicy.asBinder(), e);
    530         }
    531         return true;
    532     }
    533 
    534     /**
    535      * Called synchronized on mAudioFocusLock
    536      * @param afi
    537      * @param requestResult
    538      * @return true if the external audio focus policy (if any) is handling the focus request
    539      */
    540     boolean notifyExtFocusPolicyFocusAbandon_syncAf(AudioFocusInfo afi) {
    541         if (mFocusPolicy == null) {
    542             return false;
    543         }
    544         final FocusRequester fr = mFocusOwnersForFocusPolicy.remove(afi.getClientId());
    545         if (fr != null) {
    546             fr.release();
    547         }
    548         try {
    549             //oneway
    550             mFocusPolicy.notifyAudioFocusAbandon(afi);
    551         } catch (RemoteException e) {
    552             Log.e(TAG, "Can't call notifyAudioFocusAbandon() on IAudioPolicyCallback "
    553                     + mFocusPolicy.asBinder(), e);
    554         }
    555         return true;
    556     }
    557 
    558     /** see AudioManager.dispatchFocusChange(AudioFocusInfo afi, int focusChange, AudioPolicy ap) */
    559     int dispatchFocusChange(AudioFocusInfo afi, int focusChange) {
    560         if (DEBUG) {
    561             Log.v(TAG, "dispatchFocusChange " + focusChange + " to afi client="
    562                     + afi.getClientId());
    563         }
    564         synchronized (mAudioFocusLock) {
    565             if (mFocusPolicy == null) {
    566                 if (DEBUG) { Log.v(TAG, "> failed: no focus policy" ); }
    567                 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
    568             }
    569             final FocusRequester fr = mFocusOwnersForFocusPolicy.get(afi.getClientId());
    570             if (fr == null) {
    571                 if (DEBUG) { Log.v(TAG, "> failed: no such focus requester known" ); }
    572                 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
    573             }
    574             return fr.dispatchFocusChange(focusChange);
    575         }
    576     }
    577 
    578     private void dumpExtFocusPolicyFocusOwners(PrintWriter pw) {
    579         final Set<Entry<String, FocusRequester>> owners = mFocusOwnersForFocusPolicy.entrySet();
    580         final Iterator<Entry<String, FocusRequester>> ownerIterator = owners.iterator();
    581         while (ownerIterator.hasNext()) {
    582             final Entry<String, FocusRequester> owner = ownerIterator.next();
    583             final FocusRequester fr = owner.getValue();
    584             fr.dump(pw);
    585         }
    586     }
    587 
    588     protected int getCurrentAudioFocus() {
    589         synchronized(mAudioFocusLock) {
    590             if (mFocusStack.empty()) {
    591                 return AudioManager.AUDIOFOCUS_NONE;
    592             } else {
    593                 return mFocusStack.peek().getGainRequest();
    594             }
    595         }
    596     }
    597 
    598     /**
    599      * Delay after entering ringing or call mode after which the framework will mute streams
    600      * that are still playing.
    601      */
    602     private static final int RING_CALL_MUTING_ENFORCEMENT_DELAY_MS = 100;
    603 
    604     /**
    605      * Usages to mute when the device rings or is in a call
    606      */
    607     private final static int[] USAGES_TO_MUTE_IN_RING_OR_CALL =
    608         { AudioAttributes.USAGE_MEDIA, AudioAttributes.USAGE_GAME };
    609 
    610     /**
    611      * Return the volume ramp time expected before playback with the given AudioAttributes would
    612      * start after gaining audio focus.
    613      * @param attr attributes of the sound about to start playing
    614      * @return time in ms
    615      */
    616     protected static int getFocusRampTimeMs(int focusGain, AudioAttributes attr) {
    617         switch (attr.getUsage()) {
    618             case AudioAttributes.USAGE_MEDIA:
    619             case AudioAttributes.USAGE_GAME:
    620                 return 1000;
    621             case AudioAttributes.USAGE_ALARM:
    622             case AudioAttributes.USAGE_NOTIFICATION_RINGTONE:
    623             case AudioAttributes.USAGE_ASSISTANT:
    624             case AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY:
    625             case AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
    626                 return 700;
    627             case AudioAttributes.USAGE_VOICE_COMMUNICATION:
    628             case AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING:
    629             case AudioAttributes.USAGE_NOTIFICATION:
    630             case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
    631             case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
    632             case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
    633             case AudioAttributes.USAGE_NOTIFICATION_EVENT:
    634             case AudioAttributes.USAGE_ASSISTANCE_SONIFICATION:
    635                 return 500;
    636             case AudioAttributes.USAGE_UNKNOWN:
    637             default:
    638                 return 0;
    639         }
    640     }
    641 
    642     /** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int, int) */
    643     protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb,
    644             IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
    645             int sdk) {
    646         Log.i(TAG, " AudioFocus  requestAudioFocus() from uid/pid " + Binder.getCallingUid()
    647                 + "/" + Binder.getCallingPid()
    648                 + " clientId=" + clientId
    649                 + " req=" + focusChangeHint
    650                 + " flags=0x" + Integer.toHexString(flags));
    651         // we need a valid binder callback for clients
    652         if (!cb.pingBinder()) {
    653             Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
    654             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
    655         }
    656 
    657         if (mAppOps.noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, Binder.getCallingUid(),
    658                 callingPackageName) != AppOpsManager.MODE_ALLOWED) {
    659             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
    660         }
    661 
    662         synchronized(mAudioFocusLock) {
    663             boolean enteringRingOrCall = !mRingOrCallActive
    664                     & (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0);
    665             if (enteringRingOrCall) { mRingOrCallActive = true; }
    666 
    667             final AudioFocusInfo afiForExtPolicy;
    668             if (mFocusPolicy != null) {
    669                 // construct AudioFocusInfo as it will be communicated to audio focus policy
    670                 afiForExtPolicy = new AudioFocusInfo(aa, Binder.getCallingUid(),
    671                         clientId, callingPackageName, focusChangeHint, 0 /*lossReceived*/,
    672                         flags, sdk);
    673             } else {
    674                 afiForExtPolicy = null;
    675             }
    676 
    677             // handle delayed focus
    678             boolean focusGrantDelayed = false;
    679             if (!canReassignAudioFocus()) {
    680                 if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) == 0) {
    681                     final int result = AudioManager.AUDIOFOCUS_REQUEST_FAILED;
    682                     notifyExtFocusPolicyFocusRequest_syncAf(afiForExtPolicy, result, fd, cb);
    683                     return result;
    684                 } else {
    685                     // request has AUDIOFOCUS_FLAG_DELAY_OK: focus can't be
    686                     // granted right now, so the requester will be inserted in the focus stack
    687                     // to receive focus later
    688                     focusGrantDelayed = true;
    689                 }
    690             }
    691 
    692             // external focus policy: delay request for focus gain?
    693             final int resultWithExtPolicy = AudioManager.AUDIOFOCUS_REQUEST_DELAYED;
    694             if (notifyExtFocusPolicyFocusRequest_syncAf(
    695                     afiForExtPolicy, resultWithExtPolicy, fd, cb)) {
    696                 // stop handling focus request here as it is handled by external audio focus policy
    697                 return resultWithExtPolicy;
    698             }
    699 
    700             // handle the potential premature death of the new holder of the focus
    701             // (premature death == death before abandoning focus)
    702             // Register for client death notification
    703             AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
    704 
    705             try {
    706                 cb.linkToDeath(afdh, 0);
    707             } catch (RemoteException e) {
    708                 // client has already died!
    709                 Log.w(TAG, "AudioFocus  requestAudioFocus() could not link to "+cb+" binder death");
    710                 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
    711             }
    712 
    713             if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientId)) {
    714                 // if focus is already owned by this client and the reason for acquiring the focus
    715                 // hasn't changed, don't do anything
    716                 final FocusRequester fr = mFocusStack.peek();
    717                 if (fr.getGainRequest() == focusChangeHint && fr.getGrantFlags() == flags) {
    718                     // unlink death handler so it can be gc'ed.
    719                     // linkToDeath() creates a JNI global reference preventing collection.
    720                     cb.unlinkToDeath(afdh, 0);
    721                     notifyExtPolicyFocusGrant_syncAf(fr.toAudioFocusInfo(),
    722                             AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
    723                     return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
    724                 }
    725                 // the reason for the audio focus request has changed: remove the current top of
    726                 // stack and respond as if we had a new focus owner
    727                 if (!focusGrantDelayed) {
    728                     mFocusStack.pop();
    729                     // the entry that was "popped" is the same that was "peeked" above
    730                     fr.release();
    731                 }
    732             }
    733 
    734             // focus requester might already be somewhere below in the stack, remove it
    735             removeFocusStackEntry(clientId, false /* signal */, false /*notifyFocusFollowers*/);
    736 
    737             final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb,
    738                     clientId, afdh, callingPackageName, Binder.getCallingUid(), this, sdk);
    739             if (focusGrantDelayed) {
    740                 // focusGrantDelayed being true implies we can't reassign focus right now
    741                 // which implies the focus stack is not empty.
    742                 final int requestResult = pushBelowLockedFocusOwners(nfr);
    743                 if (requestResult != AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
    744                     notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(), requestResult);
    745                 }
    746                 return requestResult;
    747             } else {
    748                 // propagate the focus change through the stack
    749                 if (!mFocusStack.empty()) {
    750                     propagateFocusLossFromGain_syncAf(focusChangeHint, nfr);
    751                 }
    752 
    753                 // push focus requester at the top of the audio focus stack
    754                 mFocusStack.push(nfr);
    755                 nfr.handleFocusGainFromRequest(AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
    756             }
    757             notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(),
    758                     AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
    759 
    760             if (ENFORCE_MUTING_FOR_RING_OR_CALL & enteringRingOrCall) {
    761                 runAudioCheckerForRingOrCallAsync(true/*enteringRingOrCall*/);
    762             }
    763         }//synchronized(mAudioFocusLock)
    764 
    765         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
    766     }
    767 
    768     /**
    769      * @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener, AudioAttributes)
    770      * */
    771     protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa,
    772             String callingPackageName) {
    773         // AudioAttributes are currently ignored, to be used for zones
    774         Log.i(TAG, " AudioFocus  abandonAudioFocus() from uid/pid " + Binder.getCallingUid()
    775                 + "/" + Binder.getCallingPid()
    776                 + " clientId=" + clientId);
    777         try {
    778             // this will take care of notifying the new focus owner if needed
    779             synchronized(mAudioFocusLock) {
    780                 // external focus policy?
    781                 if (mFocusPolicy != null) {
    782                     final AudioFocusInfo afi = new AudioFocusInfo(aa, Binder.getCallingUid(),
    783                             clientId, callingPackageName, 0 /*gainRequest*/, 0 /*lossReceived*/,
    784                             0 /*flags*/, 0 /* sdk n/a here*/);
    785                     if (notifyExtFocusPolicyFocusAbandon_syncAf(afi)) {
    786                         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
    787                     }
    788                 }
    789 
    790                 boolean exitingRingOrCall = mRingOrCallActive
    791                         & (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0);
    792                 if (exitingRingOrCall) { mRingOrCallActive = false; }
    793 
    794                 removeFocusStackEntry(clientId, true /*signal*/, true /*notifyFocusFollowers*/);
    795 
    796                 if (ENFORCE_MUTING_FOR_RING_OR_CALL & exitingRingOrCall) {
    797                     runAudioCheckerForRingOrCallAsync(false/*enteringRingOrCall*/);
    798                 }
    799             }
    800         } catch (java.util.ConcurrentModificationException cme) {
    801             // Catching this exception here is temporary. It is here just to prevent
    802             // a crash seen when the "Silent" notification is played. This is believed to be fixed
    803             // but this try catch block is left just to be safe.
    804             Log.e(TAG, "FATAL EXCEPTION AudioFocus  abandonAudioFocus() caused " + cme);
    805             cme.printStackTrace();
    806         }
    807 
    808         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
    809     }
    810 
    811 
    812     protected void unregisterAudioFocusClient(String clientId) {
    813         synchronized(mAudioFocusLock) {
    814             removeFocusStackEntry(clientId, false, true /*notifyFocusFollowers*/);
    815         }
    816     }
    817 
    818     private void runAudioCheckerForRingOrCallAsync(final boolean enteringRingOrCall) {
    819         new Thread() {
    820             public void run() {
    821                 if (enteringRingOrCall) {
    822                     try {
    823                         Thread.sleep(RING_CALL_MUTING_ENFORCEMENT_DELAY_MS);
    824                     } catch (InterruptedException e) {
    825                         e.printStackTrace();
    826                     }
    827                 }
    828                 synchronized (mAudioFocusLock) {
    829                     // since the new thread starting running the state could have changed, so
    830                     // we need to check again mRingOrCallActive, not enteringRingOrCall
    831                     if (mRingOrCallActive) {
    832                         mFocusEnforcer.mutePlayersForCall(USAGES_TO_MUTE_IN_RING_OR_CALL);
    833                     } else {
    834                         mFocusEnforcer.unmutePlayersForCall();
    835                     }
    836                 }
    837             }
    838         }.start();
    839     }
    840 }
    841