• 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.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      * the audio attributes associated with the focus request
74      */
75     private final @NonNull AudioAttributes mAttributes;
76 
77     /**
78      * Class constructor
79      * @param aa
80      * @param focusRequest
81      * @param grantFlags
82      * @param afl
83      * @param source
84      * @param id
85      * @param hdlr
86      * @param pn
87      * @param uid
88      * @param ctlr cannot be null
89      */
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)90     FocusRequester(@NonNull AudioAttributes aa, int focusRequest, int grantFlags,
91             IAudioFocusDispatcher afl, IBinder source, @NonNull String id,
92             AudioFocusDeathHandler hdlr, @NonNull String pn, int uid,
93             @NonNull MediaFocusControl ctlr, int sdk) {
94         mAttributes = aa;
95         mFocusDispatcher = afl;
96         mSourceRef = source;
97         mClientId = id;
98         mDeathHandler = hdlr;
99         mPackageName = pn;
100         mCallingUid = uid;
101         mFocusGainRequest = focusRequest;
102         mGrantFlags = grantFlags;
103         mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE;
104         mFocusLossWasNotified = true;
105         mFocusController = ctlr;
106         mSdkTarget = sdk;
107     }
108 
FocusRequester(AudioFocusInfo afi, IAudioFocusDispatcher afl, IBinder source, AudioFocusDeathHandler hdlr, @NonNull MediaFocusControl ctlr)109     FocusRequester(AudioFocusInfo afi, IAudioFocusDispatcher afl,
110              IBinder source, AudioFocusDeathHandler hdlr, @NonNull MediaFocusControl ctlr) {
111         mAttributes = afi.getAttributes();
112         mClientId = afi.getClientId();
113         mPackageName = afi.getPackageName();
114         mCallingUid = afi.getClientUid();
115         mFocusGainRequest = afi.getGainRequest();
116         mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE;
117         mFocusLossWasNotified = true;
118         mGrantFlags = afi.getFlags();
119         mSdkTarget = afi.getSdkTarget();
120 
121         mFocusDispatcher = afl;
122         mSourceRef = source;
123         mDeathHandler = hdlr;
124         mFocusController = ctlr;
125     }
126 
hasSameClient(String otherClient)127     boolean hasSameClient(String otherClient) {
128         return mClientId.compareTo(otherClient) == 0;
129     }
130 
isLockedFocusOwner()131     boolean isLockedFocusOwner() {
132         return ((mGrantFlags & AudioManager.AUDIOFOCUS_FLAG_LOCK) != 0);
133     }
134 
hasSameBinder(IBinder ib)135     boolean hasSameBinder(IBinder ib) {
136         return (mSourceRef != null) && mSourceRef.equals(ib);
137     }
138 
hasSameDispatcher(IAudioFocusDispatcher fd)139     boolean hasSameDispatcher(IAudioFocusDispatcher fd) {
140         return (mFocusDispatcher != null) && mFocusDispatcher.equals(fd);
141     }
142 
hasSamePackage(@onNull String pack)143     boolean hasSamePackage(@NonNull String pack) {
144         return mPackageName.compareTo(pack) == 0;
145     }
146 
hasSameUid(int uid)147     boolean hasSameUid(int uid) {
148         return mCallingUid == uid;
149     }
150 
getClientUid()151     int getClientUid() {
152         return mCallingUid;
153     }
154 
getClientId()155     String getClientId() {
156         return mClientId;
157     }
158 
getGainRequest()159     int getGainRequest() {
160         return mFocusGainRequest;
161     }
162 
getGrantFlags()163     int getGrantFlags() {
164         return mGrantFlags;
165     }
166 
getAudioAttributes()167     AudioAttributes getAudioAttributes() {
168         return mAttributes;
169     }
170 
getSdkTarget()171     int getSdkTarget() {
172         return mSdkTarget;
173     }
174 
focusChangeToString(int focus)175     private static String focusChangeToString(int focus) {
176         switch(focus) {
177             case AudioManager.AUDIOFOCUS_NONE:
178                 return "none";
179             case AudioManager.AUDIOFOCUS_GAIN:
180                 return "GAIN";
181             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
182                 return "GAIN_TRANSIENT";
183             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
184                 return "GAIN_TRANSIENT_MAY_DUCK";
185             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:
186                 return "GAIN_TRANSIENT_EXCLUSIVE";
187             case AudioManager.AUDIOFOCUS_LOSS:
188                 return "LOSS";
189             case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
190                 return "LOSS_TRANSIENT";
191             case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
192                 return "LOSS_TRANSIENT_CAN_DUCK";
193             default:
194                 return "[invalid focus change" + focus + "]";
195         }
196     }
197 
focusGainToString()198     private String focusGainToString() {
199         return focusChangeToString(mFocusGainRequest);
200     }
201 
focusLossToString()202     private String focusLossToString() {
203         return focusChangeToString(mFocusLossReceived);
204     }
205 
flagsToString(int flags)206     private static String flagsToString(int flags) {
207         String msg = new String();
208         if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) != 0) {
209             msg += "DELAY_OK";
210         }
211         if ((flags & AudioManager.AUDIOFOCUS_FLAG_LOCK) != 0)     {
212             if (!msg.isEmpty()) { msg += "|"; }
213             msg += "LOCK";
214         }
215         if ((flags & AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) != 0) {
216             if (!msg.isEmpty()) { msg += "|"; }
217             msg += "PAUSES_ON_DUCKABLE_LOSS";
218         }
219         return msg;
220     }
221 
dump(PrintWriter pw)222     void dump(PrintWriter pw) {
223         pw.println("  source:" + mSourceRef
224                 + " -- pack: " + mPackageName
225                 + " -- client: " + mClientId
226                 + " -- gain: " + focusGainToString()
227                 + " -- flags: " + flagsToString(mGrantFlags)
228                 + " -- loss: " + focusLossToString()
229                 + " -- notified: " + mFocusLossWasNotified
230                 + " -- uid: " + mCallingUid
231                 + " -- attr: " + mAttributes
232                 + " -- sdk:" + mSdkTarget);
233     }
234 
235 
release()236     void release() {
237         final IBinder srcRef = mSourceRef;
238         final AudioFocusDeathHandler deathHdlr = mDeathHandler;
239         try {
240             if (srcRef != null && deathHdlr != null) {
241                 srcRef.unlinkToDeath(deathHdlr, 0);
242             }
243         } catch (java.util.NoSuchElementException e) { }
244         mDeathHandler = null;
245         mFocusDispatcher = null;
246     }
247 
248     @Override
finalize()249     protected void finalize() throws Throwable {
250         release();
251         super.finalize();
252     }
253 
254     /**
255      * For a given audio focus gain request, return the audio focus loss type that will result
256      * from it, taking into account any previous focus loss.
257      * @param gainRequest
258      * @return the audio focus loss type that matches the gain request
259      */
focusLossForGainRequest(int gainRequest)260     private int focusLossForGainRequest(int gainRequest) {
261         switch(gainRequest) {
262             case AudioManager.AUDIOFOCUS_GAIN:
263                 switch(mFocusLossReceived) {
264                     case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
265                     case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
266                     case AudioManager.AUDIOFOCUS_LOSS:
267                     case AudioManager.AUDIOFOCUS_NONE:
268                         return AudioManager.AUDIOFOCUS_LOSS;
269                 }
270             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:
271             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
272                 switch(mFocusLossReceived) {
273                     case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
274                     case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
275                     case AudioManager.AUDIOFOCUS_NONE:
276                         return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
277                     case AudioManager.AUDIOFOCUS_LOSS:
278                         return AudioManager.AUDIOFOCUS_LOSS;
279                 }
280             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
281                 switch(mFocusLossReceived) {
282                     case AudioManager.AUDIOFOCUS_NONE:
283                     case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
284                         return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK;
285                     case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
286                         return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
287                     case AudioManager.AUDIOFOCUS_LOSS:
288                         return AudioManager.AUDIOFOCUS_LOSS;
289                 }
290             default:
291                 Log.e(TAG, "focusLossForGainRequest() for invalid focus request "+ gainRequest);
292                         return AudioManager.AUDIOFOCUS_NONE;
293         }
294     }
295 
296     /**
297      * Handle the loss of focus resulting from a given focus gain.
298      * @param focusGain the focus gain from which the loss of focus is resulting
299      * @param frWinner the new focus owner
300      * @return true if the focus loss is definitive, false otherwise.
301      */
302     @GuardedBy("MediaFocusControl.mAudioFocusLock")
handleFocusLossFromGain(int focusGain, final FocusRequester frWinner, boolean forceDuck)303     boolean handleFocusLossFromGain(int focusGain, final FocusRequester frWinner, boolean forceDuck)
304     {
305         final int focusLoss = focusLossForGainRequest(focusGain);
306         handleFocusLoss(focusLoss, frWinner, forceDuck);
307         return (focusLoss == AudioManager.AUDIOFOCUS_LOSS);
308     }
309 
310     @GuardedBy("MediaFocusControl.mAudioFocusLock")
handleFocusGain(int focusGain)311     void handleFocusGain(int focusGain) {
312         try {
313             mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE;
314             mFocusController.notifyExtPolicyFocusGrant_syncAf(toAudioFocusInfo(),
315                     AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
316             final IAudioFocusDispatcher fd = mFocusDispatcher;
317             if (fd != null) {
318                 if (DEBUG) {
319                     Log.v(TAG, "dispatching " + focusChangeToString(focusGain) + " to "
320                         + mClientId);
321                 }
322                 if (mFocusLossWasNotified) {
323                     fd.dispatchAudioFocusChange(focusGain, mClientId);
324                 }
325             }
326             mFocusController.unduckPlayers(this);
327         } catch (android.os.RemoteException e) {
328             Log.e(TAG, "Failure to signal gain of audio focus due to: ", e);
329         }
330     }
331 
332     @GuardedBy("MediaFocusControl.mAudioFocusLock")
handleFocusGainFromRequest(int focusRequestResult)333     void handleFocusGainFromRequest(int focusRequestResult) {
334         if (focusRequestResult == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
335             mFocusController.unduckPlayers(this);
336         }
337     }
338 
339     @GuardedBy("MediaFocusControl.mAudioFocusLock")
handleFocusLoss(int focusLoss, @Nullable final FocusRequester frWinner, boolean forceDuck)340     void handleFocusLoss(int focusLoss, @Nullable final FocusRequester frWinner, boolean forceDuck)
341     {
342         try {
343             if (focusLoss != mFocusLossReceived) {
344                 mFocusLossReceived = focusLoss;
345                 mFocusLossWasNotified = false;
346                 // before dispatching a focus loss, check if the following conditions are met:
347                 // 1/ the framework is not supposed to notify the focus loser on a DUCK loss
348                 //    (i.e. it has a focus controller that implements a ducking policy)
349                 // 2/ it is a DUCK loss
350                 // 3/ the focus loser isn't flagged as pausing in a DUCK loss
351                 // if they are, do not notify the focus loser
352                 if (!mFocusController.mustNotifyFocusOwnerOnDuck()
353                         && mFocusLossReceived == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
354                         && (mGrantFlags
355                                 & AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) == 0) {
356                     if (DEBUG) {
357                         Log.v(TAG, "NOT dispatching " + focusChangeToString(mFocusLossReceived)
358                                 + " to " + mClientId + ", to be handled externally");
359                     }
360                     mFocusController.notifyExtPolicyFocusLoss_syncAf(
361                             toAudioFocusInfo(), false /* wasDispatched */);
362                     return;
363                 }
364 
365                 // check enforcement by the framework
366                 boolean handled = false;
367                 if (focusLoss == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
368                         && MediaFocusControl.ENFORCE_DUCKING
369                         && frWinner != null) {
370                     // candidate for enforcement by the framework
371                     if (frWinner.mCallingUid != this.mCallingUid) {
372                         if (!forceDuck && ((mGrantFlags
373                                 & AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) != 0)) {
374                             // the focus loser declared it would pause instead of duck, let it
375                             // handle it (the framework doesn't pause for apps)
376                             handled = false;
377                             Log.v(TAG, "not ducking uid " + this.mCallingUid + " - flags");
378                         } else if (!forceDuck && (MediaFocusControl.ENFORCE_DUCKING_FOR_NEW &&
379                                 this.getSdkTarget() <= MediaFocusControl.DUCKING_IN_APP_SDK_LEVEL))
380                         {
381                             // legacy behavior, apps used to be notified when they should be ducking
382                             handled = false;
383                             Log.v(TAG, "not ducking uid " + this.mCallingUid + " - old SDK");
384                         } else {
385                             handled = mFocusController.duckPlayers(frWinner, this, forceDuck);
386                         }
387                     } // else: the focus change is within the same app, so let the dispatching
388                       //       happen as if the framework was not involved.
389                 }
390 
391                 if (handled) {
392                     if (DEBUG) {
393                         Log.v(TAG, "NOT dispatching " + focusChangeToString(mFocusLossReceived)
394                             + " to " + mClientId + ", ducking implemented by framework");
395                     }
396                     mFocusController.notifyExtPolicyFocusLoss_syncAf(
397                             toAudioFocusInfo(), false /* wasDispatched */);
398                     return; // with mFocusLossWasNotified = false
399                 }
400 
401                 final IAudioFocusDispatcher fd = mFocusDispatcher;
402                 if (fd != null) {
403                     if (DEBUG) {
404                         Log.v(TAG, "dispatching " + focusChangeToString(mFocusLossReceived) + " to "
405                             + mClientId);
406                     }
407                     mFocusController.notifyExtPolicyFocusLoss_syncAf(
408                             toAudioFocusInfo(), true /* wasDispatched */);
409                     mFocusLossWasNotified = true;
410                     fd.dispatchAudioFocusChange(mFocusLossReceived, mClientId);
411                 }
412             }
413         } catch (android.os.RemoteException e) {
414             Log.e(TAG, "Failure to signal loss of audio focus due to:", e);
415         }
416     }
417 
dispatchFocusChange(int focusChange)418     int dispatchFocusChange(int focusChange) {
419         if (mFocusDispatcher == null) {
420             if (MediaFocusControl.DEBUG) { Log.e(TAG, "dispatchFocusChange: no focus dispatcher"); }
421             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
422         }
423         if (focusChange == AudioManager.AUDIOFOCUS_NONE) {
424             if (MediaFocusControl.DEBUG) { Log.v(TAG, "dispatchFocusChange: AUDIOFOCUS_NONE"); }
425             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
426         } else if ((focusChange == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
427                 || focusChange == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
428                 || focusChange == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT
429                 || focusChange == AudioManager.AUDIOFOCUS_GAIN)
430                 && (mFocusGainRequest != focusChange)){
431             Log.w(TAG, "focus gain was requested with " + mFocusGainRequest
432                     + ", dispatching " + focusChange);
433         } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
434                 || focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT
435                 || focusChange == AudioManager.AUDIOFOCUS_LOSS) {
436             mFocusLossReceived = focusChange;
437         }
438         try {
439             mFocusDispatcher.dispatchAudioFocusChange(focusChange, mClientId);
440         } catch (android.os.RemoteException e) {
441             Log.e(TAG, "dispatchFocusChange: error talking to focus listener " + mClientId, e);
442             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
443         }
444         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
445     }
446 
dispatchFocusResultFromExtPolicy(int requestResult)447     void dispatchFocusResultFromExtPolicy(int requestResult) {
448         if (mFocusDispatcher == null) {
449             if (MediaFocusControl.DEBUG) {
450                 Log.e(TAG, "dispatchFocusResultFromExtPolicy: no focus dispatcher");
451             }
452         }
453         if (DEBUG) {
454             Log.v(TAG, "dispatching result" + requestResult + " to " + mClientId);
455         }
456         try {
457             mFocusDispatcher.dispatchFocusResultFromExtPolicy(requestResult, mClientId);
458         } catch (android.os.RemoteException e) {
459             Log.e(TAG, "dispatchFocusResultFromExtPolicy: error talking to focus listener"
460                     + mClientId, e);
461         }
462     }
463 
toAudioFocusInfo()464     AudioFocusInfo toAudioFocusInfo() {
465         return new AudioFocusInfo(mAttributes, mCallingUid, mClientId, mPackageName,
466                 mFocusGainRequest, mFocusLossReceived, mGrantFlags, mSdkTarget);
467     }
468 }
469