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