• 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.ContentResolver;
23 import android.content.Context;
24 import android.media.AudioAttributes;
25 import android.media.AudioFocusInfo;
26 import android.media.AudioManager;
27 import android.media.AudioSystem;
28 import android.media.IAudioFocusDispatcher;
29 import android.media.MediaMetrics;
30 import android.media.audiopolicy.IAudioPolicyCallback;
31 import android.os.Binder;
32 import android.os.Build;
33 import android.os.Handler;
34 import android.os.HandlerThread;
35 import android.os.IBinder;
36 import android.os.Message;
37 import android.os.RemoteException;
38 import android.provider.Settings;
39 import android.util.Log;
40 
41 import com.android.internal.annotations.GuardedBy;
42 
43 import java.io.PrintWriter;
44 import java.text.DateFormat;
45 import java.util.ArrayList;
46 import java.util.Date;
47 import java.util.HashMap;
48 import java.util.Iterator;
49 import java.util.LinkedList;
50 import java.util.List;
51 import java.util.Map.Entry;
52 import java.util.Set;
53 import java.util.Stack;
54 
55 /**
56  * @hide
57  *
58  */
59 public class MediaFocusControl implements PlayerFocusEnforcer {
60 
61     private static final String TAG = "MediaFocusControl";
62     static final boolean DEBUG = false;
63 
64     /**
65      * set to true so the framework enforces ducking itself, without communicating to apps
66      * that they lost focus for most use cases.
67      */
68     static final boolean ENFORCE_DUCKING = true;
69     /**
70      * set to true to the framework enforces ducking itself only with apps above a given SDK
71      * target level. Is ignored if ENFORCE_DUCKING is false.
72      */
73     static final boolean ENFORCE_DUCKING_FOR_NEW = true;
74     /**
75      * the SDK level (included) up to which the framework doesn't enforce ducking itself. Is ignored
76      * if ENFORCE_DUCKING_FOR_NEW is false;
77      */
78     // automatic ducking was introduced for Android O
79     static final int DUCKING_IN_APP_SDK_LEVEL = Build.VERSION_CODES.N_MR1;
80     /**
81      * set to true so the framework enforces muting media/game itself when the device is ringing
82      * or in a call.
83      */
84     static final boolean ENFORCE_MUTING_FOR_RING_OR_CALL = true;
85 
86     /**
87      * set to true so the framework enforces fading out apps that lose audio focus in a
88      * non-transient way.
89      */
90     static final boolean ENFORCE_FADEOUT_FOR_FOCUS_LOSS = true;
91 
92     private final Context mContext;
93     private final AppOpsManager mAppOps;
94     private PlayerFocusEnforcer mFocusEnforcer; // never null
95     private boolean mMultiAudioFocusEnabled = false;
96 
97     private boolean mRingOrCallActive = false;
98 
99     private final Object mExtFocusChangeLock = new Object();
100     @GuardedBy("mExtFocusChangeLock")
101     private long mExtFocusChangeCounter;
102 
MediaFocusControl(Context cntxt, PlayerFocusEnforcer pfe)103     protected MediaFocusControl(Context cntxt, PlayerFocusEnforcer pfe) {
104         mContext = cntxt;
105         mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);
106         mFocusEnforcer = pfe;
107         final ContentResolver cr = mContext.getContentResolver();
108         mMultiAudioFocusEnabled = Settings.System.getIntForUser(cr,
109                 Settings.System.MULTI_AUDIO_FOCUS_ENABLED, 0, cr.getUserId()) != 0;
110         initFocusThreading();
111     }
112 
dump(PrintWriter pw)113     protected void dump(PrintWriter pw) {
114         pw.println("\nMediaFocusControl dump time: "
115                 + DateFormat.getTimeInstance().format(new Date()));
116         dumpFocusStack(pw);
117         pw.println("\n");
118         // log
119         mEventLogger.dump(pw);
120         dumpMultiAudioFocus(pw);
121     }
122 
123     //=================================================================
124     // PlayerFocusEnforcer implementation
125     @Override
duckPlayers(@onNull FocusRequester winner, @NonNull FocusRequester loser, boolean forceDuck)126     public boolean duckPlayers(@NonNull FocusRequester winner, @NonNull FocusRequester loser,
127                                boolean forceDuck) {
128         return mFocusEnforcer.duckPlayers(winner, loser, forceDuck);
129     }
130 
131     @Override
restoreVShapedPlayers(@onNull FocusRequester winner)132     public void restoreVShapedPlayers(@NonNull FocusRequester winner) {
133         mFocusEnforcer.restoreVShapedPlayers(winner);
134     }
135 
136     @Override
mutePlayersForCall(int[] usagesToMute)137     public void mutePlayersForCall(int[] usagesToMute) {
138         mFocusEnforcer.mutePlayersForCall(usagesToMute);
139     }
140 
141     @Override
unmutePlayersForCall()142     public void unmutePlayersForCall() {
143         mFocusEnforcer.unmutePlayersForCall();
144     }
145 
146     @Override
fadeOutPlayers(@onNull FocusRequester winner, @NonNull FocusRequester loser)147     public boolean fadeOutPlayers(@NonNull FocusRequester winner, @NonNull FocusRequester loser) {
148         return mFocusEnforcer.fadeOutPlayers(winner, loser);
149     }
150 
151     @Override
forgetUid(int uid)152     public void forgetUid(int uid) {
153         mFocusEnforcer.forgetUid(uid);
154     }
155 
156     //==========================================================================================
157     // AudioFocus
158     //==========================================================================================
159 
160     private final static Object mAudioFocusLock = new Object();
161 
162     /**
163      * Arbitrary maximum size of audio focus stack to prevent apps OOM'ing this process.
164      */
165     private static final int MAX_STACK_SIZE = 100;
166 
167     private static final AudioEventLogger mEventLogger = new AudioEventLogger(50,
168             "focus commands as seen by MediaFocusControl");
169 
170     private static final String mMetricsId = MediaMetrics.Name.AUDIO_FOCUS;
171 
noFocusForSuspendedApp(@onNull String packageName, int uid)172     /*package*/ void noFocusForSuspendedApp(@NonNull String packageName, int uid) {
173         synchronized (mAudioFocusLock) {
174             final Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
175             List<String> clientsToRemove = new ArrayList<>();
176             while (stackIterator.hasNext()) {
177                 final FocusRequester focusOwner = stackIterator.next();
178                 if (focusOwner.hasSameUid(uid) && focusOwner.hasSamePackage(packageName)) {
179                     clientsToRemove.add(focusOwner.getClientId());
180                     mEventLogger.log((new AudioEventLogger.StringEvent(
181                             "focus owner:" + focusOwner.getClientId()
182                                     + " in uid:" + uid + " pack: " + packageName
183                                     + " getting AUDIOFOCUS_LOSS due to app suspension"))
184                             .printLog(TAG));
185                     // make the suspended app lose focus through its focus listener (if any)
186                     focusOwner.dispatchFocusChange(AudioManager.AUDIOFOCUS_LOSS);
187                 }
188             }
189             for (String clientToRemove : clientsToRemove) {
190                 // update the stack but don't signal the change.
191                 removeFocusStackEntry(clientToRemove, false, true);
192             }
193         }
194     }
195 
hasAudioFocusUsers()196     /*package*/ boolean hasAudioFocusUsers() {
197         synchronized (mAudioFocusLock) {
198             return !mFocusStack.empty();
199         }
200     }
201 
202     /**
203      * Discard the current audio focus owner.
204      * Notify top of audio focus stack that it lost focus (regardless of possibility to reassign
205      * focus), remove it from the stack, and clear the remote control display.
206      */
discardAudioFocusOwner()207     protected void discardAudioFocusOwner() {
208         synchronized(mAudioFocusLock) {
209             if (!mFocusStack.empty()) {
210                 // notify the current focus owner it lost focus after removing it from stack
211                 final FocusRequester exFocusOwner = mFocusStack.pop();
212                 exFocusOwner.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null,
213                         false /*forceDuck*/);
214                 exFocusOwner.release();
215             }
216         }
217     }
218 
219     @GuardedBy("mAudioFocusLock")
notifyTopOfAudioFocusStack()220     private void notifyTopOfAudioFocusStack() {
221         // notify the top of the stack it gained focus
222         if (!mFocusStack.empty()) {
223             if (canReassignAudioFocus()) {
224                 mFocusStack.peek().handleFocusGain(AudioManager.AUDIOFOCUS_GAIN);
225             }
226         }
227 
228         if (mMultiAudioFocusEnabled && !mMultiAudioFocusList.isEmpty()) {
229             for (FocusRequester multifr : mMultiAudioFocusList) {
230                 if (isLockedFocusOwner(multifr)) {
231                     multifr.handleFocusGain(AudioManager.AUDIOFOCUS_GAIN);
232                 }
233             }
234         }
235     }
236 
237     /**
238      * Focus is requested, propagate the associated loss throughout the stack.
239      * Will also remove entries in the stack that have just received a definitive loss of focus.
240      * @param focusGain the new focus gain that will later be added at the top of the stack
241      */
242     @GuardedBy("mAudioFocusLock")
propagateFocusLossFromGain_syncAf(int focusGain, final FocusRequester fr, boolean forceDuck)243     private void propagateFocusLossFromGain_syncAf(int focusGain, final FocusRequester fr,
244                                                    boolean forceDuck) {
245         final List<String> clientsToRemove = new LinkedList<String>();
246         // going through the audio focus stack to signal new focus, traversing order doesn't
247         // matter as all entries respond to the same external focus gain
248         if (!mFocusStack.empty()) {
249             for (FocusRequester focusLoser : mFocusStack) {
250                 final boolean isDefinitiveLoss =
251                         focusLoser.handleFocusLossFromGain(focusGain, fr, forceDuck);
252                 if (isDefinitiveLoss) {
253                     clientsToRemove.add(focusLoser.getClientId());
254                 }
255             }
256         }
257 
258         if (mMultiAudioFocusEnabled && !mMultiAudioFocusList.isEmpty()) {
259             for (FocusRequester multifocusLoser : mMultiAudioFocusList) {
260                 final boolean isDefinitiveLoss =
261                         multifocusLoser.handleFocusLossFromGain(focusGain, fr, forceDuck);
262                 if (isDefinitiveLoss) {
263                     clientsToRemove.add(multifocusLoser.getClientId());
264                 }
265             }
266         }
267 
268         for (String clientToRemove : clientsToRemove) {
269             removeFocusStackEntry(clientToRemove, false /*signal*/,
270                     true /*notifyFocusFollowers*/);
271         }
272     }
273 
274     private final Stack<FocusRequester> mFocusStack = new Stack<FocusRequester>();
275 
276     ArrayList<FocusRequester> mMultiAudioFocusList = new ArrayList<FocusRequester>();
277 
278     /**
279      * Helper function:
280      * Display in the log the current entries in the audio focus stack
281      */
dumpFocusStack(PrintWriter pw)282     private void dumpFocusStack(PrintWriter pw) {
283         pw.println("\nAudio Focus stack entries (last is top of stack):");
284         synchronized(mAudioFocusLock) {
285             Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
286             while(stackIterator.hasNext()) {
287                 stackIterator.next().dump(pw);
288             }
289             pw.println("\n");
290             if (mFocusPolicy == null) {
291                 pw.println("No external focus policy\n");
292             } else {
293                 pw.println("External focus policy: "+ mFocusPolicy + ", focus owners:\n");
294                 dumpExtFocusPolicyFocusOwners(pw);
295             }
296         }
297         pw.println("\n");
298         pw.println(" Notify on duck:  " + mNotifyFocusOwnerOnDuck + "\n");
299         pw.println(" In ring or call: " + mRingOrCallActive + "\n");
300     }
301 
302     /**
303      * Remove a focus listener from the focus stack.
304      * @param clientToRemove the focus listener
305      * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
306      *   focus, notify the next item in the stack it gained focus.
307      */
308     @GuardedBy("mAudioFocusLock")
removeFocusStackEntry(String clientToRemove, boolean signal, boolean notifyFocusFollowers)309     private void removeFocusStackEntry(String clientToRemove, boolean signal,
310             boolean notifyFocusFollowers) {
311         AudioFocusInfo abandonSource = null;
312         // is the current top of the focus stack abandoning focus? (because of request, not death)
313         if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientToRemove))
314         {
315             //Log.i(TAG, "   removeFocusStackEntry() removing top of stack");
316             FocusRequester fr = mFocusStack.pop();
317             fr.maybeRelease();
318             if (notifyFocusFollowers) {
319                 abandonSource = fr.toAudioFocusInfo();
320             }
321             if (signal) {
322                 // notify the new top of the stack it gained focus
323                 notifyTopOfAudioFocusStack();
324             }
325         } else {
326             // focus is abandoned by a client that's not at the top of the stack,
327             // no need to update focus.
328             // (using an iterator on the stack so we can safely remove an entry after having
329             //  evaluated it, traversal order doesn't matter here)
330             Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
331             while(stackIterator.hasNext()) {
332                 FocusRequester fr = stackIterator.next();
333                 if(fr.hasSameClient(clientToRemove)) {
334                     Log.i(TAG, "AudioFocus  removeFocusStackEntry(): removing entry for "
335                             + clientToRemove);
336                     stackIterator.remove();
337                     if (notifyFocusFollowers) {
338                         abandonSource = fr.toAudioFocusInfo();
339                     }
340                     // stack entry not used anymore, clear references
341                     fr.maybeRelease();
342                 }
343             }
344         }
345         // focus followers still want to know focus was abandoned, handled as a loss
346         if (abandonSource != null) {
347             abandonSource.clearLossReceived();
348             notifyExtPolicyFocusLoss_syncAf(abandonSource, false);
349         }
350 
351         if (mMultiAudioFocusEnabled && !mMultiAudioFocusList.isEmpty()) {
352             Iterator<FocusRequester> listIterator = mMultiAudioFocusList.iterator();
353             while (listIterator.hasNext()) {
354                 FocusRequester fr = listIterator.next();
355                 if (fr.hasSameClient(clientToRemove)) {
356                     listIterator.remove();
357                     fr.release();
358                 }
359             }
360 
361             if (signal) {
362                 // notify the new top of the stack it gained focus
363                 notifyTopOfAudioFocusStack();
364             }
365         }
366     }
367 
368     /**
369      * Remove focus listeners from the focus stack for a particular client when it has died.
370      */
371     @GuardedBy("mAudioFocusLock")
removeFocusStackEntryOnDeath(IBinder cb)372     private void removeFocusStackEntryOnDeath(IBinder cb) {
373         // is the owner of the audio focus part of the client to remove?
374         boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
375                 mFocusStack.peek().hasSameBinder(cb);
376         // (using an iterator on the stack so we can safely remove an entry after having
377         //  evaluated it, traversal order doesn't matter here)
378         Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
379         while(stackIterator.hasNext()) {
380             FocusRequester fr = stackIterator.next();
381             if(fr.hasSameBinder(cb)) {
382                 Log.i(TAG, "AudioFocus  removeFocusStackEntryOnDeath(): removing entry for " + cb);
383                 mEventLogger.log(new AudioEventLogger.StringEvent(
384                         "focus requester:" + fr.getClientId()
385                                 + " in uid:" + fr.getClientUid()
386                                 + " pack:" + fr.getPackageName()
387                                 + " died"));
388                 notifyExtPolicyFocusLoss_syncAf(fr.toAudioFocusInfo(), false);
389 
390                 stackIterator.remove();
391                 // stack entry not used anymore, clear references
392                 fr.release();
393             }
394         }
395         if (isTopOfStackForClientToRemove) {
396             // we removed an entry at the top of the stack:
397             //  notify the new top of the stack it gained focus.
398             notifyTopOfAudioFocusStack();
399         }
400     }
401 
402     /**
403      * Helper function for external focus policy:
404      * Remove focus listeners from the list of potential focus owners for a particular client when
405      * it has died.
406      */
407     @GuardedBy("mAudioFocusLock")
removeFocusEntryForExtPolicyOnDeath(IBinder cb)408     private void removeFocusEntryForExtPolicyOnDeath(IBinder cb) {
409         if (mFocusOwnersForFocusPolicy.isEmpty()) {
410             return;
411         }
412         boolean released = false;
413         final Set<Entry<String, FocusRequester>> owners = mFocusOwnersForFocusPolicy.entrySet();
414         final Iterator<Entry<String, FocusRequester>> ownerIterator = owners.iterator();
415         while (ownerIterator.hasNext()) {
416             final Entry<String, FocusRequester> owner = ownerIterator.next();
417             final FocusRequester fr = owner.getValue();
418             if (fr.hasSameBinder(cb)) {
419                 ownerIterator.remove();
420                 mEventLogger.log(new AudioEventLogger.StringEvent(
421                         "focus requester:" + fr.getClientId()
422                                 + " in uid:" + fr.getClientUid()
423                                 + " pack:" + fr.getPackageName()
424                                 + " died"));
425                 fr.release();
426                 notifyExtFocusPolicyFocusAbandon_syncAf(fr.toAudioFocusInfo());
427                 break;
428             }
429         }
430     }
431 
432     /**
433      * Helper function:
434      * Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
435      * The implementation guarantees that a state where focus cannot be immediately reassigned
436      * implies that an "locked" focus owner is at the top of the focus stack.
437      * Modifications to the implementation that break this assumption will cause focus requests to
438      * misbehave when honoring the AudioManager.AUDIOFOCUS_FLAG_DELAY_OK flag.
439      */
canReassignAudioFocus()440     private boolean canReassignAudioFocus() {
441         // focus requests are rejected during a phone call or when the phone is ringing
442         // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus
443         if (!mFocusStack.isEmpty() && isLockedFocusOwner(mFocusStack.peek())) {
444             return false;
445         }
446         return true;
447     }
448 
isLockedFocusOwner(FocusRequester fr)449     private boolean isLockedFocusOwner(FocusRequester fr) {
450         return (fr.hasSameClient(AudioSystem.IN_VOICE_COMM_FOCUS_ID) || fr.isLockedFocusOwner());
451     }
452 
453     /**
454      * Helper function
455      * Pre-conditions: focus stack is not empty, there is one or more locked focus owner
456      *                 at the top of the focus stack
457      * Push the focus requester onto the audio focus stack at the first position immediately
458      * following the locked focus owners.
459      * @return {@link AudioManager#AUDIOFOCUS_REQUEST_GRANTED} or
460      *     {@link AudioManager#AUDIOFOCUS_REQUEST_DELAYED}
461      */
462     @GuardedBy("mAudioFocusLock")
pushBelowLockedFocusOwners(FocusRequester nfr)463     private int pushBelowLockedFocusOwners(FocusRequester nfr) {
464         int lastLockedFocusOwnerIndex = mFocusStack.size();
465         for (int index = mFocusStack.size()-1; index >= 0; index--) {
466             if (isLockedFocusOwner(mFocusStack.elementAt(index))) {
467                 lastLockedFocusOwnerIndex = index;
468             }
469         }
470         if (lastLockedFocusOwnerIndex == mFocusStack.size()) {
471             // this should not happen, but handle it and log an error
472             Log.e(TAG, "No exclusive focus owner found in propagateFocusLossFromGain_syncAf()",
473                     new Exception());
474             // no exclusive owner, push at top of stack, focus is granted, propagate change
475             propagateFocusLossFromGain_syncAf(nfr.getGainRequest(), nfr, false /*forceDuck*/);
476             mFocusStack.push(nfr);
477             return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
478         } else {
479             mFocusStack.insertElementAt(nfr, lastLockedFocusOwnerIndex);
480             return AudioManager.AUDIOFOCUS_REQUEST_DELAYED;
481         }
482     }
483 
484     /**
485      * Inner class to monitor audio focus client deaths, and remove them from the audio focus
486      * stack if necessary.
487      */
488     protected class AudioFocusDeathHandler implements IBinder.DeathRecipient {
489         private IBinder mCb; // To be notified of client's death
490 
AudioFocusDeathHandler(IBinder cb)491         AudioFocusDeathHandler(IBinder cb) {
492             mCb = cb;
493         }
494 
binderDied()495         public void binderDied() {
496             synchronized(mAudioFocusLock) {
497                 if (mFocusPolicy != null) {
498                     removeFocusEntryForExtPolicyOnDeath(mCb);
499                 } else {
500                     removeFocusStackEntryOnDeath(mCb);
501                     if (mMultiAudioFocusEnabled && !mMultiAudioFocusList.isEmpty()) {
502                         Iterator<FocusRequester> listIterator = mMultiAudioFocusList.iterator();
503                         while (listIterator.hasNext()) {
504                             FocusRequester fr = listIterator.next();
505                             if (fr.hasSameBinder(mCb)) {
506                                 listIterator.remove();
507                                 fr.release();
508                             }
509                         }
510                     }
511                 }
512             }
513         }
514     }
515 
516     /**
517      * Indicates whether to notify an audio focus owner when it loses focus
518      * with {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK} if it will only duck.
519      * This variable being false indicates an AudioPolicy has been registered and has signaled
520      * it will handle audio ducking.
521      */
522     private boolean mNotifyFocusOwnerOnDuck = true;
523 
setDuckingInExtPolicyAvailable(boolean available)524     protected void setDuckingInExtPolicyAvailable(boolean available) {
525         mNotifyFocusOwnerOnDuck = !available;
526     }
527 
mustNotifyFocusOwnerOnDuck()528     boolean mustNotifyFocusOwnerOnDuck() { return mNotifyFocusOwnerOnDuck; }
529 
530     private ArrayList<IAudioPolicyCallback> mFocusFollowers = new ArrayList<IAudioPolicyCallback>();
531 
addFocusFollower(IAudioPolicyCallback ff)532     void addFocusFollower(IAudioPolicyCallback ff) {
533         if (ff == null) {
534             return;
535         }
536         synchronized(mAudioFocusLock) {
537             boolean found = false;
538             for (IAudioPolicyCallback pcb : mFocusFollowers) {
539                 if (pcb.asBinder().equals(ff.asBinder())) {
540                     found = true;
541                     break;
542                 }
543             }
544             if (found) {
545                 return;
546             } else {
547                 mFocusFollowers.add(ff);
548                 notifyExtPolicyCurrentFocusAsync(ff);
549             }
550         }
551     }
552 
removeFocusFollower(IAudioPolicyCallback ff)553     void removeFocusFollower(IAudioPolicyCallback ff) {
554         if (ff == null) {
555             return;
556         }
557         synchronized(mAudioFocusLock) {
558             for (IAudioPolicyCallback pcb : mFocusFollowers) {
559                 if (pcb.asBinder().equals(ff.asBinder())) {
560                     mFocusFollowers.remove(pcb);
561                     break;
562                 }
563             }
564         }
565     }
566 
567     /** The current audio focus policy */
568     @GuardedBy("mAudioFocusLock")
569     @Nullable private IAudioPolicyCallback mFocusPolicy = null;
570     /**
571      * The audio focus policy that was registered before a test focus policy was registered
572      * during a test
573      */
574     @GuardedBy("mAudioFocusLock")
575     @Nullable private IAudioPolicyCallback mPreviousFocusPolicy = null;
576 
577     // Since we don't have a stack of focus owners when using an external focus policy, we keep
578     // track of all the focus requesters in this map, with their clientId as the key. This is
579     // used both for focus dispatch and death handling
580     private HashMap<String, FocusRequester> mFocusOwnersForFocusPolicy =
581             new HashMap<String, FocusRequester>();
582 
setFocusPolicy(IAudioPolicyCallback policy, boolean isTestFocusPolicy)583     void setFocusPolicy(IAudioPolicyCallback policy, boolean isTestFocusPolicy) {
584         if (policy == null) {
585             return;
586         }
587         synchronized (mAudioFocusLock) {
588             if (isTestFocusPolicy) {
589                 mPreviousFocusPolicy = mFocusPolicy;
590             }
591             mFocusPolicy = policy;
592         }
593     }
594 
unsetFocusPolicy(IAudioPolicyCallback policy, boolean isTestFocusPolicy)595     void unsetFocusPolicy(IAudioPolicyCallback policy, boolean isTestFocusPolicy) {
596         if (policy == null) {
597             return;
598         }
599         synchronized (mAudioFocusLock) {
600             if (mFocusPolicy == policy) {
601                 if (isTestFocusPolicy) {
602                     // restore the focus policy that was there before the focus policy test started
603                     mFocusPolicy = mPreviousFocusPolicy;
604                 } else {
605                     mFocusPolicy = null;
606                 }
607             }
608         }
609     }
610 
611     /**
612      * @param pcb non null
613      */
notifyExtPolicyCurrentFocusAsync(IAudioPolicyCallback pcb)614     void notifyExtPolicyCurrentFocusAsync(IAudioPolicyCallback pcb) {
615         final IAudioPolicyCallback pcb2 = pcb;
616         final Thread thread = new Thread() {
617             @Override
618             public void run() {
619                 synchronized(mAudioFocusLock) {
620                     if (mFocusStack.isEmpty()) {
621                         return;
622                     }
623                     try {
624                         pcb2.notifyAudioFocusGrant(mFocusStack.peek().toAudioFocusInfo(),
625                                 // top of focus stack always has focus
626                                 AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
627                     } catch (RemoteException e) {
628                         Log.e(TAG, "Can't call notifyAudioFocusGrant() on IAudioPolicyCallback "
629                                 + pcb2.asBinder(), e);
630                     }
631                 }
632             }
633         };
634         thread.start();
635     }
636 
637     /**
638      * Called synchronized on mAudioFocusLock
639      */
notifyExtPolicyFocusGrant_syncAf(AudioFocusInfo afi, int requestResult)640     void notifyExtPolicyFocusGrant_syncAf(AudioFocusInfo afi, int requestResult) {
641         for (IAudioPolicyCallback pcb : mFocusFollowers) {
642             try {
643                 // oneway
644                 pcb.notifyAudioFocusGrant(afi, requestResult);
645             } catch (RemoteException e) {
646                 Log.e(TAG, "Can't call notifyAudioFocusGrant() on IAudioPolicyCallback "
647                         + pcb.asBinder(), e);
648             }
649         }
650     }
651 
652     /**
653      * Called synchronized on mAudioFocusLock
654      */
notifyExtPolicyFocusLoss_syncAf(AudioFocusInfo afi, boolean wasDispatched)655     void notifyExtPolicyFocusLoss_syncAf(AudioFocusInfo afi, boolean wasDispatched) {
656         for (IAudioPolicyCallback pcb : mFocusFollowers) {
657             try {
658                 // oneway
659                 pcb.notifyAudioFocusLoss(afi, wasDispatched);
660             } catch (RemoteException e) {
661                 Log.e(TAG, "Can't call notifyAudioFocusLoss() on IAudioPolicyCallback "
662                         + pcb.asBinder(), e);
663             }
664         }
665     }
666 
667     /**
668      * Called synchronized on mAudioFocusLock.
669      * Can only be called with an external focus policy installed (mFocusPolicy != null)
670      * @param afi
671      * @param fd
672      * @param cb binder of the focus requester
673      * @return true if the external audio focus policy (if any) can handle the focus request,
674      *     and false if there was any error handling the request (e.g. error talking to policy,
675      *     focus requester is already dead)
676      */
notifyExtFocusPolicyFocusRequest_syncAf(AudioFocusInfo afi, IAudioFocusDispatcher fd, @NonNull IBinder cb)677     boolean notifyExtFocusPolicyFocusRequest_syncAf(AudioFocusInfo afi,
678             IAudioFocusDispatcher fd, @NonNull IBinder cb) {
679         if (DEBUG) {
680             Log.v(TAG, "notifyExtFocusPolicyFocusRequest client="+afi.getClientId()
681             + " dispatcher=" + fd);
682         }
683         synchronized (mExtFocusChangeLock) {
684             afi.setGen(mExtFocusChangeCounter++);
685         }
686         final FocusRequester existingFr = mFocusOwnersForFocusPolicy.get(afi.getClientId());
687         boolean keepTrack = false;
688         if (existingFr != null) {
689             if (!existingFr.hasSameDispatcher(fd)) {
690                 existingFr.release();
691                 keepTrack = true;
692             }
693         } else {
694             keepTrack = true;
695         }
696         if (keepTrack) {
697             final AudioFocusDeathHandler hdlr = new AudioFocusDeathHandler(cb);
698             try {
699                 cb.linkToDeath(hdlr, 0);
700             } catch (RemoteException e) {
701                 // client has already died!
702                 return false;
703             }
704             // new focus (future) focus owner to keep track of
705             mFocusOwnersForFocusPolicy.put(afi.getClientId(),
706                     new FocusRequester(afi, fd, cb, hdlr, this));
707         }
708 
709         try {
710             //oneway
711             mFocusPolicy.notifyAudioFocusRequest(afi, AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
712             return true;
713         } catch (RemoteException e) {
714             Log.e(TAG, "Can't call notifyAudioFocusRequest() on IAudioPolicyCallback "
715                     + mFocusPolicy.asBinder(), e);
716         }
717         return false;
718     }
719 
setFocusRequestResultFromExtPolicy(AudioFocusInfo afi, int requestResult)720     void setFocusRequestResultFromExtPolicy(AudioFocusInfo afi, int requestResult) {
721         synchronized (mExtFocusChangeLock) {
722             if (afi.getGen() > mExtFocusChangeCounter) {
723                 return;
724             }
725         }
726         final FocusRequester fr;
727         if (requestResult == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
728             fr = mFocusOwnersForFocusPolicy.remove(afi.getClientId());
729         } else {
730             fr = mFocusOwnersForFocusPolicy.get(afi.getClientId());
731         }
732         if (fr != null) {
733             fr.dispatchFocusResultFromExtPolicy(requestResult);
734         }
735     }
736 
737     /**
738      * Called synchronized on mAudioFocusLock
739      * @param afi
740      * @return true if the external audio focus policy (if any) is handling the focus request
741      */
notifyExtFocusPolicyFocusAbandon_syncAf(AudioFocusInfo afi)742     boolean notifyExtFocusPolicyFocusAbandon_syncAf(AudioFocusInfo afi) {
743         if (mFocusPolicy == null) {
744             return false;
745         }
746         final FocusRequester fr = mFocusOwnersForFocusPolicy.remove(afi.getClientId());
747         if (fr != null) {
748             fr.release();
749         }
750         try {
751             //oneway
752             mFocusPolicy.notifyAudioFocusAbandon(afi);
753         } catch (RemoteException e) {
754             Log.e(TAG, "Can't call notifyAudioFocusAbandon() on IAudioPolicyCallback "
755                     + mFocusPolicy.asBinder(), e);
756         }
757         return true;
758     }
759 
760     /** see AudioManager.dispatchFocusChange(AudioFocusInfo afi, int focusChange, AudioPolicy ap) */
dispatchFocusChange(AudioFocusInfo afi, int focusChange)761     int dispatchFocusChange(AudioFocusInfo afi, int focusChange) {
762         if (DEBUG) {
763             Log.v(TAG, "dispatchFocusChange " + focusChange + " to afi client="
764                     + afi.getClientId());
765         }
766         synchronized (mAudioFocusLock) {
767             if (mFocusPolicy == null) {
768                 if (DEBUG) { Log.v(TAG, "> failed: no focus policy" ); }
769                 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
770             }
771             final FocusRequester fr;
772             if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
773                 fr = mFocusOwnersForFocusPolicy.remove(afi.getClientId());
774             } else {
775                 fr = mFocusOwnersForFocusPolicy.get(afi.getClientId());
776             }
777             if (fr == null) {
778                 if (DEBUG) { Log.v(TAG, "> failed: no such focus requester known" ); }
779                 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
780             }
781             return fr.dispatchFocusChange(focusChange);
782         }
783     }
784 
dumpExtFocusPolicyFocusOwners(PrintWriter pw)785     private void dumpExtFocusPolicyFocusOwners(PrintWriter pw) {
786         final Set<Entry<String, FocusRequester>> owners = mFocusOwnersForFocusPolicy.entrySet();
787         final Iterator<Entry<String, FocusRequester>> ownerIterator = owners.iterator();
788         while (ownerIterator.hasNext()) {
789             final Entry<String, FocusRequester> owner = ownerIterator.next();
790             final FocusRequester fr = owner.getValue();
791             fr.dump(pw);
792         }
793     }
794 
getCurrentAudioFocus()795     protected int getCurrentAudioFocus() {
796         synchronized(mAudioFocusLock) {
797             if (mFocusStack.empty()) {
798                 return AudioManager.AUDIOFOCUS_NONE;
799             } else {
800                 return mFocusStack.peek().getGainRequest();
801             }
802         }
803     }
804 
805     /**
806      * Delay after entering ringing or call mode after which the framework will mute streams
807      * that are still playing.
808      */
809     private static final int RING_CALL_MUTING_ENFORCEMENT_DELAY_MS = 100;
810 
811     /**
812      * Usages to mute when the device rings or is in a call
813      */
814     private final static int[] USAGES_TO_MUTE_IN_RING_OR_CALL =
815         { AudioAttributes.USAGE_MEDIA, AudioAttributes.USAGE_GAME };
816 
817     /**
818      * Return the volume ramp time expected before playback with the given AudioAttributes would
819      * start after gaining audio focus.
820      * @param attr attributes of the sound about to start playing
821      * @return time in ms
822      */
getFocusRampTimeMs(int focusGain, AudioAttributes attr)823     protected static int getFocusRampTimeMs(int focusGain, AudioAttributes attr) {
824         switch (attr.getUsage()) {
825             case AudioAttributes.USAGE_MEDIA:
826             case AudioAttributes.USAGE_GAME:
827                 return 1000;
828             case AudioAttributes.USAGE_ALARM:
829             case AudioAttributes.USAGE_NOTIFICATION_RINGTONE:
830             case AudioAttributes.USAGE_ASSISTANT:
831             case AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY:
832             case AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
833             case AudioAttributes.USAGE_ANNOUNCEMENT:
834                 return 700;
835             case AudioAttributes.USAGE_VOICE_COMMUNICATION:
836             case AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING:
837             case AudioAttributes.USAGE_NOTIFICATION:
838             case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
839             case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
840             case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
841             case AudioAttributes.USAGE_NOTIFICATION_EVENT:
842             case AudioAttributes.USAGE_ASSISTANCE_SONIFICATION:
843             case AudioAttributes.USAGE_VEHICLE_STATUS:
844                 return 500;
845             case AudioAttributes.USAGE_EMERGENCY:
846             case AudioAttributes.USAGE_SAFETY:
847             case AudioAttributes.USAGE_UNKNOWN:
848             default:
849                 return 0;
850         }
851     }
852 
853     /** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int, int)
854      * @param aa
855      * @param focusChangeHint
856      * @param cb
857      * @param fd
858      * @param clientId
859      * @param callingPackageName
860      * @param flags
861      * @param sdk
862      * @param forceDuck only true if
863      *     {@link android.media.AudioFocusRequest.Builder#setFocusGain(int)} was set to true for
864      *                  accessibility.
865      * @param testUid ignored if flags is not AudioManager.AUDIOFOCUS_FLAG_TEST (strictly equals to)
866      *                otherwise the UID being injected for testing
867      * @return
868      */
requestAudioFocus(@onNull AudioAttributes aa, int focusChangeHint, IBinder cb, IAudioFocusDispatcher fd, @NonNull String clientId, @NonNull String callingPackageName, int flags, int sdk, boolean forceDuck, int testUid)869     protected int requestAudioFocus(@NonNull AudioAttributes aa, int focusChangeHint, IBinder cb,
870             IAudioFocusDispatcher fd, @NonNull String clientId, @NonNull String callingPackageName,
871             int flags, int sdk, boolean forceDuck, int testUid) {
872         new MediaMetrics.Item(mMetricsId)
873                 .setUid(Binder.getCallingUid())
874                 .set(MediaMetrics.Property.CALLING_PACKAGE, callingPackageName)
875                 .set(MediaMetrics.Property.CLIENT_NAME, clientId)
876                 .set(MediaMetrics.Property.EVENT, "requestAudioFocus")
877                 .set(MediaMetrics.Property.FLAGS, flags)
878                 .set(MediaMetrics.Property.FOCUS_CHANGE_HINT,
879                         AudioManager.audioFocusToString(focusChangeHint))
880                 //.set(MediaMetrics.Property.SDK, sdk)
881                 .record();
882 
883         // when using the test API, a fake UID can be injected (testUid is ignored otherwise)
884         // note that the test on flags is not a mask test on purpose, AUDIOFOCUS_FLAG_TEST is
885         // supposed to be alone in bitfield
886         final int uid = (flags == AudioManager.AUDIOFOCUS_FLAG_TEST)
887                 ? testUid : Binder.getCallingUid();
888         mEventLogger.log((new AudioEventLogger.StringEvent(
889                 "requestAudioFocus() from uid/pid " + uid
890                     + "/" + Binder.getCallingPid()
891                     + " AA=" + aa.usageToString() + "/" + aa.contentTypeToString()
892                     + " clientId=" + clientId + " callingPack=" + callingPackageName
893                     + " req=" + focusChangeHint
894                     + " flags=0x" + Integer.toHexString(flags)
895                     + " sdk=" + sdk))
896                 .printLog(TAG));
897         // we need a valid binder callback for clients
898         if (!cb.pingBinder()) {
899             Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
900             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
901         }
902 
903         if ((flags != AudioManager.AUDIOFOCUS_FLAG_TEST)
904                 // note we're using the real uid for appOp evaluation
905                 && (mAppOps.noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, Binder.getCallingUid(),
906                         callingPackageName) != AppOpsManager.MODE_ALLOWED)) {
907             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
908         }
909 
910         synchronized(mAudioFocusLock) {
911             if (mFocusStack.size() > MAX_STACK_SIZE) {
912                 Log.e(TAG, "Max AudioFocus stack size reached, failing requestAudioFocus()");
913                 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
914             }
915 
916             boolean enteringRingOrCall = !mRingOrCallActive
917                     & (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0);
918             if (enteringRingOrCall) { mRingOrCallActive = true; }
919 
920             final AudioFocusInfo afiForExtPolicy;
921             if (mFocusPolicy != null) {
922                 // construct AudioFocusInfo as it will be communicated to audio focus policy
923                 afiForExtPolicy = new AudioFocusInfo(aa, uid,
924                         clientId, callingPackageName, focusChangeHint, 0 /*lossReceived*/,
925                         flags, sdk);
926             } else {
927                 afiForExtPolicy = null;
928             }
929 
930             // handle delayed focus
931             boolean focusGrantDelayed = false;
932             if (!canReassignAudioFocus()) {
933                 if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) == 0) {
934                     return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
935                 } else {
936                     // request has AUDIOFOCUS_FLAG_DELAY_OK: focus can't be
937                     // granted right now, so the requester will be inserted in the focus stack
938                     // to receive focus later
939                     focusGrantDelayed = true;
940                 }
941             }
942 
943             // external focus policy?
944             if (mFocusPolicy != null) {
945                 if (notifyExtFocusPolicyFocusRequest_syncAf(afiForExtPolicy, fd, cb)) {
946                     // stop handling focus request here as it is handled by external audio
947                     // focus policy (return code will be handled in AudioManager)
948                     return AudioManager.AUDIOFOCUS_REQUEST_WAITING_FOR_EXT_POLICY;
949                 } else {
950                     // an error occured, client already dead, bail early
951                     return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
952                 }
953             }
954 
955             // handle the potential premature death of the new holder of the focus
956             // (premature death == death before abandoning focus)
957             // Register for client death notification
958             AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
959 
960             try {
961                 cb.linkToDeath(afdh, 0);
962             } catch (RemoteException e) {
963                 // client has already died!
964                 Log.w(TAG, "AudioFocus  requestAudioFocus() could not link to "+cb+" binder death");
965                 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
966             }
967 
968             if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientId)) {
969                 // if focus is already owned by this client and the reason for acquiring the focus
970                 // hasn't changed, don't do anything
971                 final FocusRequester fr = mFocusStack.peek();
972                 if (fr.getGainRequest() == focusChangeHint && fr.getGrantFlags() == flags) {
973                     // unlink death handler so it can be gc'ed.
974                     // linkToDeath() creates a JNI global reference preventing collection.
975                     cb.unlinkToDeath(afdh, 0);
976                     notifyExtPolicyFocusGrant_syncAf(fr.toAudioFocusInfo(),
977                             AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
978                     return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
979                 }
980                 // the reason for the audio focus request has changed: remove the current top of
981                 // stack and respond as if we had a new focus owner
982                 if (!focusGrantDelayed) {
983                     mFocusStack.pop();
984                     // the entry that was "popped" is the same that was "peeked" above
985                     fr.release();
986                 }
987             }
988 
989             // focus requester might already be somewhere below in the stack, remove it
990             removeFocusStackEntry(clientId, false /* signal */, false /*notifyFocusFollowers*/);
991 
992             final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb,
993                     clientId, afdh, callingPackageName, uid, this, sdk);
994 
995             if (mMultiAudioFocusEnabled
996                     && (focusChangeHint == AudioManager.AUDIOFOCUS_GAIN)) {
997                 if (enteringRingOrCall) {
998                     if (!mMultiAudioFocusList.isEmpty()) {
999                         for (FocusRequester multifr : mMultiAudioFocusList) {
1000                             multifr.handleFocusLossFromGain(focusChangeHint, nfr, forceDuck);
1001                         }
1002                     }
1003                 } else {
1004                     boolean needAdd = true;
1005                     if (!mMultiAudioFocusList.isEmpty()) {
1006                         for (FocusRequester multifr : mMultiAudioFocusList) {
1007                             if (multifr.getClientUid() == Binder.getCallingUid()) {
1008                                 needAdd = false;
1009                                 break;
1010                             }
1011                         }
1012                     }
1013                     if (needAdd) {
1014                         mMultiAudioFocusList.add(nfr);
1015                     }
1016                     nfr.handleFocusGainFromRequest(AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
1017                     notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(),
1018                             AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
1019                     return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
1020                 }
1021             }
1022 
1023             if (focusGrantDelayed) {
1024                 // focusGrantDelayed being true implies we can't reassign focus right now
1025                 // which implies the focus stack is not empty.
1026                 final int requestResult = pushBelowLockedFocusOwners(nfr);
1027                 if (requestResult != AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
1028                     notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(), requestResult);
1029                 }
1030                 return requestResult;
1031             } else {
1032                 // propagate the focus change through the stack
1033                 propagateFocusLossFromGain_syncAf(focusChangeHint, nfr, forceDuck);
1034 
1035                 // push focus requester at the top of the audio focus stack
1036                 mFocusStack.push(nfr);
1037                 nfr.handleFocusGainFromRequest(AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
1038             }
1039             notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(),
1040                     AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
1041 
1042             if (ENFORCE_MUTING_FOR_RING_OR_CALL & enteringRingOrCall) {
1043                 runAudioCheckerForRingOrCallAsync(true/*enteringRingOrCall*/);
1044             }
1045         }//synchronized(mAudioFocusLock)
1046 
1047         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
1048     }
1049 
1050     /**
1051      * @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener, AudioAttributes)
1052      * */
abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa, String callingPackageName)1053     protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa,
1054             String callingPackageName) {
1055         new MediaMetrics.Item(mMetricsId)
1056                 .setUid(Binder.getCallingUid())
1057                 .set(MediaMetrics.Property.CALLING_PACKAGE, callingPackageName)
1058                 .set(MediaMetrics.Property.CLIENT_NAME, clientId)
1059                 .set(MediaMetrics.Property.EVENT, "abandonAudioFocus")
1060                 .record();
1061 
1062         // AudioAttributes are currently ignored, to be used for zones / a11y
1063         mEventLogger.log((new AudioEventLogger.StringEvent(
1064                 "abandonAudioFocus() from uid/pid " + Binder.getCallingUid()
1065                     + "/" + Binder.getCallingPid()
1066                     + " clientId=" + clientId))
1067                 .printLog(TAG));
1068         try {
1069             // this will take care of notifying the new focus owner if needed
1070             synchronized(mAudioFocusLock) {
1071                 // external focus policy?
1072                 if (mFocusPolicy != null) {
1073                     final AudioFocusInfo afi = new AudioFocusInfo(aa, Binder.getCallingUid(),
1074                             clientId, callingPackageName, 0 /*gainRequest*/, 0 /*lossReceived*/,
1075                             0 /*flags*/, 0 /* sdk n/a here*/);
1076                     if (notifyExtFocusPolicyFocusAbandon_syncAf(afi)) {
1077                         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
1078                     }
1079                 }
1080 
1081                 boolean exitingRingOrCall = mRingOrCallActive
1082                         & (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0);
1083                 if (exitingRingOrCall) { mRingOrCallActive = false; }
1084 
1085                 removeFocusStackEntry(clientId, true /*signal*/, true /*notifyFocusFollowers*/);
1086 
1087                 if (ENFORCE_MUTING_FOR_RING_OR_CALL & exitingRingOrCall) {
1088                     runAudioCheckerForRingOrCallAsync(false/*enteringRingOrCall*/);
1089                 }
1090             }
1091         } catch (java.util.ConcurrentModificationException cme) {
1092             // Catching this exception here is temporary. It is here just to prevent
1093             // a crash seen when the "Silent" notification is played. This is believed to be fixed
1094             // but this try catch block is left just to be safe.
1095             Log.e(TAG, "FATAL EXCEPTION AudioFocus  abandonAudioFocus() caused " + cme);
1096             cme.printStackTrace();
1097         }
1098 
1099         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
1100     }
1101 
1102 
unregisterAudioFocusClient(String clientId)1103     protected void unregisterAudioFocusClient(String clientId) {
1104         synchronized(mAudioFocusLock) {
1105             removeFocusStackEntry(clientId, false, true /*notifyFocusFollowers*/);
1106         }
1107     }
1108 
runAudioCheckerForRingOrCallAsync(final boolean enteringRingOrCall)1109     private void runAudioCheckerForRingOrCallAsync(final boolean enteringRingOrCall) {
1110         new Thread() {
1111             public void run() {
1112                 if (enteringRingOrCall) {
1113                     try {
1114                         Thread.sleep(RING_CALL_MUTING_ENFORCEMENT_DELAY_MS);
1115                     } catch (InterruptedException e) {
1116                         e.printStackTrace();
1117                     }
1118                 }
1119                 synchronized (mAudioFocusLock) {
1120                     // since the new thread starting running the state could have changed, so
1121                     // we need to check again mRingOrCallActive, not enteringRingOrCall
1122                     if (mRingOrCallActive) {
1123                         mFocusEnforcer.mutePlayersForCall(USAGES_TO_MUTE_IN_RING_OR_CALL);
1124                     } else {
1125                         mFocusEnforcer.unmutePlayersForCall();
1126                     }
1127                 }
1128             }
1129         }.start();
1130     }
1131 
updateMultiAudioFocus(boolean enabled)1132     public void updateMultiAudioFocus(boolean enabled) {
1133         Log.d(TAG, "updateMultiAudioFocus( " + enabled + " )");
1134         mMultiAudioFocusEnabled = enabled;
1135         final ContentResolver cr = mContext.getContentResolver();
1136         Settings.System.putIntForUser(cr,
1137                 Settings.System.MULTI_AUDIO_FOCUS_ENABLED, enabled ? 1 : 0, cr.getUserId());
1138         if (!mFocusStack.isEmpty()) {
1139             final FocusRequester fr = mFocusStack.peek();
1140             fr.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null, false);
1141         }
1142         if (!enabled) {
1143             if (!mMultiAudioFocusList.isEmpty()) {
1144                 for (FocusRequester multifr : mMultiAudioFocusList) {
1145                     multifr.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null, false);
1146                 }
1147                 mMultiAudioFocusList.clear();
1148             }
1149         }
1150     }
1151 
getMultiAudioFocusEnabled()1152     public boolean getMultiAudioFocusEnabled() {
1153         return mMultiAudioFocusEnabled;
1154     }
1155 
getFadeOutDurationOnFocusLossMillis(AudioAttributes aa)1156     /*package*/ long getFadeOutDurationOnFocusLossMillis(AudioAttributes aa) {
1157         if (!ENFORCE_FADEOUT_FOR_FOCUS_LOSS) {
1158             return 0;
1159         }
1160         return FadeOutManager.getFadeOutDurationOnFocusLossMillis(aa);
1161     }
1162 
dumpMultiAudioFocus(PrintWriter pw)1163     private void dumpMultiAudioFocus(PrintWriter pw) {
1164         pw.println("Multi Audio Focus enabled :" + mMultiAudioFocusEnabled);
1165         if (!mMultiAudioFocusList.isEmpty()) {
1166             pw.println("Multi Audio Focus List:");
1167             pw.println("------------------------------");
1168             for (FocusRequester multifr : mMultiAudioFocusList) {
1169                 multifr.dump(pw);
1170             }
1171             pw.println("------------------------------");
1172         }
1173     }
1174 
1175     //=================================================================
1176     // Async focus events
postDelayedLossAfterFade(FocusRequester focusLoser, long delayMs)1177     void postDelayedLossAfterFade(FocusRequester focusLoser, long delayMs) {
1178         if (DEBUG) {
1179             Log.v(TAG, "postDelayedLossAfterFade loser=" + focusLoser.getPackageName());
1180         }
1181         mFocusHandler.sendMessageDelayed(
1182                 mFocusHandler.obtainMessage(MSG_L_FOCUS_LOSS_AFTER_FADE, focusLoser),
1183                 FadeOutManager.FADE_OUT_DURATION_MS);
1184     }
1185     //=================================================================
1186     // Message handling
1187     private Handler mFocusHandler;
1188     private HandlerThread mFocusThread;
1189 
1190     /**
1191      * dispatch a focus loss after an app has been faded out. Focus loser is to be released
1192      * after dispatch as it has already left the stack
1193      * args:
1194      *     msg.obj: the audio focus loser
1195      *         type:FocusRequester
1196      */
1197     private static final int MSG_L_FOCUS_LOSS_AFTER_FADE = 1;
1198 
initFocusThreading()1199     private void initFocusThreading() {
1200         mFocusThread = new HandlerThread(TAG);
1201         mFocusThread.start();
1202         mFocusHandler = new Handler(mFocusThread.getLooper()) {
1203             @Override
1204             public void handleMessage(Message msg) {
1205                 switch (msg.what) {
1206                     case MSG_L_FOCUS_LOSS_AFTER_FADE:
1207                         if (DEBUG) {
1208                             Log.d(TAG, "MSG_L_FOCUS_LOSS_AFTER_FADE loser="
1209                                     + ((FocusRequester) msg.obj).getPackageName());
1210                         }
1211                         synchronized (mAudioFocusLock) {
1212                             final FocusRequester loser = (FocusRequester) msg.obj;
1213                             if (loser.isInFocusLossLimbo()) {
1214                                 loser.dispatchFocusChange(AudioManager.AUDIOFOCUS_LOSS);
1215                                 loser.release();
1216                                 mFocusEnforcer.forgetUid(loser.getClientUid());
1217                             }
1218                         }
1219                         break;
1220                     default:
1221                         break;
1222                 }
1223             }
1224         };
1225 
1226     }
1227 }
1228