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