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 static android.media.audiopolicy.Flags.enableFadeManagerConfiguration;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.app.AppOpsManager;
24 import android.content.ContentResolver;
25 import android.content.Context;
26 import android.media.AudioAttributes;
27 import android.media.AudioFocusInfo;
28 import android.media.AudioManager;
29 import android.media.AudioSystem;
30 import android.media.IAudioFocusDispatcher;
31 import android.media.MediaMetrics;
32 import android.media.audiopolicy.AudioPolicy;
33 import android.media.audiopolicy.IAudioPolicyCallback;
34 import android.os.Binder;
35 import android.os.Build;
36 import android.os.Handler;
37 import android.os.HandlerThread;
38 import android.os.IBinder;
39 import android.os.Message;
40 import android.os.RemoteException;
41 import android.provider.Settings;
42 import android.util.Log;
43 
44 import com.android.internal.annotations.GuardedBy;
45 import com.android.server.utils.EventLogger;
46 
47 import java.io.PrintWriter;
48 import java.text.DateFormat;
49 import java.util.ArrayList;
50 import java.util.Arrays;
51 import java.util.Date;
52 import java.util.HashMap;
53 import java.util.Iterator;
54 import java.util.LinkedList;
55 import java.util.List;
56 import java.util.Map.Entry;
57 import java.util.Set;
58 import java.util.Stack;
59 
60 /**
61  * @hide
62  *
63  */
64 public class MediaFocusControl implements PlayerFocusEnforcer {
65 
66     private static final String TAG = "MediaFocusControl";
67     static final boolean DEBUG = false;
68 
69     /**
70      * set to true so the framework enforces ducking itself, without communicating to apps
71      * that they lost focus for most use cases.
72      */
73     static final boolean ENFORCE_DUCKING = true;
74     /**
75      * set to true to the framework enforces ducking itself only with apps above a given SDK
76      * target level. Is ignored if ENFORCE_DUCKING is false.
77      */
78     static final boolean ENFORCE_DUCKING_FOR_NEW = true;
79     /**
80      * the SDK level (included) up to which the framework doesn't enforce ducking itself. Is ignored
81      * if ENFORCE_DUCKING_FOR_NEW is false;
82      */
83     // automatic ducking was introduced for Android O
84     static final int DUCKING_IN_APP_SDK_LEVEL = Build.VERSION_CODES.N_MR1;
85     /**
86      * set to true so the framework enforces muting media/game itself when the device is ringing
87      * or in a call.
88      */
89     static final boolean ENFORCE_MUTING_FOR_RING_OR_CALL = true;
90 
91     /**
92      * set to true so the framework enforces fading out apps that lose audio focus in a
93      * non-transient way.
94      */
95     static final boolean ENFORCE_FADEOUT_FOR_FOCUS_LOSS = true;
96 
97     private final Context mContext;
98     private final AppOpsManager mAppOps;
99     private final @NonNull PlayerFocusEnforcer mFocusEnforcer;
100     private boolean mMultiAudioFocusEnabled = false;
101 
102     private boolean mRingOrCallActive = false;
103 
104     private final Object mExtFocusChangeLock = new Object();
105     @GuardedBy("mExtFocusChangeLock")
106     private long mExtFocusChangeCounter;
107 
MediaFocusControl(Context cntxt, PlayerFocusEnforcer pfe)108     protected MediaFocusControl(Context cntxt, PlayerFocusEnforcer pfe) {
109         mContext = cntxt;
110         mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);
111         mFocusEnforcer = pfe;
112         final ContentResolver cr = mContext.getContentResolver();
113         mMultiAudioFocusEnabled = Settings.System.getIntForUser(cr,
114                 Settings.System.MULTI_AUDIO_FOCUS_ENABLED, 0, cr.getUserId()) != 0;
115         initFocusThreading();
116     }
117 
dump(PrintWriter pw)118     protected void dump(PrintWriter pw) {
119         pw.println("\nMediaFocusControl dump time: "
120                 + DateFormat.getTimeInstance().format(new Date()));
121         dumpFocusStack(pw);
122         pw.println("\n");
123         // log
124         mEventLogger.dump(pw);
125         dumpMultiAudioFocus(pw);
126     }
127 
128     /**
129      * Test method to return the duration of the fade out applied on the players of a focus loser
130      * @return the fade out duration in ms
131      */
getFocusFadeOutDurationForTest()132     public long getFocusFadeOutDurationForTest() {
133         return getFadeOutDurationMillis(
134                 new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build());
135     }
136 
137     /**
138      * Test method to return the length of time after a fade out before the focus loser is unmuted
139      * (and is faded back in).
140      * @return the time gap after a fade out completion on focus loss, and fade in start in ms
141      */
getFocusUnmuteDelayAfterFadeOutForTest()142     public long getFocusUnmuteDelayAfterFadeOutForTest() {
143         return getFadeInDelayForOffendersMillis(
144                 new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build());
145     }
146 
147     //=================================================================
148     // PlayerFocusEnforcer implementation
149     @Override
duckPlayers(@onNull FocusRequester winner, @NonNull FocusRequester loser, boolean forceDuck)150     public boolean duckPlayers(@NonNull FocusRequester winner, @NonNull FocusRequester loser,
151                                boolean forceDuck) {
152         return mFocusEnforcer.duckPlayers(winner, loser, forceDuck);
153     }
154 
155     @Override
restoreVShapedPlayers(@onNull FocusRequester winner)156     public void restoreVShapedPlayers(@NonNull FocusRequester winner) {
157         mFocusEnforcer.restoreVShapedPlayers(winner);
158         // remove scheduled events to unfade out offending players (if any) corresponding to
159         // this uid, as we're removing any effects of muting/ducking/fade out now
160         mFocusHandler.removeEqualMessages(MSL_L_FORGET_UID,
161                 new ForgetFadeUidInfo(winner.getClientUid()));
162 
163     }
164 
165     @Override
mutePlayersForCall(int[] usagesToMute)166     public void mutePlayersForCall(int[] usagesToMute) {
167         mFocusEnforcer.mutePlayersForCall(usagesToMute);
168     }
169 
170     @Override
unmutePlayersForCall()171     public void unmutePlayersForCall() {
172         mFocusEnforcer.unmutePlayersForCall();
173     }
174 
175     @Override
fadeOutPlayers(@onNull FocusRequester winner, @NonNull FocusRequester loser)176     public boolean fadeOutPlayers(@NonNull FocusRequester winner, @NonNull FocusRequester loser) {
177         return mFocusEnforcer.fadeOutPlayers(winner, loser);
178     }
179 
180     @Override
forgetUid(int uid)181     public void forgetUid(int uid) {
182         mFocusEnforcer.forgetUid(uid);
183     }
184 
185     @Override
getFadeOutDurationMillis(@onNull AudioAttributes aa)186     public long getFadeOutDurationMillis(@NonNull AudioAttributes aa) {
187         if (aa == null) {
188             return 0;
189         }
190         return mFocusEnforcer.getFadeOutDurationMillis(aa);
191     }
192 
193     @Override
getFadeInDelayForOffendersMillis(@onNull AudioAttributes aa)194     public long getFadeInDelayForOffendersMillis(@NonNull AudioAttributes aa) {
195         if (aa == null) {
196             return 0;
197         }
198         return mFocusEnforcer.getFadeInDelayForOffendersMillis(aa);
199     }
200 
201     @Override
shouldEnforceFade()202     public boolean shouldEnforceFade() {
203         if (!enableFadeManagerConfiguration()) {
204             return ENFORCE_FADEOUT_FOR_FOCUS_LOSS;
205         }
206 
207         return mFocusEnforcer.shouldEnforceFade();
208     }
209     //==========================================================================================
210     // AudioFocus
211     //==========================================================================================
212 
213     private final static Object mAudioFocusLock = new Object();
214 
215     /**
216      * Arbitrary maximum size of audio focus stack to prevent apps OOM'ing this process.
217      */
218     private static final int MAX_STACK_SIZE = 100;
219 
220     private static final EventLogger
221             mEventLogger = new EventLogger(50,
222             "focus commands as seen by MediaFocusControl");
223 
224     private static final String mMetricsId = MediaMetrics.Name.AUDIO_FOCUS;
225 
noFocusForSuspendedApp(@onNull String packageName, int uid)226     /*package*/ void noFocusForSuspendedApp(@NonNull String packageName, int uid) {
227         synchronized (mAudioFocusLock) {
228             final Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
229             List<String> clientsToRemove = new ArrayList<>();
230             while (stackIterator.hasNext()) {
231                 final FocusRequester focusOwner = stackIterator.next();
232                 if (focusOwner.hasSameUid(uid) && focusOwner.hasSamePackage(packageName)) {
233                     clientsToRemove.add(focusOwner.getClientId());
234                     mEventLogger.enqueue((new EventLogger.StringEvent(
235                             "focus owner:" + focusOwner.getClientId()
236                                     + " in uid:" + uid + " pack: " + packageName
237                                     + " getting AUDIOFOCUS_LOSS due to app suspension"))
238                             .printLog(TAG));
239                     // make the suspended app lose focus through its focus listener (if any)
240                     focusOwner.dispatchFocusChange(AudioManager.AUDIOFOCUS_LOSS);
241                 }
242             }
243             for (String clientToRemove : clientsToRemove) {
244                 // update the stack but don't signal the change.
245                 removeFocusStackEntry(clientToRemove, false, true);
246             }
247         }
248     }
249 
hasAudioFocusUsers()250     /*package*/ boolean hasAudioFocusUsers() {
251         synchronized (mAudioFocusLock) {
252             return !mFocusStack.empty();
253         }
254     }
255 
256     /**
257      * Discard the current audio focus owner (unless the user is considered {@link
258      * FocusRequester#isAlwaysVisibleUser() always visible)}.
259      * Notify top of audio focus stack that it lost focus (regardless of possibility to reassign
260      * focus), remove it from the stack, and clear the remote control display.
261      * @return whether the current audio focus owner was discarded (including if there was none);
262      *         returns false if it was purposefully kept
263      */
maybeDiscardAudioFocusOwner()264     protected boolean maybeDiscardAudioFocusOwner() {
265         synchronized(mAudioFocusLock) {
266             if (!mFocusStack.empty()) {
267                 final FocusRequester exFocusOwner = mFocusStack.peek();
268                 if (!exFocusOwner.isAlwaysVisibleUser()) {
269                     mFocusStack.pop();
270                     exFocusOwner.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null,
271                             false /*forceDuck*/);
272                     exFocusOwner.release();
273                     return true;
274                 } else {
275                     return false;
276                 }
277             }
278         }
279         return true;
280     }
281 
282     /**
283      * Return a copy of the focus stack for external consumption (composed of AudioFocusInfo
284      * instead of FocusRequester instances)
285      * @return a SystemApi-friendly version of the focus stack, in the same order (last entry
286      *         is top of focus stack, i.e. latest focus owner)
287      * @see AudioPolicy#getFocusStack()
288      */
getFocusStack()289     @NonNull List<AudioFocusInfo> getFocusStack() {
290         synchronized (mAudioFocusLock) {
291             final ArrayList<AudioFocusInfo> stack = new ArrayList<>(mFocusStack.size());
292             for (FocusRequester fr : mFocusStack) {
293                 stack.add(fr.toAudioFocusInfo());
294             }
295             return stack;
296         }
297     }
298 
299     /**
300      * Return the UID of the focus owner that has focus with exclusive focus gain
301      * @return -1 if nobody has exclusive focus, the UID of the owner otherwise
302      */
getExclusiveFocusOwnerUid()303     protected int getExclusiveFocusOwnerUid() {
304         synchronized (mAudioFocusLock) {
305             if (mFocusStack.empty()) {
306                 return -1;
307             }
308             final FocusRequester owner = mFocusStack.peek();
309             if (owner.getGainRequest() != AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE) {
310                 return -1;
311             }
312             return owner.getClientUid();
313         }
314     }
315 
316     /**
317      * Send AUDIOFOCUS_LOSS to a specific stack entry.
318      * Note this method is supporting an external API, and is restricted to LOSS in order to
319      * prevent allowing the stack to be in an invalid state (e.g. entry inside stack has focus)
320      * @param focusLoser the stack entry that is exiting the stack through a focus loss
321      * @return false if the focusLoser wasn't found in the stack, true otherwise
322      * @see AudioPolicy#sendFocusLoss(AudioFocusInfo)
323      */
sendFocusLoss(@onNull AudioFocusInfo focusLoser)324     boolean sendFocusLoss(@NonNull AudioFocusInfo focusLoser) {
325         synchronized (mAudioFocusLock) {
326             FocusRequester loserToRemove = null;
327             for (FocusRequester fr : mFocusStack) {
328                 if (fr.getClientId().equals(focusLoser.getClientId())) {
329                     fr.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null,
330                             false /*forceDuck*/);
331                     loserToRemove = fr;
332                     break;
333                 }
334             }
335             if (loserToRemove != null) {
336                 mFocusStack.remove(loserToRemove);
337                 loserToRemove.release();
338                 return true;
339             }
340         }
341         return false;
342     }
343 
344     @GuardedBy("mAudioFocusLock")
notifyTopOfAudioFocusStack()345     private void notifyTopOfAudioFocusStack() {
346         // notify the top of the stack it gained focus
347         if (!mFocusStack.empty()) {
348             if (canReassignAudioFocus()) {
349                 mFocusStack.peek().handleFocusGain(AudioManager.AUDIOFOCUS_GAIN);
350             }
351         }
352 
353         if (mMultiAudioFocusEnabled && !mMultiAudioFocusList.isEmpty()) {
354             for (FocusRequester multifr : mMultiAudioFocusList) {
355                 if (isLockedFocusOwner(multifr)) {
356                     multifr.handleFocusGain(AudioManager.AUDIOFOCUS_GAIN);
357                 }
358             }
359         }
360     }
361 
362     /**
363      * Focus is requested, propagate the associated loss throughout the stack.
364      * Will also remove entries in the stack that have just received a definitive loss of focus.
365      * @param focusGain the new focus gain that will later be added at the top of the stack
366      */
367     @GuardedBy("mAudioFocusLock")
propagateFocusLossFromGain_syncAf(int focusGain, final FocusRequester fr, boolean forceDuck)368     private void propagateFocusLossFromGain_syncAf(int focusGain, final FocusRequester fr,
369                                                    boolean forceDuck) {
370         if (DEBUG) {
371             Log.i(TAG, "propagateFocusLossFromGain_syncAf gain:" + focusGain);
372         }
373         final List<String> clientsToRemove = new LinkedList<String>();
374         // going through the audio focus stack to signal new focus, traversing order doesn't
375         // matter as all entries respond to the same external focus gain
376         if (!mFocusStack.empty()) {
377             for (FocusRequester focusLoser : mFocusStack) {
378                 if (DEBUG) {
379                     Log.i(TAG, "propagateFocusLossFromGain_syncAf checking client:"
380                             + focusLoser.getClientId());
381                 }
382                 final boolean isDefinitiveLoss =
383                         focusLoser.handleFocusLossFromGain(focusGain, fr, forceDuck);
384                 if (isDefinitiveLoss) {
385                     clientsToRemove.add(focusLoser.getClientId());
386                 }
387             }
388         } else if (DEBUG) {
389             Log.i(TAG, "propagateFocusLossFromGain_syncAf empty stack");
390         }
391 
392         if (mMultiAudioFocusEnabled && !mMultiAudioFocusList.isEmpty()) {
393             for (FocusRequester multifocusLoser : mMultiAudioFocusList) {
394                 final boolean isDefinitiveLoss =
395                         multifocusLoser.handleFocusLossFromGain(focusGain, fr, forceDuck);
396                 if (isDefinitiveLoss) {
397                     clientsToRemove.add(multifocusLoser.getClientId());
398                 }
399             }
400         }
401 
402         for (String clientToRemove : clientsToRemove) {
403             removeFocusStackEntry(clientToRemove, false /*signal*/,
404                     true /*notifyFocusFollowers*/);
405         }
406     }
407 
408     private final Stack<FocusRequester> mFocusStack = new Stack<FocusRequester>();
409 
410     ArrayList<FocusRequester> mMultiAudioFocusList = new ArrayList<FocusRequester>();
411 
412     /**
413      * Helper function:
414      * Display in the log the current entries in the audio focus stack
415      */
dumpFocusStack(PrintWriter pw)416     private void dumpFocusStack(PrintWriter pw) {
417         pw.println("\nAudio Focus stack entries (last is top of stack):");
418         synchronized(mAudioFocusLock) {
419             Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
420             while(stackIterator.hasNext()) {
421                 stackIterator.next().dump(pw);
422             }
423             pw.println("\n");
424             if (mFocusPolicy == null) {
425                 pw.println("No external focus policy\n");
426             } else {
427                 pw.println("External focus policy: "+ mFocusPolicy + ", focus owners:\n");
428                 dumpExtFocusPolicyFocusOwners(pw);
429             }
430         }
431         pw.println("\n");
432         pw.println(" Notify on duck:  " + mNotifyFocusOwnerOnDuck + "\n");
433         pw.println(" In ring or call: " + mRingOrCallActive + "\n");
434     }
435 
436     /**
437      * Remove a focus listener from the focus stack.
438      * @param clientToRemove the focus listener
439      * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
440      *   focus, notify the next item in the stack it gained focus.
441      */
442     @GuardedBy("mAudioFocusLock")
removeFocusStackEntry(String clientToRemove, boolean signal, boolean notifyFocusFollowers)443     private void removeFocusStackEntry(String clientToRemove, boolean signal,
444             boolean notifyFocusFollowers) {
445         if (DEBUG) {
446             Log.i(TAG, "removeFocusStackEntry client:" + clientToRemove);
447         }
448         AudioFocusInfo abandonSource = null;
449         // is the current top of the focus stack abandoning focus? (because of request, not death)
450         if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientToRemove))
451         {
452             //Log.i(TAG, "   removeFocusStackEntry() removing top of stack");
453             FocusRequester fr = mFocusStack.pop();
454             fr.maybeRelease();
455             if (notifyFocusFollowers) {
456                 abandonSource = fr.toAudioFocusInfo();
457             }
458             if (signal) {
459                 // notify the new top of the stack it gained focus
460                 notifyTopOfAudioFocusStack();
461             }
462         } else {
463             // focus is abandoned by a client that's not at the top of the stack,
464             // no need to update focus.
465             // (using an iterator on the stack so we can safely remove an entry after having
466             //  evaluated it, traversal order doesn't matter here)
467             Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
468             while(stackIterator.hasNext()) {
469                 FocusRequester fr = stackIterator.next();
470                 if(fr.hasSameClient(clientToRemove)) {
471                     Log.i(TAG, "AudioFocus  removeFocusStackEntry(): removing entry for "
472                             + clientToRemove);
473                     stackIterator.remove();
474                     if (notifyFocusFollowers) {
475                         abandonSource = fr.toAudioFocusInfo();
476                     }
477                     // stack entry not used anymore, clear references
478                     fr.maybeRelease();
479                 }
480             }
481         }
482         // focus followers still want to know focus was abandoned, handled as a loss
483         if (abandonSource != null) {
484             abandonSource.clearLossReceived();
485             notifyExtPolicyFocusLoss_syncAf(abandonSource, false);
486         }
487 
488         if (mMultiAudioFocusEnabled && !mMultiAudioFocusList.isEmpty()) {
489             Iterator<FocusRequester> listIterator = mMultiAudioFocusList.iterator();
490             while (listIterator.hasNext()) {
491                 FocusRequester fr = listIterator.next();
492                 if (fr.hasSameClient(clientToRemove)) {
493                     listIterator.remove();
494                     fr.release();
495                 }
496             }
497 
498             if (signal) {
499                 // notify the new top of the stack it gained focus
500                 notifyTopOfAudioFocusStack();
501             }
502         }
503     }
504 
505     /**
506      * Remove focus listeners from the focus stack for a particular client when it has died.
507      */
508     @GuardedBy("mAudioFocusLock")
removeFocusStackEntryOnDeath(IBinder cb)509     private void removeFocusStackEntryOnDeath(IBinder cb) {
510         // is the owner of the audio focus part of the client to remove?
511         boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
512                 mFocusStack.peek().hasSameBinder(cb);
513         // (using an iterator on the stack so we can safely remove an entry after having
514         //  evaluated it, traversal order doesn't matter here)
515         Iterator<FocusRequester> stackIterator = mFocusStack.iterator();
516         while(stackIterator.hasNext()) {
517             FocusRequester fr = stackIterator.next();
518             if(fr.hasSameBinder(cb)) {
519                 Log.i(TAG, "AudioFocus  removeFocusStackEntryOnDeath(): removing entry for " + cb);
520                 mEventLogger.enqueue(new EventLogger.StringEvent(
521                         "focus requester:" + fr.getClientId()
522                                 + " in uid:" + fr.getClientUid()
523                                 + " pack:" + fr.getPackageName()
524                                 + " died"));
525                 notifyExtPolicyFocusLoss_syncAf(fr.toAudioFocusInfo(), false);
526 
527                 stackIterator.remove();
528                 // stack entry not used anymore, clear references
529                 fr.release();
530             }
531         }
532         if (isTopOfStackForClientToRemove) {
533             // we removed an entry at the top of the stack:
534             //  notify the new top of the stack it gained focus.
535             notifyTopOfAudioFocusStack();
536         }
537     }
538 
539     /**
540      * Helper function for external focus policy:
541      * Remove focus listeners from the list of potential focus owners for a particular client when
542      * it has died.
543      */
544     @GuardedBy("mAudioFocusLock")
removeFocusEntryForExtPolicyOnDeath(IBinder cb)545     private void removeFocusEntryForExtPolicyOnDeath(IBinder cb) {
546         if (mFocusOwnersForFocusPolicy.isEmpty()) {
547             return;
548         }
549         boolean released = false;
550         final Set<Entry<String, FocusRequester>> owners = mFocusOwnersForFocusPolicy.entrySet();
551         final Iterator<Entry<String, FocusRequester>> ownerIterator = owners.iterator();
552         while (ownerIterator.hasNext()) {
553             final Entry<String, FocusRequester> owner = ownerIterator.next();
554             final FocusRequester fr = owner.getValue();
555             if (fr.hasSameBinder(cb)) {
556                 ownerIterator.remove();
557                 mEventLogger.enqueue(new EventLogger.StringEvent(
558                         "focus requester:" + fr.getClientId()
559                                 + " in uid:" + fr.getClientUid()
560                                 + " pack:" + fr.getPackageName()
561                                 + " died"));
562                 fr.release();
563                 notifyExtFocusPolicyFocusAbandon_syncAf(fr.toAudioFocusInfo());
564                 break;
565             }
566         }
567     }
568 
569     /**
570      * Helper function:
571      * Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
572      * The implementation guarantees that a state where focus cannot be immediately reassigned
573      * implies that an "locked" focus owner is at the top of the focus stack.
574      * Modifications to the implementation that break this assumption will cause focus requests to
575      * misbehave when honoring the AudioManager.AUDIOFOCUS_FLAG_DELAY_OK flag.
576      */
canReassignAudioFocus()577     private boolean canReassignAudioFocus() {
578         // focus requests are rejected during a phone call or when the phone is ringing
579         // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus
580         if (!mFocusStack.isEmpty() && isLockedFocusOwner(mFocusStack.peek())) {
581             return false;
582         }
583         return true;
584     }
585 
isLockedFocusOwner(FocusRequester fr)586     private boolean isLockedFocusOwner(FocusRequester fr) {
587         return (fr.hasSameClient(AudioSystem.IN_VOICE_COMM_FOCUS_ID) || fr.isLockedFocusOwner());
588     }
589 
590     /**
591      * Helper function
592      * Pre-conditions: focus stack is not empty, there is one or more locked focus owner
593      *                 at the top of the focus stack
594      * Push the focus requester onto the audio focus stack at the first position immediately
595      * following the locked focus owners.
596      * Propagate through the stack the changes that the new (future) focus owner causes.
597      * @param nfr the future focus owner that will gain focus when the locked focus owners are
598      *            removed from the stack
599      * @return {@link AudioManager#AUDIOFOCUS_REQUEST_GRANTED} or
600      *     {@link AudioManager#AUDIOFOCUS_REQUEST_DELAYED}
601      */
602     @GuardedBy("mAudioFocusLock")
pushBelowLockedFocusOwnersAndPropagate(FocusRequester nfr)603     private int pushBelowLockedFocusOwnersAndPropagate(FocusRequester nfr) {
604         if (DEBUG) {
605             Log.v(TAG, "pushBelowLockedFocusOwnersAndPropagate client=" + nfr.getClientId());
606         }
607         int lastLockedFocusOwnerIndex = mFocusStack.size();
608         for (int index = mFocusStack.size() - 1; index >= 0; index--) {
609             if (isLockedFocusOwner(mFocusStack.elementAt(index))) {
610                 lastLockedFocusOwnerIndex = index;
611             }
612         }
613         if (lastLockedFocusOwnerIndex == mFocusStack.size()) {
614             // this should not happen, but handle it and log an error
615             Log.e(TAG, "No exclusive focus owner found in propagateFocusLossFromGain_syncAf()",
616                     new Exception());
617             // no exclusive owner, push at top of stack, focus is granted, propagate change
618             propagateFocusLossFromGain_syncAf(nfr.getGainRequest(), nfr, false /*forceDuck*/);
619             mFocusStack.push(nfr);
620             return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
621         }
622 
623         if (DEBUG) {
624             Log.v(TAG, "> lastLockedFocusOwnerIndex=" + lastLockedFocusOwnerIndex);
625         }
626         mFocusStack.insertElementAt(nfr, lastLockedFocusOwnerIndex);
627 
628         // propagate potential focus loss (and removal from stack) after the newly
629         // inserted FocusRequester (at index lastLockedFocusOwnerIndex-1)
630         final List<String> clientsToRemove = new LinkedList<String>();
631         for (int index = lastLockedFocusOwnerIndex - 1; index >= 0; index--) {
632             final boolean isDefinitiveLoss =
633                     mFocusStack.elementAt(index).handleFocusLossFromGain(
634                             nfr.getGainRequest(), nfr, false /*forceDuck*/);
635             if (isDefinitiveLoss) {
636                 clientsToRemove.add(mFocusStack.elementAt(index).getClientId());
637             }
638         }
639         for (String clientToRemove : clientsToRemove) {
640             if (DEBUG) {
641                 Log.v(TAG, "> removing focus client " + clientToRemove);
642             }
643             removeFocusStackEntry(clientToRemove, false /*signal*/,
644                     true /*notifyFocusFollowers*/);
645         }
646 
647         return AudioManager.AUDIOFOCUS_REQUEST_DELAYED;
648     }
649 
650     /**
651      * Inner class to monitor audio focus client deaths, and remove them from the audio focus
652      * stack if necessary.
653      */
654     protected class AudioFocusDeathHandler implements IBinder.DeathRecipient {
655         private IBinder mCb; // To be notified of client's death
656 
AudioFocusDeathHandler(IBinder cb)657         AudioFocusDeathHandler(IBinder cb) {
658             mCb = cb;
659         }
660 
binderDied()661         public void binderDied() {
662             synchronized(mAudioFocusLock) {
663                 if (mFocusPolicy != null) {
664                     removeFocusEntryForExtPolicyOnDeath(mCb);
665                 } else {
666                     removeFocusStackEntryOnDeath(mCb);
667                     if (mMultiAudioFocusEnabled && !mMultiAudioFocusList.isEmpty()) {
668                         Iterator<FocusRequester> listIterator = mMultiAudioFocusList.iterator();
669                         while (listIterator.hasNext()) {
670                             FocusRequester fr = listIterator.next();
671                             if (fr.hasSameBinder(mCb)) {
672                                 listIterator.remove();
673                                 fr.release();
674                             }
675                         }
676                     }
677                 }
678             }
679         }
680     }
681 
682     /**
683      * Indicates whether to notify an audio focus owner when it loses focus
684      * with {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK} if it will only duck.
685      * This variable being false indicates an AudioPolicy has been registered and has signaled
686      * it will handle audio ducking.
687      */
688     private boolean mNotifyFocusOwnerOnDuck = true;
689 
setDuckingInExtPolicyAvailable(boolean available)690     protected void setDuckingInExtPolicyAvailable(boolean available) {
691         mNotifyFocusOwnerOnDuck = !available;
692     }
693 
mustNotifyFocusOwnerOnDuck()694     boolean mustNotifyFocusOwnerOnDuck() { return mNotifyFocusOwnerOnDuck; }
695 
696     private ArrayList<IAudioPolicyCallback> mFocusFollowers = new ArrayList<IAudioPolicyCallback>();
697 
addFocusFollower(IAudioPolicyCallback ff)698     void addFocusFollower(IAudioPolicyCallback ff) {
699         if (ff == null) {
700             return;
701         }
702         synchronized(mAudioFocusLock) {
703             boolean found = false;
704             for (IAudioPolicyCallback pcb : mFocusFollowers) {
705                 if (pcb.asBinder().equals(ff.asBinder())) {
706                     found = true;
707                     break;
708                 }
709             }
710             if (found) {
711                 return;
712             } else {
713                 mFocusFollowers.add(ff);
714                 notifyExtPolicyCurrentFocusAsync(ff);
715             }
716         }
717     }
718 
removeFocusFollower(IAudioPolicyCallback ff)719     void removeFocusFollower(IAudioPolicyCallback ff) {
720         if (ff == null) {
721             return;
722         }
723         synchronized(mAudioFocusLock) {
724             for (IAudioPolicyCallback pcb : mFocusFollowers) {
725                 if (pcb.asBinder().equals(ff.asBinder())) {
726                     mFocusFollowers.remove(pcb);
727                     break;
728                 }
729             }
730         }
731     }
732 
733     /** The current audio focus policy */
734     @GuardedBy("mAudioFocusLock")
735     @Nullable private IAudioPolicyCallback mFocusPolicy = null;
736     /**
737      * The audio focus policy that was registered before a test focus policy was registered
738      * during a test
739      */
740     @GuardedBy("mAudioFocusLock")
741     @Nullable private IAudioPolicyCallback mPreviousFocusPolicy = null;
742 
743     // Since we don't have a stack of focus owners when using an external focus policy, we keep
744     // track of all the focus requesters in this map, with their clientId as the key. This is
745     // used both for focus dispatch and death handling
746     private HashMap<String, FocusRequester> mFocusOwnersForFocusPolicy =
747             new HashMap<String, FocusRequester>();
748 
setFocusPolicy(IAudioPolicyCallback policy, boolean isTestFocusPolicy)749     void setFocusPolicy(IAudioPolicyCallback policy, boolean isTestFocusPolicy) {
750         if (policy == null) {
751             return;
752         }
753         synchronized (mAudioFocusLock) {
754             if (isTestFocusPolicy) {
755                 mPreviousFocusPolicy = mFocusPolicy;
756             }
757             mFocusPolicy = policy;
758         }
759     }
760 
unsetFocusPolicy(IAudioPolicyCallback policy, boolean isTestFocusPolicy)761     void unsetFocusPolicy(IAudioPolicyCallback policy, boolean isTestFocusPolicy) {
762         if (policy == null) {
763             return;
764         }
765         synchronized (mAudioFocusLock) {
766             if (mFocusPolicy == policy) {
767                 if (isTestFocusPolicy) {
768                     // restore the focus policy that was there before the focus policy test started
769                     mFocusPolicy = mPreviousFocusPolicy;
770                 } else {
771                     mFocusPolicy = null;
772                 }
773             }
774         }
775     }
776 
777     /**
778      * @param pcb non null
779      */
notifyExtPolicyCurrentFocusAsync(IAudioPolicyCallback pcb)780     void notifyExtPolicyCurrentFocusAsync(IAudioPolicyCallback pcb) {
781         final IAudioPolicyCallback pcb2 = pcb;
782         final Thread thread = new Thread() {
783             @Override
784             public void run() {
785                 synchronized(mAudioFocusLock) {
786                     if (mFocusStack.isEmpty()) {
787                         return;
788                     }
789                     try {
790                         pcb2.notifyAudioFocusGrant(mFocusStack.peek().toAudioFocusInfo(),
791                                 // top of focus stack always has focus
792                                 AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
793                     } catch (RemoteException e) {
794                         Log.e(TAG, "Can't call notifyAudioFocusGrant() on IAudioPolicyCallback "
795                                 + pcb2.asBinder(), e);
796                     }
797                 }
798             }
799         };
800         thread.start();
801     }
802 
803     /**
804      * Called synchronized on mAudioFocusLock
805      */
notifyExtPolicyFocusGrant_syncAf(AudioFocusInfo afi, int requestResult)806     void notifyExtPolicyFocusGrant_syncAf(AudioFocusInfo afi, int requestResult) {
807         for (IAudioPolicyCallback pcb : mFocusFollowers) {
808             try {
809                 // oneway
810                 pcb.notifyAudioFocusGrant(afi, requestResult);
811             } catch (RemoteException e) {
812                 Log.e(TAG, "Can't call notifyAudioFocusGrant() on IAudioPolicyCallback "
813                         + pcb.asBinder(), e);
814             }
815         }
816     }
817 
818     /**
819      * Called synchronized on mAudioFocusLock
820      */
notifyExtPolicyFocusLoss_syncAf(AudioFocusInfo afi, boolean wasDispatched)821     void notifyExtPolicyFocusLoss_syncAf(AudioFocusInfo afi, boolean wasDispatched) {
822         for (IAudioPolicyCallback pcb : mFocusFollowers) {
823             try {
824                 // oneway
825                 pcb.notifyAudioFocusLoss(afi, wasDispatched);
826             } catch (RemoteException e) {
827                 Log.e(TAG, "Can't call notifyAudioFocusLoss() on IAudioPolicyCallback "
828                         + pcb.asBinder(), e);
829             }
830         }
831     }
832 
833     /**
834      * Called synchronized on mAudioFocusLock.
835      * Can only be called with an external focus policy installed (mFocusPolicy != null)
836      * @param afi
837      * @param fd
838      * @param cb binder of the focus requester
839      * @return true if the external audio focus policy (if any) can handle the focus request,
840      *     and false if there was any error handling the request (e.g. error talking to policy,
841      *     focus requester is already dead)
842      */
notifyExtFocusPolicyFocusRequest_syncAf(AudioFocusInfo afi, IAudioFocusDispatcher fd, @NonNull IBinder cb)843     boolean notifyExtFocusPolicyFocusRequest_syncAf(AudioFocusInfo afi,
844             IAudioFocusDispatcher fd, @NonNull IBinder cb) {
845         if (DEBUG) {
846             Log.v(TAG, "notifyExtFocusPolicyFocusRequest client="+afi.getClientId()
847             + " dispatcher=" + fd);
848         }
849         synchronized (mExtFocusChangeLock) {
850             afi.setGen(mExtFocusChangeCounter++);
851         }
852         final FocusRequester existingFr = mFocusOwnersForFocusPolicy.get(afi.getClientId());
853         boolean keepTrack = false;
854         if (existingFr != null) {
855             if (!existingFr.hasSameDispatcher(fd)) {
856                 existingFr.release();
857                 keepTrack = true;
858             }
859         } else {
860             keepTrack = true;
861         }
862         if (keepTrack) {
863             final AudioFocusDeathHandler hdlr = new AudioFocusDeathHandler(cb);
864             try {
865                 cb.linkToDeath(hdlr, 0);
866             } catch (RemoteException e) {
867                 // client has already died!
868                 return false;
869             }
870             // new focus (future) focus owner to keep track of
871             mFocusOwnersForFocusPolicy.put(afi.getClientId(),
872                     new FocusRequester(afi, fd, cb, hdlr, this));
873         }
874 
875         try {
876             //oneway
877             mFocusPolicy.notifyAudioFocusRequest(afi, AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
878             return true;
879         } catch (RemoteException e) {
880             Log.e(TAG, "Can't call notifyAudioFocusRequest() on IAudioPolicyCallback "
881                     + mFocusPolicy.asBinder(), e);
882         }
883         return false;
884     }
885 
setFocusRequestResultFromExtPolicy(AudioFocusInfo afi, int requestResult)886     void setFocusRequestResultFromExtPolicy(AudioFocusInfo afi, int requestResult) {
887         synchronized (mExtFocusChangeLock) {
888             if (afi.getGen() > mExtFocusChangeCounter) {
889                 return;
890             }
891         }
892         synchronized (mAudioFocusLock) {
893             FocusRequester fr = getFocusRequesterLocked(afi.getClientId(),
894                     /* shouldRemove= */ requestResult == AudioManager.AUDIOFOCUS_REQUEST_FAILED);
895             if (fr != null) {
896                 fr.dispatchFocusResultFromExtPolicy(requestResult);
897                 // if fade is enabled for external focus policies, apply it when setting
898                 // focus result as well
899                 if (enableFadeManagerConfiguration()) {
900                     fr.handleFocusGainFromRequest(requestResult);
901                 }
902             }
903         }
904     }
905 
906     /**
907      * Called synchronized on mAudioFocusLock
908      * @param afi
909      * @return true if the external audio focus policy (if any) is handling the focus request
910      */
notifyExtFocusPolicyFocusAbandon_syncAf(AudioFocusInfo afi)911     boolean notifyExtFocusPolicyFocusAbandon_syncAf(AudioFocusInfo afi) {
912         if (mFocusPolicy == null) {
913             return false;
914         }
915         final FocusRequester fr = mFocusOwnersForFocusPolicy.remove(afi.getClientId());
916         if (fr != null) {
917             fr.release();
918         }
919         try {
920             //oneway
921             mFocusPolicy.notifyAudioFocusAbandon(afi);
922         } catch (RemoteException e) {
923             Log.e(TAG, "Can't call notifyAudioFocusAbandon() on IAudioPolicyCallback "
924                     + mFocusPolicy.asBinder(), e);
925         }
926         return true;
927     }
928 
929     /** see AudioManager.dispatchFocusChange(AudioFocusInfo afi, int focusChange, AudioPolicy ap) */
dispatchFocusChange(AudioFocusInfo afi, int focusChange)930     int dispatchFocusChange(AudioFocusInfo afi, int focusChange) {
931         if (DEBUG) {
932             Log.v(TAG, "dispatchFocusChange " + focusChange + " to afi client="
933                     + afi.getClientId());
934         }
935         synchronized (mAudioFocusLock) {
936             FocusRequester fr = getFocusRequesterLocked(afi.getClientId(),
937                     /* shouldRemove= */ focusChange == AudioManager.AUDIOFOCUS_LOSS);
938             if (fr == null) {
939                 if (DEBUG) {
940                     Log.v(TAG, "> failed: no such focus requester known");
941                 }
942                 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
943             }
944             return fr.dispatchFocusChange(focusChange);
945         }
946     }
947 
dispatchFocusChangeWithFade(AudioFocusInfo afi, int focusChange, List<AudioFocusInfo> otherActiveAfis)948     int dispatchFocusChangeWithFade(AudioFocusInfo afi, int focusChange,
949             List<AudioFocusInfo> otherActiveAfis) {
950         if (DEBUG) {
951             Log.v(TAG, "dispatchFocusChangeWithFade " + AudioManager.audioFocusToString(focusChange)
952                     + " to afi client=" + afi.getClientId()
953                     + " other active afis=" + otherActiveAfis);
954         }
955 
956         synchronized (mAudioFocusLock) {
957             String clientId = afi.getClientId();
958             // do not remove the entry since it can be posted for fade
959             FocusRequester fr = getFocusRequesterLocked(clientId, /* shouldRemove= */ false);
960             if (fr == null) {
961                 if (DEBUG) {
962                     Log.v(TAG, "> failed: no such focus requester known");
963                 }
964                 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
965             }
966 
967             // convert other AudioFocusInfo to corresponding FocusRequester
968             ArrayList<FocusRequester> otherActiveFrs = new ArrayList<>();
969             for (int index = 0; index < otherActiveAfis.size(); index++) {
970                 FocusRequester otherFr = getFocusRequesterLocked(
971                         otherActiveAfis.get(index).getClientId(), /* shouldRemove= */ false);
972                 if (otherFr == null) {
973                     continue;
974                 }
975                 otherActiveFrs.add(otherFr);
976             }
977 
978             int status = fr.dispatchFocusChangeWithFadeLocked(focusChange, otherActiveFrs);
979             if (status != AudioManager.AUDIOFOCUS_REQUEST_DELAYED
980                     && focusChange == AudioManager.AUDIOFOCUS_LOSS) {
981                 mFocusOwnersForFocusPolicy.remove(clientId);
982             }
983 
984             return status;
985         }
986     }
987 
988     @GuardedBy("mAudioFocusLock")
getFocusRequesterLocked(String clientId, boolean shouldRemove)989     private FocusRequester getFocusRequesterLocked(String clientId, boolean shouldRemove) {
990         if (mFocusPolicy == null) {
991             if (DEBUG) {
992                 Log.v(TAG, "> failed: no focus policy");
993             }
994             return null;
995         }
996 
997         FocusRequester fr;
998         if (shouldRemove) {
999             fr = mFocusOwnersForFocusPolicy.remove(clientId);
1000         } else {
1001             fr = mFocusOwnersForFocusPolicy.get(clientId);
1002         }
1003 
1004         if (fr == null && DEBUG) {
1005             Log.v(TAG, "> failed: no such focus requester known");
1006         }
1007         return fr;
1008     }
1009 
dumpExtFocusPolicyFocusOwners(PrintWriter pw)1010     private void dumpExtFocusPolicyFocusOwners(PrintWriter pw) {
1011         final Set<Entry<String, FocusRequester>> owners = mFocusOwnersForFocusPolicy.entrySet();
1012         final Iterator<Entry<String, FocusRequester>> ownerIterator = owners.iterator();
1013         while (ownerIterator.hasNext()) {
1014             final Entry<String, FocusRequester> owner = ownerIterator.next();
1015             final FocusRequester fr = owner.getValue();
1016             fr.dump(pw);
1017         }
1018     }
1019 
getCurrentAudioFocus()1020     protected int getCurrentAudioFocus() {
1021         synchronized(mAudioFocusLock) {
1022             if (mFocusStack.empty()) {
1023                 return AudioManager.AUDIOFOCUS_NONE;
1024             } else {
1025                 return mFocusStack.peek().getGainRequest();
1026             }
1027         }
1028     }
1029 
1030     /**
1031      * Delay after entering ringing or call mode after which the framework will mute streams
1032      * that are still playing.
1033      */
1034     private static final int RING_CALL_MUTING_ENFORCEMENT_DELAY_MS = 100;
1035 
1036     /**
1037      * Usages to mute when the device rings or is in a call
1038      */
1039     private final static int[] USAGES_TO_MUTE_IN_RING_OR_CALL =
1040         { AudioAttributes.USAGE_MEDIA, AudioAttributes.USAGE_GAME };
1041 
1042     /**
1043      * Return the volume ramp time expected before playback with the given AudioAttributes would
1044      * start after gaining audio focus.
1045      * @param attr attributes of the sound about to start playing
1046      * @return time in ms
1047      */
getFocusRampTimeMs(int focusGain, AudioAttributes attr)1048     protected static int getFocusRampTimeMs(int focusGain, AudioAttributes attr) {
1049         switch (attr.getUsage()) {
1050             case AudioAttributes.USAGE_MEDIA:
1051             case AudioAttributes.USAGE_GAME:
1052                 return 1000;
1053             case AudioAttributes.USAGE_ALARM:
1054             case AudioAttributes.USAGE_NOTIFICATION_RINGTONE:
1055             case AudioAttributes.USAGE_ASSISTANT:
1056             case AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY:
1057             case AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
1058             case AudioAttributes.USAGE_ANNOUNCEMENT:
1059                 return 700;
1060             case AudioAttributes.USAGE_VOICE_COMMUNICATION:
1061             case AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING:
1062             case AudioAttributes.USAGE_NOTIFICATION:
1063             case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
1064             case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
1065             case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
1066             case AudioAttributes.USAGE_NOTIFICATION_EVENT:
1067             case AudioAttributes.USAGE_ASSISTANCE_SONIFICATION:
1068             case AudioAttributes.USAGE_VEHICLE_STATUS:
1069                 return 500;
1070             case AudioAttributes.USAGE_EMERGENCY:
1071             case AudioAttributes.USAGE_SAFETY:
1072             case AudioAttributes.USAGE_UNKNOWN:
1073             default:
1074                 return 0;
1075         }
1076     }
1077 
1078     /** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int, int)
1079      * @param aa
1080      * @param focusChangeHint
1081      * @param cb
1082      * @param fd
1083      * @param clientId
1084      * @param callingPackageName
1085      * @param flags
1086      * @param sdk
1087      * @param forceDuck only true if
1088      *     {@link android.media.AudioFocusRequest.Builder#setFocusGain(int)} was set to true for
1089      *                  accessibility.
1090      * @param testUid ignored if flags doesn't contain AudioManager.AUDIOFOCUS_FLAG_TEST
1091      *                otherwise the UID being injected for testing
1092      * @param permissionOverridesCheck true if permission checks guaranteed that the call should
1093      *                                 go through, false otherwise (e.g. non-privileged caller)
1094      * @return
1095      */
requestAudioFocus(@onNull AudioAttributes aa, int focusChangeHint, IBinder cb, IAudioFocusDispatcher fd, @NonNull String clientId, @NonNull String callingPackageName, int flags, int sdk, boolean forceDuck, int testUid, boolean permissionOverridesCheck)1096     protected int requestAudioFocus(@NonNull AudioAttributes aa, int focusChangeHint, IBinder cb,
1097             IAudioFocusDispatcher fd, @NonNull String clientId, @NonNull String callingPackageName,
1098             int flags, int sdk, boolean forceDuck, int testUid,
1099             boolean permissionOverridesCheck) {
1100         new MediaMetrics.Item(mMetricsId)
1101                 .setUid(Binder.getCallingUid())
1102                 .set(MediaMetrics.Property.CALLING_PACKAGE, callingPackageName)
1103                 .set(MediaMetrics.Property.CLIENT_NAME, clientId)
1104                 .set(MediaMetrics.Property.EVENT, "requestAudioFocus")
1105                 .set(MediaMetrics.Property.FLAGS, flags)
1106                 .set(MediaMetrics.Property.FOCUS_CHANGE_HINT,
1107                         AudioManager.audioFocusToString(focusChangeHint))
1108                 //.set(MediaMetrics.Property.SDK, sdk)
1109                 .record();
1110 
1111         // when using the test API, a fake UID can be injected (testUid is ignored otherwise)
1112         // note that the test on flags is not a mask test on purpose, AUDIOFOCUS_FLAG_TEST is
1113         // supposed to be alone in bitfield
1114         final int uid = (flags == AudioManager.AUDIOFOCUS_FLAG_TEST)
1115                 ? testUid : Binder.getCallingUid();
1116         mEventLogger.enqueue((new EventLogger.StringEvent(
1117                 "requestAudioFocus() from uid/pid " + uid
1118                     + "/" + Binder.getCallingPid()
1119                     + " AA=" + aa.usageToString() + "/" + aa.contentTypeToString()
1120                     + " clientId=" + clientId + " callingPack=" + callingPackageName
1121                     + " req=" + focusChangeHint
1122                     + " flags=0x" + Integer.toHexString(flags)
1123                     + " sdk=" + sdk))
1124                 .printLog(TAG));
1125         // we need a valid binder callback for clients
1126         if (!cb.pingBinder()) {
1127             Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
1128             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
1129         }
1130 
1131         synchronized(mAudioFocusLock) {
1132             // check whether a focus freeze is in place and filter
1133             if (isFocusFrozenForTest()) {
1134                 int focusRequesterUid;
1135                 if ((flags & AudioManager.AUDIOFOCUS_FLAG_TEST)
1136                         == AudioManager.AUDIOFOCUS_FLAG_TEST) {
1137                     focusRequesterUid = testUid;
1138                 } else {
1139                     focusRequesterUid = Binder.getCallingUid();
1140                 }
1141                 if (isFocusFrozenForTestForUid(focusRequesterUid)) {
1142                     Log.i(TAG, "requestAudioFocus: focus frozen for test for uid:"
1143                             + focusRequesterUid);
1144                     return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
1145                 }
1146                 Log.i(TAG, "requestAudioFocus: focus frozen for test but uid:" + focusRequesterUid
1147                         + " is exempt");
1148             }
1149 
1150             if (mFocusStack.size() > MAX_STACK_SIZE) {
1151                 Log.e(TAG, "Max AudioFocus stack size reached, failing requestAudioFocus()");
1152                 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
1153             }
1154 
1155             boolean enteringRingOrCall = !mRingOrCallActive
1156                     & (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0);
1157             if (enteringRingOrCall) { mRingOrCallActive = true; }
1158 
1159             final AudioFocusInfo afiForExtPolicy;
1160             if (mFocusPolicy != null) {
1161                 // construct AudioFocusInfo as it will be communicated to audio focus policy
1162                 afiForExtPolicy = new AudioFocusInfo(aa, uid,
1163                         clientId, callingPackageName, focusChangeHint, 0 /*lossReceived*/,
1164                         flags, sdk);
1165             } else {
1166                 afiForExtPolicy = null;
1167             }
1168 
1169             // handle delayed focus
1170             boolean focusGrantDelayed = false;
1171             if (!canReassignAudioFocus()) {
1172                 if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) == 0) {
1173                     return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
1174                 } else {
1175                     // request has AUDIOFOCUS_FLAG_DELAY_OK: focus can't be
1176                     // granted right now, so the requester will be inserted in the focus stack
1177                     // to receive focus later
1178                     focusGrantDelayed = true;
1179                 }
1180             }
1181 
1182             // external focus policy?
1183             if (mFocusPolicy != null) {
1184                 if (notifyExtFocusPolicyFocusRequest_syncAf(afiForExtPolicy, fd, cb)) {
1185                     // stop handling focus request here as it is handled by external audio
1186                     // focus policy (return code will be handled in AudioManager)
1187                     return AudioManager.AUDIOFOCUS_REQUEST_WAITING_FOR_EXT_POLICY;
1188                 } else {
1189                     // an error occured, client already dead, bail early
1190                     return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
1191                 }
1192             }
1193 
1194             // handle the potential premature death of the new holder of the focus
1195             // (premature death == death before abandoning focus)
1196             // Register for client death notification
1197             AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
1198 
1199             try {
1200                 cb.linkToDeath(afdh, 0);
1201             } catch (RemoteException e) {
1202                 // client has already died!
1203                 Log.w(TAG, "AudioFocus  requestAudioFocus() could not link to "+cb+" binder death");
1204                 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
1205             }
1206 
1207             if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientId)) {
1208                 // if focus is already owned by this client and the reason for acquiring the focus
1209                 // hasn't changed, don't do anything
1210                 final FocusRequester fr = mFocusStack.peek();
1211                 if (fr.getGainRequest() == focusChangeHint && fr.getGrantFlags() == flags) {
1212                     // unlink death handler so it can be gc'ed.
1213                     // linkToDeath() creates a JNI global reference preventing collection.
1214                     cb.unlinkToDeath(afdh, 0);
1215                     notifyExtPolicyFocusGrant_syncAf(fr.toAudioFocusInfo(),
1216                             AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
1217                     return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
1218                 }
1219                 // the reason for the audio focus request has changed: remove the current top of
1220                 // stack and respond as if we had a new focus owner
1221                 if (!focusGrantDelayed) {
1222                     mFocusStack.pop();
1223                     // the entry that was "popped" is the same that was "peeked" above
1224                     fr.release();
1225                 }
1226             }
1227 
1228             // focus requester might already be somewhere below in the stack, remove it
1229             removeFocusStackEntry(clientId, false /* signal */, false /*notifyFocusFollowers*/);
1230 
1231             final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb,
1232                     clientId, afdh, callingPackageName, uid, this, sdk);
1233 
1234             if (mMultiAudioFocusEnabled
1235                     && (focusChangeHint == AudioManager.AUDIOFOCUS_GAIN)) {
1236                 if (enteringRingOrCall) {
1237                     if (!mMultiAudioFocusList.isEmpty()) {
1238                         for (FocusRequester multifr : mMultiAudioFocusList) {
1239                             multifr.handleFocusLossFromGain(focusChangeHint, nfr, forceDuck);
1240                         }
1241                     }
1242                 } else {
1243                     boolean needAdd = true;
1244                     if (!mMultiAudioFocusList.isEmpty()) {
1245                         for (FocusRequester multifr : mMultiAudioFocusList) {
1246                             if (multifr.getClientUid() == Binder.getCallingUid()) {
1247                                 needAdd = false;
1248                                 break;
1249                             }
1250                         }
1251                     }
1252                     if (needAdd) {
1253                         mMultiAudioFocusList.add(nfr);
1254                     }
1255                     nfr.handleFocusGainFromRequest(AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
1256                     notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(),
1257                             AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
1258                     return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
1259                 }
1260             }
1261 
1262             if (focusGrantDelayed) {
1263                 // focusGrantDelayed being true implies we can't reassign focus right now
1264                 // which implies the focus stack is not empty.
1265                 final int requestResult = pushBelowLockedFocusOwnersAndPropagate(nfr);
1266                 if (requestResult != AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
1267                     notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(), requestResult);
1268                 }
1269                 return requestResult;
1270             } else {
1271                 // propagate the focus change through the stack
1272                 propagateFocusLossFromGain_syncAf(focusChangeHint, nfr, forceDuck);
1273 
1274                 // push focus requester at the top of the audio focus stack
1275                 mFocusStack.push(nfr);
1276                 nfr.handleFocusGainFromRequest(AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
1277             }
1278             notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(),
1279                     AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
1280 
1281             if (ENFORCE_MUTING_FOR_RING_OR_CALL & enteringRingOrCall) {
1282                 runAudioCheckerForRingOrCallAsync(true/*enteringRingOrCall*/);
1283             }
1284         }//synchronized(mAudioFocusLock)
1285 
1286         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
1287     }
1288 
1289     /**
1290      * @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener, AudioAttributes)
1291      * */
abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa, String callingPackageName)1292     protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa,
1293             String callingPackageName) {
1294         new MediaMetrics.Item(mMetricsId)
1295                 .setUid(Binder.getCallingUid())
1296                 .set(MediaMetrics.Property.CALLING_PACKAGE, callingPackageName)
1297                 .set(MediaMetrics.Property.CLIENT_NAME, clientId)
1298                 .set(MediaMetrics.Property.EVENT, "abandonAudioFocus")
1299                 .record();
1300 
1301         // AudioAttributes are currently ignored, to be used for zones / a11y
1302         mEventLogger.enqueue((new EventLogger.StringEvent(
1303                 "abandonAudioFocus() from uid/pid " + Binder.getCallingUid()
1304                     + "/" + Binder.getCallingPid()
1305                     + " clientId=" + clientId))
1306                 .printLog(TAG));
1307         try {
1308             // this will take care of notifying the new focus owner if needed
1309             synchronized(mAudioFocusLock) {
1310                 // external focus policy?
1311                 if (mFocusPolicy != null) {
1312                     final AudioFocusInfo afi = new AudioFocusInfo(aa, Binder.getCallingUid(),
1313                             clientId, callingPackageName, 0 /*gainRequest*/, 0 /*lossReceived*/,
1314                             0 /*flags*/, 0 /* sdk n/a here*/);
1315                     if (notifyExtFocusPolicyFocusAbandon_syncAf(afi)) {
1316                         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
1317                     }
1318                 }
1319 
1320                 boolean exitingRingOrCall = mRingOrCallActive
1321                         & (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0);
1322                 if (exitingRingOrCall) { mRingOrCallActive = false; }
1323 
1324                 removeFocusStackEntry(clientId, true /*signal*/, true /*notifyFocusFollowers*/);
1325 
1326                 if (ENFORCE_MUTING_FOR_RING_OR_CALL & exitingRingOrCall) {
1327                     runAudioCheckerForRingOrCallAsync(false/*enteringRingOrCall*/);
1328                 }
1329             }
1330         } catch (java.util.ConcurrentModificationException cme) {
1331             // Catching this exception here is temporary. It is here just to prevent
1332             // a crash seen when the "Silent" notification is played. This is believed to be fixed
1333             // but this try catch block is left just to be safe.
1334             Log.e(TAG, "FATAL EXCEPTION AudioFocus  abandonAudioFocus() caused " + cme);
1335             cme.printStackTrace();
1336         }
1337 
1338         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
1339     }
1340 
1341     /**
1342      * Reference to the caller of {@link #enterAudioFocusFreezeForTest(IBinder, int[])}
1343      * Will be null when there is no focus freeze for test
1344      */
1345     @GuardedBy("mAudioFocusLock")
1346     @Nullable
1347     private IBinder mFocusFreezerForTest = null;
1348 
1349     /**
1350      * The death handler for {@link #mFocusFreezerForTest}
1351      * Will be null when there is no focus freeze for test
1352      */
1353     @GuardedBy("mAudioFocusLock")
1354     @Nullable
1355     private IBinder.DeathRecipient mFocusFreezerDeathHandler = null;
1356 
1357     /**
1358      *  Array of UIDs exempt from focus freeze when focus is frozen for test, null during normal
1359      *  operations.
1360      *  Will be null when there is no focus freeze for test
1361      */
1362     @GuardedBy("mAudioFocusLock")
1363     @Nullable
1364     private int[] mFocusFreezeExemptUids = null;
1365 
1366     @GuardedBy("mAudioFocusLock")
isFocusFrozenForTest()1367     private boolean isFocusFrozenForTest() {
1368         return (mFocusFreezerForTest != null);
1369     }
1370 
1371     /**
1372      * Checks if the given UID can request focus when a focus freeze is in place for a test.
1373      * Focus can be requested if focus is not frozen or if it's frozen but the UID is exempt.
1374      * @param uidToCheck
1375      * @return true if that UID is barred from requesting focus, false if its focus request
1376      *     can proceed being processed
1377      */
1378     @GuardedBy("mAudioFocusLock")
isFocusFrozenForTestForUid(int uidToCheck)1379     private boolean isFocusFrozenForTestForUid(int uidToCheck) {
1380         if (isFocusFrozenForTest()) {
1381             return false;
1382         }
1383         // check the list of exempts (array is not null because we're in a freeze for test
1384         for (int uid : mFocusFreezeExemptUids) {
1385             if (uid == uidToCheck) {
1386                 return false;
1387             }
1388         }
1389         // uid was not found in the exempt list, its focus request is denied
1390         return true;
1391     }
1392 
enterAudioFocusFreezeForTest( @onNull IBinder cb, @NonNull int[] exemptedUids)1393     protected boolean enterAudioFocusFreezeForTest(
1394             @NonNull IBinder cb, @NonNull int[] exemptedUids) {
1395         Log.i(TAG, "enterAudioFocusFreezeForTest UIDs exempt:" + Arrays.toString(exemptedUids));
1396         synchronized (mAudioFocusLock) {
1397             if (mFocusFreezerForTest != null) {
1398                 Log.e(TAG, "Error enterAudioFocusFreezeForTest: focus already frozen");
1399                 return false;
1400             }
1401             // new focus freeze, register death handler
1402             try {
1403                 mFocusFreezerDeathHandler = new IBinder.DeathRecipient() {
1404                     @Override
1405                     public void binderDied() {
1406                         Log.i(TAG, "Audio focus freezer died, exiting focus freeze for test");
1407                         releaseFocusFreeze();
1408                     }
1409                 };
1410                 cb.linkToDeath(mFocusFreezerDeathHandler, 0);
1411                 mFocusFreezerForTest = cb;
1412                 mFocusFreezeExemptUids = exemptedUids.clone();
1413             } catch (RemoteException e) {
1414                 // client has already died!
1415                 mFocusFreezerForTest = null;
1416                 mFocusFreezeExemptUids = null;
1417                 return false;
1418             }
1419         }
1420         return true;
1421     }
1422 
exitAudioFocusFreezeForTest(@onNull IBinder cb)1423     protected boolean exitAudioFocusFreezeForTest(@NonNull IBinder cb) {
1424         synchronized (mAudioFocusLock) {
1425             if (mFocusFreezerForTest != cb) {
1426                 Log.e(TAG, "Error exitAudioFocusFreezeForTest: "
1427                         + ((mFocusFreezerForTest == null)
1428                         ? "call to exit while not frozen"
1429                         : "call to exit not coming from freeze owner"));
1430                 return false;
1431             }
1432             mFocusFreezerForTest.unlinkToDeath(mFocusFreezerDeathHandler, 0);
1433             releaseFocusFreeze();
1434         }
1435         return true;
1436     }
1437 
releaseFocusFreeze()1438     private void releaseFocusFreeze() {
1439         synchronized (mAudioFocusLock) {
1440             mFocusFreezerDeathHandler = null;
1441             mFocusFreezeExemptUids = null;
1442             mFocusFreezerForTest = null;
1443         }
1444     }
1445 
unregisterAudioFocusClient(String clientId)1446     protected void unregisterAudioFocusClient(String clientId) {
1447         synchronized(mAudioFocusLock) {
1448             removeFocusStackEntry(clientId, false, true /*notifyFocusFollowers*/);
1449         }
1450     }
1451 
runAudioCheckerForRingOrCallAsync(final boolean enteringRingOrCall)1452     private void runAudioCheckerForRingOrCallAsync(final boolean enteringRingOrCall) {
1453         new Thread() {
1454             public void run() {
1455                 if (enteringRingOrCall) {
1456                     try {
1457                         Thread.sleep(RING_CALL_MUTING_ENFORCEMENT_DELAY_MS);
1458                     } catch (InterruptedException e) {
1459                         e.printStackTrace();
1460                     }
1461                 }
1462                 synchronized (mAudioFocusLock) {
1463                     // since the new thread starting running the state could have changed, so
1464                     // we need to check again mRingOrCallActive, not enteringRingOrCall
1465                     if (mRingOrCallActive) {
1466                         mFocusEnforcer.mutePlayersForCall(USAGES_TO_MUTE_IN_RING_OR_CALL);
1467                     } else {
1468                         mFocusEnforcer.unmutePlayersForCall();
1469                     }
1470                 }
1471             }
1472         }.start();
1473     }
1474 
updateMultiAudioFocus(boolean enabled)1475     public void updateMultiAudioFocus(boolean enabled) {
1476         Log.d(TAG, "updateMultiAudioFocus( " + enabled + " )");
1477         mMultiAudioFocusEnabled = enabled;
1478         final ContentResolver cr = mContext.getContentResolver();
1479         Settings.System.putIntForUser(cr,
1480                 Settings.System.MULTI_AUDIO_FOCUS_ENABLED, enabled ? 1 : 0, cr.getUserId());
1481         if (!mFocusStack.isEmpty()) {
1482             final FocusRequester fr = mFocusStack.peek();
1483             fr.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null, false);
1484         }
1485         if (!enabled) {
1486             if (!mMultiAudioFocusList.isEmpty()) {
1487                 for (FocusRequester multifr : mMultiAudioFocusList) {
1488                     multifr.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null, false);
1489                 }
1490                 mMultiAudioFocusList.clear();
1491             }
1492         }
1493     }
1494 
getMultiAudioFocusEnabled()1495     public boolean getMultiAudioFocusEnabled() {
1496         return mMultiAudioFocusEnabled;
1497     }
1498 
getFadeOutDurationOnFocusLossMillis(AudioAttributes aa)1499     /*package*/ long getFadeOutDurationOnFocusLossMillis(AudioAttributes aa) {
1500         if (!ENFORCE_FADEOUT_FOR_FOCUS_LOSS) {
1501             return 0;
1502         }
1503         return getFadeOutDurationMillis(aa);
1504     }
1505 
dumpMultiAudioFocus(PrintWriter pw)1506     private void dumpMultiAudioFocus(PrintWriter pw) {
1507         pw.println("Multi Audio Focus enabled :" + mMultiAudioFocusEnabled);
1508         if (!mMultiAudioFocusList.isEmpty()) {
1509             pw.println("Multi Audio Focus List:");
1510             pw.println("------------------------------");
1511             for (FocusRequester multifr : mMultiAudioFocusList) {
1512                 multifr.dump(pw);
1513             }
1514             pw.println("------------------------------");
1515         }
1516     }
1517 
1518     //=================================================================
1519     // Async focus events
postDelayedLossAfterFade(FocusRequester focusLoser, long delayMs)1520     void postDelayedLossAfterFade(FocusRequester focusLoser, long delayMs) {
1521         if (DEBUG) {
1522             Log.v(TAG, "postDelayedLossAfterFade loser=" + focusLoser.getPackageName());
1523         }
1524         mFocusHandler.sendMessageDelayed(
1525                 mFocusHandler.obtainMessage(MSG_L_FOCUS_LOSS_AFTER_FADE, focusLoser), delayMs);
1526     }
1527 
postForgetUidLater(FocusRequester focusRequester)1528     private void postForgetUidLater(FocusRequester focusRequester) {
1529         mFocusHandler.sendMessageDelayed(
1530                 mFocusHandler.obtainMessage(MSL_L_FORGET_UID,
1531                         new ForgetFadeUidInfo(focusRequester.getClientUid())),
1532                 getFadeInDelayForOffendersMillis(focusRequester.getAudioAttributes()));
1533     }
1534 
1535     //=================================================================
1536     // Message handling
1537     private Handler mFocusHandler;
1538     private HandlerThread mFocusThread;
1539 
1540     /**
1541      * dispatch a focus loss after an app has been faded out. Focus loser is to be released
1542      * after dispatch as it has already left the stack
1543      * args:
1544      *     msg.obj: the audio focus loser
1545      *         type:FocusRequester
1546      */
1547     private static final int MSG_L_FOCUS_LOSS_AFTER_FADE = 1;
1548 
1549     private static final int MSL_L_FORGET_UID = 2;
1550 
initFocusThreading()1551     private void initFocusThreading() {
1552         mFocusThread = new HandlerThread(TAG);
1553         mFocusThread.start();
1554         mFocusHandler = new Handler(mFocusThread.getLooper()) {
1555             @Override
1556             public void handleMessage(Message msg) {
1557                 switch (msg.what) {
1558                     case MSG_L_FOCUS_LOSS_AFTER_FADE:
1559                         if (DEBUG) {
1560                             Log.d(TAG, "MSG_L_FOCUS_LOSS_AFTER_FADE loser="
1561                                     + ((FocusRequester) msg.obj).getPackageName());
1562                         }
1563                         synchronized (mAudioFocusLock) {
1564                             final FocusRequester loser = (FocusRequester) msg.obj;
1565                             if (loser.isInFocusLossLimbo()) {
1566                                 loser.dispatchFocusChange(AudioManager.AUDIOFOCUS_LOSS);
1567                                 loser.release();
1568                                 postForgetUidLater(loser);
1569                             }
1570                         }
1571                         break;
1572 
1573                     case MSL_L_FORGET_UID:
1574                         final int uid = ((ForgetFadeUidInfo) msg.obj).mUid;
1575                         if (DEBUG) {
1576                             Log.d(TAG, "MSL_L_FORGET_UID uid=" + uid);
1577                         }
1578                         mFocusEnforcer.forgetUid(uid);
1579                         break;
1580                     default:
1581                         break;
1582                 }
1583             }
1584         };
1585     }
1586 
1587     /**
1588      * Class to associate a UID with a scheduled event to "forget" a UID for the fade out behavior.
1589      * Having a class with an equals() override allows using Handler.removeEqualsMessage() to
1590      * unschedule events when needed. Here we need to unschedule the "unfading out" == "forget uid"
1591      * whenever a new, more recent, focus related event happens before this one is handled.
1592      */
1593     private static final class ForgetFadeUidInfo {
1594         private final int mUid;
1595 
ForgetFadeUidInfo(int uid)1596         ForgetFadeUidInfo(int uid) {
1597             mUid = uid;
1598         }
1599 
1600         @Override
equals(Object o)1601         public boolean equals(Object o) {
1602             if (this == o) {
1603                 return true;
1604             }
1605             if (o == null || getClass() != o.getClass()) {
1606                 return false;
1607             }
1608             final ForgetFadeUidInfo f = (ForgetFadeUidInfo) o;
1609             if (f.mUid != mUid) {
1610                 return false;
1611             }
1612             return true;
1613         }
1614 
1615         @Override
hashCode()1616         public int hashCode() {
1617             return mUid;
1618         }
1619     }
1620 }
1621