• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
MediaFocusControl(Context cntxt, PlayerFocusEnforcer pfe)82     protected MediaFocusControl(Context cntxt, PlayerFocusEnforcer pfe) {
83         mContext = cntxt;
84         mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);
85         mFocusEnforcer = pfe;
86     }
87 
dump(PrintWriter pw)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
duckPlayers(FocusRequester winner, FocusRequester loser)97     public boolean duckPlayers(FocusRequester winner, FocusRequester loser) {
98         return mFocusEnforcer.duckPlayers(winner, loser);
99     }
100 
101     @Override
unduckPlayers(FocusRequester winner)102     public void unduckPlayers(FocusRequester winner) {
103         mFocusEnforcer.unduckPlayers(winner);
104     }
105 
106     @Override
mutePlayersForCall(int[] usagesToMute)107     public void mutePlayersForCall(int[] usagesToMute) {
108         mFocusEnforcer.mutePlayersForCall(usagesToMute);
109     }
110 
111     @Override
unmutePlayersForCall()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      */
discardAudioFocusOwner()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      */
notifyTopOfAudioFocusStack()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      */
propagateFocusLossFromGain_syncAf(int focusGain, final FocusRequester fr)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      */
dumpFocusStack(PrintWriter pw)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      */
removeFocusStackEntry(String clientToRemove, boolean signal, boolean notifyFocusFollowers)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      */
removeFocusStackEntryOnDeath(IBinder cb)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      */
removeFocusEntryForExtPolicy(IBinder cb)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      */
canReassignAudioFocus()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 
isLockedFocusOwner(FocusRequester fr)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      */
pushBelowLockedFocusOwners(FocusRequester nfr)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 
AudioFocusDeathHandler(IBinder cb)344         AudioFocusDeathHandler(IBinder cb) {
345             mCb = cb;
346         }
347 
binderDied()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 
setDuckingInExtPolicyAvailable(boolean available)367     protected void setDuckingInExtPolicyAvailable(boolean available) {
368         mNotifyFocusOwnerOnDuck = !available;
369     }
370 
mustNotifyFocusOwnerOnDuck()371     boolean mustNotifyFocusOwnerOnDuck() { return mNotifyFocusOwnerOnDuck; }
372 
373     private ArrayList<IAudioPolicyCallback> mFocusFollowers = new ArrayList<IAudioPolicyCallback>();
374 
addFocusFollower(IAudioPolicyCallback ff)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 
removeFocusFollower(IAudioPolicyCallback ff)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 
setFocusPolicy(IAudioPolicyCallback policy)418     void setFocusPolicy(IAudioPolicyCallback policy) {
419         if (policy == null) {
420             return;
421         }
422         synchronized (mAudioFocusLock) {
423             mFocusPolicy = policy;
424         }
425     }
426 
unsetFocusPolicy(IAudioPolicyCallback policy)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      */
notifyExtPolicyCurrentFocusAsync(IAudioPolicyCallback pcb)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      */
notifyExtPolicyFocusGrant_syncAf(AudioFocusInfo afi, int requestResult)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      */
notifyExtPolicyFocusLoss_syncAf(AudioFocusInfo afi, boolean wasDispatched)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      */
notifyExtFocusPolicyFocusRequest_syncAf(AudioFocusInfo afi, int requestResult, IAudioFocusDispatcher fd, IBinder cb)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      */
notifyExtFocusPolicyFocusAbandon_syncAf(AudioFocusInfo afi)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) */
dispatchFocusChange(AudioFocusInfo afi, int focusChange)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 
dumpExtFocusPolicyFocusOwners(PrintWriter pw)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 
getCurrentAudioFocus()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      */
getFocusRampTimeMs(int focusGain, AudioAttributes attr)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) */
requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb, IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags, int sdk)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      * */
abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa, String callingPackageName)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 
unregisterAudioFocusClient(String clientId)812     protected void unregisterAudioFocusClient(String clientId) {
813         synchronized(mAudioFocusLock) {
814             removeFocusStackEntry(clientId, false, true /*notifyFocusFollowers*/);
815         }
816     }
817 
runAudioCheckerForRingOrCallAsync(final boolean enteringRingOrCall)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