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.content.pm.UserProperties;
22 import android.media.AudioAttributes;
23 import android.media.AudioFocusInfo;
24 import android.media.AudioManager;
25 import android.media.IAudioFocusDispatcher;
26 import android.os.IBinder;
27 import android.os.UserHandle;
28 import android.util.Log;
29 
30 import com.android.internal.annotations.GuardedBy;
31 import com.android.server.LocalServices;
32 import com.android.server.audio.MediaFocusControl.AudioFocusDeathHandler;
33 import com.android.server.pm.UserManagerInternal;
34 
35 import java.io.PrintWriter;
36 import java.util.List;
37 
38 /**
39  * @hide
40  * Class to handle all the information about a user of audio focus. The lifecycle of each
41  * instance is managed by android.media.MediaFocusControl, from its addition to the audio focus
42  * stack, or the map of focus owners for an external focus policy, to its release.
43  */
44 public class FocusRequester {
45 
46     // on purpose not using this classe's name, as it will only be used from MediaFocusControl
47     private static final String TAG = "FocusRequester";
48     private static final boolean DEBUG = false;
49 
50     private AudioFocusDeathHandler mDeathHandler; // may be null
51     private IAudioFocusDispatcher mFocusDispatcher; // may be null
52     private final IBinder mSourceRef; // may be null
53     private final @NonNull String mClientId;
54     private final @NonNull String mPackageName;
55     private final int mCallingUid;
56     private final MediaFocusControl mFocusController; // never null
57     private final int mSdkTarget;
58 
59     /**
60      * the audio focus gain request that caused the addition of this object in the focus stack.
61      */
62     private final int mFocusGainRequest;
63     /**
64      * the flags associated with the gain request that qualify the type of grant (e.g. accepting
65      * delay vs grant must be immediate)
66      */
67     private final int mGrantFlags;
68     /**
69      * the audio focus loss received my mFocusDispatcher, is AudioManager.AUDIOFOCUS_NONE if
70      *  it never lost focus.
71      */
72     private int mFocusLossReceived;
73     /**
74      * whether this focus owner listener was notified when it lost focus
75      */
76     private boolean mFocusLossWasNotified;
77     /**
78      * whether this focus owner has already lost focus, but is being faded out until focus loss
79      * dispatch occurs. It's in "limbo" mode: has lost focus but not released yet until notified
80      */
81     boolean mFocusLossFadeLimbo;
82     /**
83      * the audio attributes associated with the focus request
84      */
85     private final @NonNull AudioAttributes mAttributes;
86 
87     /**
88      * Class constructor
89      * @param aa
90      * @param focusRequest
91      * @param grantFlags
92      * @param afl
93      * @param source
94      * @param id
95      * @param hdlr
96      * @param pn
97      * @param uid
98      * @param ctlr cannot be null
99      */
FocusRequester(@onNull AudioAttributes aa, int focusRequest, int grantFlags, IAudioFocusDispatcher afl, IBinder source, @NonNull String id, AudioFocusDeathHandler hdlr, @NonNull String pn, int uid, @NonNull MediaFocusControl ctlr, int sdk)100     FocusRequester(@NonNull AudioAttributes aa, int focusRequest, int grantFlags,
101             IAudioFocusDispatcher afl, IBinder source, @NonNull String id,
102             AudioFocusDeathHandler hdlr, @NonNull String pn, int uid,
103             @NonNull MediaFocusControl ctlr, int sdk) {
104         mAttributes = aa;
105         mFocusDispatcher = afl;
106         mSourceRef = source;
107         mClientId = id;
108         mDeathHandler = hdlr;
109         mPackageName = pn;
110         mCallingUid = uid;
111         mFocusGainRequest = focusRequest;
112         mGrantFlags = grantFlags;
113         mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE;
114         mFocusLossWasNotified = true;
115         mFocusLossFadeLimbo = false;
116         mFocusController = ctlr;
117         mSdkTarget = sdk;
118     }
119 
FocusRequester(AudioFocusInfo afi, IAudioFocusDispatcher afl, IBinder source, AudioFocusDeathHandler hdlr, @NonNull MediaFocusControl ctlr)120     FocusRequester(AudioFocusInfo afi, IAudioFocusDispatcher afl,
121              IBinder source, AudioFocusDeathHandler hdlr, @NonNull MediaFocusControl ctlr) {
122         mAttributes = afi.getAttributes();
123         mClientId = afi.getClientId();
124         mPackageName = afi.getPackageName();
125         mCallingUid = afi.getClientUid();
126         mFocusGainRequest = afi.getGainRequest();
127         mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE;
128         mFocusLossWasNotified = true;
129         mFocusLossFadeLimbo = false;
130         mGrantFlags = afi.getFlags();
131         mSdkTarget = afi.getSdkTarget();
132 
133         mFocusDispatcher = afl;
134         mSourceRef = source;
135         mDeathHandler = hdlr;
136         mFocusController = ctlr;
137     }
138 
hasSameClient(String otherClient)139     boolean hasSameClient(String otherClient) {
140         return mClientId.compareTo(otherClient) == 0;
141     }
142 
isLockedFocusOwner()143     boolean isLockedFocusOwner() {
144         return ((mGrantFlags & AudioManager.AUDIOFOCUS_FLAG_LOCK) != 0);
145     }
146 
147     /**
148      * @return true if the focus requester is scheduled to receive a focus loss
149      */
isInFocusLossLimbo()150     boolean isInFocusLossLimbo() {
151         return mFocusLossFadeLimbo;
152     }
153 
hasSameBinder(IBinder ib)154     boolean hasSameBinder(IBinder ib) {
155         return (mSourceRef != null) && mSourceRef.equals(ib);
156     }
157 
hasSameDispatcher(IAudioFocusDispatcher fd)158     boolean hasSameDispatcher(IAudioFocusDispatcher fd) {
159         return (mFocusDispatcher != null) && mFocusDispatcher.equals(fd);
160     }
161 
getPackageName()162     @NonNull String getPackageName() {
163         return mPackageName;
164     }
165 
hasSamePackage(@onNull String pack)166     boolean hasSamePackage(@NonNull String pack) {
167         return mPackageName.compareTo(pack) == 0;
168     }
169 
hasSameUid(int uid)170     boolean hasSameUid(int uid) {
171         return mCallingUid == uid;
172     }
173 
isAlwaysVisibleUser()174     boolean isAlwaysVisibleUser() {
175         final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
176         final UserProperties properties = umi.getUserProperties(UserHandle.getUserId(mCallingUid));
177         return properties != null && properties.getAlwaysVisible();
178     }
179 
getClientUid()180     int getClientUid() {
181         return mCallingUid;
182     }
183 
getClientId()184     String getClientId() {
185         return mClientId;
186     }
187 
getGainRequest()188     int getGainRequest() {
189         return mFocusGainRequest;
190     }
191 
getGrantFlags()192     int getGrantFlags() {
193         return mGrantFlags;
194     }
195 
getAudioAttributes()196     @NonNull AudioAttributes getAudioAttributes() {
197         return mAttributes;
198     }
199 
getSdkTarget()200     int getSdkTarget() {
201         return mSdkTarget;
202     }
203 
focusChangeToString(int focus)204     private static String focusChangeToString(int focus) {
205         switch(focus) {
206             case AudioManager.AUDIOFOCUS_NONE:
207                 return "none";
208             case AudioManager.AUDIOFOCUS_GAIN:
209                 return "GAIN";
210             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
211                 return "GAIN_TRANSIENT";
212             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
213                 return "GAIN_TRANSIENT_MAY_DUCK";
214             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:
215                 return "GAIN_TRANSIENT_EXCLUSIVE";
216             case AudioManager.AUDIOFOCUS_LOSS:
217                 return "LOSS";
218             case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
219                 return "LOSS_TRANSIENT";
220             case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
221                 return "LOSS_TRANSIENT_CAN_DUCK";
222             default:
223                 return "[invalid focus change" + focus + "]";
224         }
225     }
226 
focusGainToString()227     private String focusGainToString() {
228         return focusChangeToString(mFocusGainRequest);
229     }
230 
focusLossToString()231     private String focusLossToString() {
232         return focusChangeToString(mFocusLossReceived);
233     }
234 
flagsToString(int flags)235     private static String flagsToString(int flags) {
236         String msg = new String();
237         if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) != 0) {
238             msg += "DELAY_OK";
239         }
240         if ((flags & AudioManager.AUDIOFOCUS_FLAG_LOCK) != 0)     {
241             if (!msg.isEmpty()) { msg += "|"; }
242             msg += "LOCK";
243         }
244         if ((flags & AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) != 0) {
245             if (!msg.isEmpty()) { msg += "|"; }
246             msg += "PAUSES_ON_DUCKABLE_LOSS";
247         }
248         return msg;
249     }
250 
dump(PrintWriter pw)251     void dump(PrintWriter pw) {
252         pw.println("  source:" + mSourceRef
253                 + " -- pack: " + mPackageName
254                 + " -- client: " + mClientId
255                 + " -- gain: " + focusGainToString()
256                 + " -- flags: " + flagsToString(mGrantFlags)
257                 + " -- loss: " + focusLossToString()
258                 + " -- notified: " + mFocusLossWasNotified
259                 + " -- limbo" + mFocusLossFadeLimbo
260                 + " -- uid: " + mCallingUid
261                 + " -- attr: " + mAttributes
262                 + " -- sdk:" + mSdkTarget);
263     }
264 
265     /**
266      * Clear all references, except for instances in "loss limbo" due to the current fade out
267      * for which there will be an attempt to be clear after the loss has been notified
268      */
maybeRelease()269     void maybeRelease() {
270         if (!mFocusLossFadeLimbo) {
271             release();
272         }
273     }
274 
release()275     void release() {
276         final IBinder srcRef = mSourceRef;
277         final AudioFocusDeathHandler deathHdlr = mDeathHandler;
278         try {
279             if (srcRef != null && deathHdlr != null) {
280                 srcRef.unlinkToDeath(deathHdlr, 0);
281             }
282         } catch (java.util.NoSuchElementException e) { }
283         mDeathHandler = null;
284         mFocusDispatcher = null;
285     }
286 
287     @Override
finalize()288     protected void finalize() throws Throwable {
289         release();
290         super.finalize();
291     }
292 
293     /**
294      * For a given audio focus gain request, return the audio focus loss type that will result
295      * from it, taking into account any previous focus loss.
296      * @param gainRequest
297      * @return the audio focus loss type that matches the gain request
298      */
focusLossForGainRequest(int gainRequest)299     private int focusLossForGainRequest(int gainRequest) {
300         switch(gainRequest) {
301             case AudioManager.AUDIOFOCUS_GAIN:
302                 switch(mFocusLossReceived) {
303                     case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
304                     case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
305                     case AudioManager.AUDIOFOCUS_LOSS:
306                     case AudioManager.AUDIOFOCUS_NONE:
307                         return AudioManager.AUDIOFOCUS_LOSS;
308                 }
309             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:
310             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
311                 switch(mFocusLossReceived) {
312                     case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
313                     case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
314                     case AudioManager.AUDIOFOCUS_NONE:
315                         return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
316                     case AudioManager.AUDIOFOCUS_LOSS:
317                         return AudioManager.AUDIOFOCUS_LOSS;
318                 }
319             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
320                 switch(mFocusLossReceived) {
321                     case AudioManager.AUDIOFOCUS_NONE:
322                     case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
323                         return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK;
324                     case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
325                         return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
326                     case AudioManager.AUDIOFOCUS_LOSS:
327                         return AudioManager.AUDIOFOCUS_LOSS;
328                 }
329             default:
330                 Log.e(TAG, "focusLossForGainRequest() for invalid focus request "+ gainRequest);
331                         return AudioManager.AUDIOFOCUS_NONE;
332         }
333     }
334 
335     /**
336      * Handle the loss of focus resulting from a given focus gain.
337      * @param focusGain the focus gain from which the loss of focus is resulting
338      * @param frWinner the new focus owner
339      * @return true if the focus loss is definitive, false otherwise.
340      */
341     @GuardedBy("MediaFocusControl.mAudioFocusLock")
handleFocusLossFromGain(int focusGain, final FocusRequester frWinner, boolean forceDuck)342     boolean handleFocusLossFromGain(int focusGain, final FocusRequester frWinner, boolean forceDuck)
343     {
344         if (DEBUG) {
345             Log.i(TAG, "handleFocusLossFromGain for " + mClientId + " gain:" + focusGain);
346         }
347         final int focusLoss = focusLossForGainRequest(focusGain);
348         handleFocusLoss(focusLoss, frWinner, forceDuck);
349         return (focusLoss == AudioManager.AUDIOFOCUS_LOSS);
350     }
351 
352     @GuardedBy("MediaFocusControl.mAudioFocusLock")
handleFocusGain(int focusGain)353     void handleFocusGain(int focusGain) {
354         try {
355             mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE;
356             mFocusLossFadeLimbo = false;
357             mFocusController.notifyExtPolicyFocusGrant_syncAf(toAudioFocusInfo(),
358                     AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
359             final IAudioFocusDispatcher fd = mFocusDispatcher;
360             if (fd != null) {
361                 if (DEBUG) {
362                     Log.v(TAG, "dispatching " + focusChangeToString(focusGain) + " to "
363                         + mClientId);
364                 }
365                 if (mFocusLossWasNotified) {
366                     fd.dispatchAudioFocusChange(focusGain, mClientId);
367                 }
368             }
369             mFocusController.restoreVShapedPlayers(this);
370         } catch (android.os.RemoteException e) {
371             Log.e(TAG, "Failure to signal gain of audio focus due to: ", e);
372         }
373     }
374 
375     @GuardedBy("MediaFocusControl.mAudioFocusLock")
handleFocusGainFromRequest(int focusRequestResult)376     void handleFocusGainFromRequest(int focusRequestResult) {
377         if (focusRequestResult == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
378             mFocusController.restoreVShapedPlayers(this);
379         }
380     }
381 
382     @GuardedBy("MediaFocusControl.mAudioFocusLock")
handleFocusLoss(int focusLoss, @Nullable final FocusRequester frWinner, boolean forceDuck)383     void handleFocusLoss(int focusLoss, @Nullable final FocusRequester frWinner, boolean forceDuck)
384     {
385         if (DEBUG) {
386             Log.i(TAG, "handleFocusLoss for " + mClientId + " loss:" + focusLoss);
387         }
388         try {
389             if (focusLoss != mFocusLossReceived) {
390                 mFocusLossReceived = focusLoss;
391                 mFocusLossWasNotified = false;
392                 // before dispatching a focus loss, check if the following conditions are met:
393                 // 1/ the framework is not supposed to notify the focus loser on a DUCK loss
394                 //    (i.e. it has a focus controller that implements a ducking policy)
395                 // 2/ it is a DUCK loss
396                 // 3/ the focus loser isn't flagged as pausing in a DUCK loss
397                 // if they are, do not notify the focus loser
398                 if (!mFocusController.mustNotifyFocusOwnerOnDuck()
399                         && mFocusLossReceived == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
400                         && (mGrantFlags
401                                 & AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) == 0) {
402                     if (DEBUG) {
403                         Log.v(TAG, "NOT dispatching " + focusChangeToString(mFocusLossReceived)
404                                 + " to " + mClientId + ", to be handled externally");
405                     }
406                     mFocusController.notifyExtPolicyFocusLoss_syncAf(
407                             toAudioFocusInfo(), false /* wasDispatched */);
408                     return;
409                 }
410 
411                 // check enforcement by the framework
412                 boolean handled = false;
413                 if (frWinner != null) {
414                     handled = frameworkHandleFocusLoss(focusLoss, frWinner, forceDuck);
415                 }
416 
417                 if (handled) {
418                     if (DEBUG) {
419                         Log.v(TAG, "NOT dispatching " + focusChangeToString(mFocusLossReceived)
420                                 + " to " + mClientId + ", response handled by framework");
421                     }
422                     mFocusController.notifyExtPolicyFocusLoss_syncAf(
423                             toAudioFocusInfo(), false /* wasDispatched */);
424                     return; // with mFocusLossWasNotified = false
425                 }
426 
427                 final IAudioFocusDispatcher fd = mFocusDispatcher;
428                 if (fd != null) {
429                     if (DEBUG) {
430                         Log.v(TAG, "dispatching " + focusChangeToString(mFocusLossReceived) + " to "
431                             + mClientId);
432                     }
433                     mFocusController.notifyExtPolicyFocusLoss_syncAf(
434                             toAudioFocusInfo(), true /* wasDispatched */);
435                     mFocusLossWasNotified = true;
436                     fd.dispatchAudioFocusChange(mFocusLossReceived, mClientId);
437                 } else if (DEBUG) {
438                     Log.i(TAG, "NOT dispatching " + focusChangeToString(mFocusLossReceived)
439                             + " to " + mClientId + " no IAudioFocusDispatcher");
440                 }
441             }
442         } catch (android.os.RemoteException e) {
443             Log.e(TAG, "Failure to signal loss of audio focus due to:", e);
444         }
445     }
446 
447     /**
448      * Let the framework handle the focus loss if possible
449      * @param focusLoss
450      * @param frWinner
451      * @param forceDuck
452      * @return true if the framework handled the focus loss
453      */
454     @GuardedBy("MediaFocusControl.mAudioFocusLock")
frameworkHandleFocusLoss(int focusLoss, @NonNull final FocusRequester frWinner, boolean forceDuck)455     private boolean frameworkHandleFocusLoss(int focusLoss, @NonNull final FocusRequester frWinner,
456                                              boolean forceDuck) {
457         if (frWinner.mCallingUid == this.mCallingUid) {
458             // the focus change is within the same app, so let the dispatching
459             // happen as if the framework was not involved.
460             return false;
461         }
462 
463         if (focusLoss == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
464             if (!MediaFocusControl.ENFORCE_DUCKING) {
465                 return false;
466             }
467 
468             // candidate for enforcement by the framework
469             if (!forceDuck && ((mGrantFlags
470                     & AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) != 0)) {
471                 // the focus loser declared it would pause instead of duck, let it
472                 // handle it (the framework doesn't pause for apps)
473                 Log.v(TAG, "not ducking uid " + this.mCallingUid + " - flags");
474                 return false;
475             }
476             if (!forceDuck && (MediaFocusControl.ENFORCE_DUCKING_FOR_NEW
477                     && this.getSdkTarget() <= MediaFocusControl.DUCKING_IN_APP_SDK_LEVEL)) {
478                 // legacy behavior, apps used to be notified when they should be ducking
479                 Log.v(TAG, "not ducking uid " + this.mCallingUid + " - old SDK");
480                 return false;
481             }
482 
483             return mFocusController.duckPlayers(frWinner, /*loser*/ this, forceDuck);
484         }
485 
486         if (focusLoss == AudioManager.AUDIOFOCUS_LOSS) {
487             if (!MediaFocusControl.ENFORCE_FADEOUT_FOR_FOCUS_LOSS) {
488                 return false;
489             }
490 
491             // candidate for fade-out before a receiving a loss
492             boolean playersAreFaded =  mFocusController.fadeOutPlayers(frWinner, /* loser */ this);
493             if (playersAreFaded) {
494                 // active players are being faded out, delay the dispatch of focus loss
495                 // mark this instance as being faded so it's not released yet as the focus loss
496                 // will be dispatched later, it is now in limbo mode
497                 mFocusLossFadeLimbo = true;
498                 mFocusController.postDelayedLossAfterFade(this,
499                         mFocusController.getFadeOutDurationOnFocusLossMillis(
500                                 this.getAudioAttributes()));
501                 return true;
502             }
503         }
504 
505         return false;
506     }
507 
dispatchFocusChange(int focusChange)508     int dispatchFocusChange(int focusChange) {
509         final IAudioFocusDispatcher fd = mFocusDispatcher;
510         if (fd == null) {
511             if (MediaFocusControl.DEBUG) { Log.e(TAG, "dispatchFocusChange: no focus dispatcher"); }
512             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
513         }
514         if (focusChange == AudioManager.AUDIOFOCUS_NONE) {
515             if (MediaFocusControl.DEBUG) { Log.v(TAG, "dispatchFocusChange: AUDIOFOCUS_NONE"); }
516             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
517         } else if ((focusChange == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
518                 || focusChange == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
519                 || focusChange == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT
520                 || focusChange == AudioManager.AUDIOFOCUS_GAIN)
521                 && (mFocusGainRequest != focusChange)){
522             Log.w(TAG, "focus gain was requested with " + mFocusGainRequest
523                     + ", dispatching " + focusChange);
524         } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
525                 || focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT
526                 || focusChange == AudioManager.AUDIOFOCUS_LOSS) {
527             mFocusLossReceived = focusChange;
528         }
529         try {
530             fd.dispatchAudioFocusChange(focusChange, mClientId);
531         } catch (android.os.RemoteException e) {
532             Log.e(TAG, "dispatchFocusChange: error talking to focus listener " + mClientId, e);
533             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
534         }
535         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
536     }
537 
538     @GuardedBy("MediaFocusControl.mAudioFocusLock")
dispatchFocusChangeWithFadeLocked(int focusChange, List<FocusRequester> otherActiveFrs)539     int dispatchFocusChangeWithFadeLocked(int focusChange, List<FocusRequester> otherActiveFrs) {
540         if (focusChange == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
541                 || focusChange == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
542                 || focusChange == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT
543                 || focusChange == AudioManager.AUDIOFOCUS_GAIN) {
544             mFocusLossFadeLimbo = false;
545             mFocusController.restoreVShapedPlayers(this);
546         } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS
547                 && mFocusController.shouldEnforceFade()) {
548             for (int index = 0; index < otherActiveFrs.size(); index++) {
549                 // candidate for fade-out before a receiving a loss
550                 if (mFocusController.fadeOutPlayers(otherActiveFrs.get(index), /* loser= */ this)) {
551                     // active players are being faded out, delay the dispatch of focus loss
552                     // mark this instance as being faded so it's not released yet as the focus loss
553                     // will be dispatched later, it is now in limbo mode
554                     mFocusLossFadeLimbo = true;
555                     mFocusController.postDelayedLossAfterFade(this,
556                             mFocusController.getFadeOutDurationOnFocusLossMillis(
557                                     this.getAudioAttributes()));
558                     return AudioManager.AUDIOFOCUS_REQUEST_DELAYED;
559                 }
560             }
561         }
562         return dispatchFocusChange(focusChange);
563     }
564 
dispatchFocusResultFromExtPolicy(int requestResult)565     void dispatchFocusResultFromExtPolicy(int requestResult) {
566         final IAudioFocusDispatcher fd = mFocusDispatcher;
567         if (fd == null) {
568             if (MediaFocusControl.DEBUG) {
569                 Log.e(TAG, "dispatchFocusResultFromExtPolicy: no focus dispatcher");
570             }
571             return;
572         }
573         if (DEBUG) {
574             Log.v(TAG, "dispatching result" + requestResult + " to " + mClientId);
575         }
576         try {
577             fd.dispatchFocusResultFromExtPolicy(requestResult, mClientId);
578         } catch (android.os.RemoteException e) {
579             Log.e(TAG, "dispatchFocusResultFromExtPolicy: error talking to focus listener"
580                     + mClientId, e);
581         }
582     }
583 
toAudioFocusInfo()584     AudioFocusInfo toAudioFocusInfo() {
585         return new AudioFocusInfo(mAttributes, mCallingUid, mClientId, mPackageName,
586                 mFocusGainRequest, mFocusLossReceived, mGrantFlags, mSdkTarget);
587     }
588 }
589