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