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