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