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 
MediaFocusControl(Context cntxt, PlayerFocusEnforcer pfe)90     protected MediaFocusControl(Context cntxt, PlayerFocusEnforcer pfe) {
91         mContext = cntxt;
92         mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);
93         mFocusEnforcer = pfe;
94     }
95 
dump(PrintWriter pw)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
duckPlayers(FocusRequester winner, FocusRequester loser, boolean forceDuck)108     public boolean duckPlayers(FocusRequester winner, FocusRequester loser, boolean forceDuck) {
109         return mFocusEnforcer.duckPlayers(winner, loser, forceDuck);
110     }
111 
112     @Override
unduckPlayers(FocusRequester winner)113     public void unduckPlayers(FocusRequester winner) {
114         mFocusEnforcer.unduckPlayers(winner);
115     }
116 
117     @Override
mutePlayersForCall(int[] usagesToMute)118     public void mutePlayersForCall(int[] usagesToMute) {
119         mFocusEnforcer.mutePlayersForCall(usagesToMute);
120     }
121 
122     @Override
unmutePlayersForCall()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      */
discardAudioFocusOwner()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")
notifyTopOfAudioFocusStack()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")
propagateFocusLossFromGain_syncAf(int focusGain, final FocusRequester fr, boolean forceDuck)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      */
dumpFocusStack(PrintWriter pw)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")
removeFocusStackEntry(String clientToRemove, boolean signal, boolean notifyFocusFollowers)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")
removeFocusStackEntryOnDeath(IBinder cb)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")
removeFocusEntryForExtPolicy(IBinder cb)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      */
canReassignAudioFocus()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 
isLockedFocusOwner(FocusRequester fr)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")
pushBelowLockedFocusOwners(FocusRequester nfr)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 
AudioFocusDeathHandler(IBinder cb)372         AudioFocusDeathHandler(IBinder cb) {
373             mCb = cb;
374         }
375 
binderDied()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 
setDuckingInExtPolicyAvailable(boolean available)395     protected void setDuckingInExtPolicyAvailable(boolean available) {
396         mNotifyFocusOwnerOnDuck = !available;
397     }
398 
mustNotifyFocusOwnerOnDuck()399     boolean mustNotifyFocusOwnerOnDuck() { return mNotifyFocusOwnerOnDuck; }
400 
401     private ArrayList<IAudioPolicyCallback> mFocusFollowers = new ArrayList<IAudioPolicyCallback>();
402 
addFocusFollower(IAudioPolicyCallback ff)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 
removeFocusFollower(IAudioPolicyCallback ff)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 
setFocusPolicy(IAudioPolicyCallback policy)446     void setFocusPolicy(IAudioPolicyCallback policy) {
447         if (policy == null) {
448             return;
449         }
450         synchronized (mAudioFocusLock) {
451             mFocusPolicy = policy;
452         }
453     }
454 
unsetFocusPolicy(IAudioPolicyCallback policy)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      */
notifyExtPolicyCurrentFocusAsync(IAudioPolicyCallback pcb)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      */
notifyExtPolicyFocusGrant_syncAf(AudioFocusInfo afi, int requestResult)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      */
notifyExtPolicyFocusLoss_syncAf(AudioFocusInfo afi, boolean wasDispatched)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      */
notifyExtFocusPolicyFocusRequest_syncAf(AudioFocusInfo afi, IAudioFocusDispatcher fd, IBinder cb)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 
setFocusRequestResultFromExtPolicy(AudioFocusInfo afi, int requestResult)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      */
notifyExtFocusPolicyFocusAbandon_syncAf(AudioFocusInfo afi)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) */
dispatchFocusChange(AudioFocusInfo afi, int focusChange)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 
dumpExtFocusPolicyFocusOwners(PrintWriter pw)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 
getCurrentAudioFocus()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      */
getFocusRampTimeMs(int focusGain, AudioAttributes attr)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) */
requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb, IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags, int sdk, boolean forceDuck)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      * */
abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa, String callingPackageName)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 
unregisterAudioFocusClient(String clientId)867     protected void unregisterAudioFocusClient(String clientId) {
868         synchronized(mAudioFocusLock) {
869             removeFocusStackEntry(clientId, false, true /*notifyFocusFollowers*/);
870         }
871     }
872 
runAudioCheckerForRingOrCallAsync(final boolean enteringRingOrCall)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