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