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