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