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 android.media;
18 
19 import android.annotation.NonNull;
20 import android.media.MediaFocusControl.AudioFocusDeathHandler;
21 import android.os.IBinder;
22 import android.util.Log;
23 
24 import java.io.PrintWriter;
25 
26 /**
27  * @hide
28  * Class to handle all the information about a user of audio focus. The lifecycle of each
29  * instance is managed by android.media.MediaFocusControl, from its addition to the audio focus
30  * stack to its release.
31  */
32 class FocusRequester {
33 
34     // on purpose not using this classe's name, as it will only be used from MediaFocusControl
35     private static final String TAG = "MediaFocusControl";
36     private static final boolean DEBUG = false;
37 
38     private AudioFocusDeathHandler mDeathHandler;
39     private final IAudioFocusDispatcher mFocusDispatcher; // may be null
40     private final IBinder mSourceRef;
41     private final String mClientId;
42     private final String mPackageName;
43     private final int mCallingUid;
44     private final MediaFocusControl mFocusController; // never null
45     /**
46      * the audio focus gain request that caused the addition of this object in the focus stack.
47      */
48     private final int mFocusGainRequest;
49     /**
50      * the flags associated with the gain request that qualify the type of grant (e.g. accepting
51      * delay vs grant must be immediate)
52      */
53     private final int mGrantFlags;
54     /**
55      * the audio focus loss received my mFocusDispatcher, is AudioManager.AUDIOFOCUS_NONE if
56      *  it never lost focus.
57      */
58     private int mFocusLossReceived;
59     /**
60      * the audio attributes associated with the focus request
61      */
62     private final AudioAttributes mAttributes;
63 
64     /**
65      * Class constructor
66      * @param aa
67      * @param focusRequest
68      * @param grantFlags
69      * @param afl
70      * @param source
71      * @param id
72      * @param hdlr
73      * @param pn
74      * @param uid
75      * @param ctlr cannot be null
76      */
FocusRequester(AudioAttributes aa, int focusRequest, int grantFlags, IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr, String pn, int uid, @NonNull MediaFocusControl ctlr)77     FocusRequester(AudioAttributes aa, int focusRequest, int grantFlags,
78             IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr,
79             String pn, int uid, @NonNull MediaFocusControl ctlr) {
80         mAttributes = aa;
81         mFocusDispatcher = afl;
82         mSourceRef = source;
83         mClientId = id;
84         mDeathHandler = hdlr;
85         mPackageName = pn;
86         mCallingUid = uid;
87         mFocusGainRequest = focusRequest;
88         mGrantFlags = grantFlags;
89         mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE;
90         mFocusController = ctlr;
91     }
92 
93 
hasSameClient(String otherClient)94     boolean hasSameClient(String otherClient) {
95         try {
96             return mClientId.compareTo(otherClient) == 0;
97         } catch (NullPointerException e) {
98             return false;
99         }
100     }
101 
isLockedFocusOwner()102     boolean isLockedFocusOwner() {
103         return ((mGrantFlags & AudioManager.AUDIOFOCUS_FLAG_LOCK) != 0);
104     }
105 
hasSameBinder(IBinder ib)106     boolean hasSameBinder(IBinder ib) {
107         return (mSourceRef != null) && mSourceRef.equals(ib);
108     }
109 
hasSamePackage(String pack)110     boolean hasSamePackage(String pack) {
111         try {
112             return mPackageName.compareTo(pack) == 0;
113         } catch (NullPointerException e) {
114             return false;
115         }
116     }
117 
hasSameUid(int uid)118     boolean hasSameUid(int uid) {
119         return mCallingUid == uid;
120     }
121 
getClientId()122     String getClientId() {
123         return mClientId;
124     }
125 
getGainRequest()126     int getGainRequest() {
127         return mFocusGainRequest;
128     }
129 
getGrantFlags()130     int getGrantFlags() {
131         return mGrantFlags;
132     }
133 
getAudioAttributes()134     AudioAttributes getAudioAttributes() {
135         return mAttributes;
136     }
137 
138 
focusChangeToString(int focus)139     private static String focusChangeToString(int focus) {
140         switch(focus) {
141             case AudioManager.AUDIOFOCUS_NONE:
142                 return "none";
143             case AudioManager.AUDIOFOCUS_GAIN:
144                 return "GAIN";
145             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
146                 return "GAIN_TRANSIENT";
147             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
148                 return "GAIN_TRANSIENT_MAY_DUCK";
149             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:
150                 return "GAIN_TRANSIENT_EXCLUSIVE";
151             case AudioManager.AUDIOFOCUS_LOSS:
152                 return "LOSS";
153             case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
154                 return "LOSS_TRANSIENT";
155             case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
156                 return "LOSS_TRANSIENT_CAN_DUCK";
157             default:
158                 return "[invalid focus change" + focus + "]";
159         }
160     }
161 
focusGainToString()162     private String focusGainToString() {
163         return focusChangeToString(mFocusGainRequest);
164     }
165 
focusLossToString()166     private String focusLossToString() {
167         return focusChangeToString(mFocusLossReceived);
168     }
169 
flagsToString(int flags)170     private static String flagsToString(int flags) {
171         String msg = new String();
172         if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) != 0) {
173             msg += "DELAY_OK";
174         }
175         if ((flags & AudioManager.AUDIOFOCUS_FLAG_LOCK) != 0)     {
176             if (!msg.isEmpty()) { msg += "|"; }
177             msg += "LOCK";
178         }
179         if ((flags & AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) != 0) {
180             if (!msg.isEmpty()) { msg += "|"; }
181             msg += "PAUSES_ON_DUCKABLE_LOSS";
182         }
183         return msg;
184     }
185 
dump(PrintWriter pw)186     void dump(PrintWriter pw) {
187         pw.println("  source:" + mSourceRef
188                 + " -- pack: " + mPackageName
189                 + " -- client: " + mClientId
190                 + " -- gain: " + focusGainToString()
191                 + " -- flags: " + flagsToString(mGrantFlags)
192                 + " -- loss: " + focusLossToString()
193                 + " -- uid: " + mCallingUid
194                 + " -- attr: " + mAttributes);
195     }
196 
197 
release()198     void release() {
199         try {
200             if (mSourceRef != null && mDeathHandler != null) {
201                 mSourceRef.unlinkToDeath(mDeathHandler, 0);
202                 mDeathHandler = null;
203             }
204         } catch (java.util.NoSuchElementException e) {
205             Log.e(TAG, "FocusRequester.release() hit ", e);
206         }
207     }
208 
209     @Override
finalize()210     protected void finalize() throws Throwable {
211         release();
212         super.finalize();
213     }
214 
215     /**
216      * For a given audio focus gain request, return the audio focus loss type that will result
217      * from it, taking into account any previous focus loss.
218      * @param gainRequest
219      * @return the audio focus loss type that matches the gain request
220      */
focusLossForGainRequest(int gainRequest)221     private int focusLossForGainRequest(int gainRequest) {
222         switch(gainRequest) {
223             case AudioManager.AUDIOFOCUS_GAIN:
224                 switch(mFocusLossReceived) {
225                     case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
226                     case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
227                     case AudioManager.AUDIOFOCUS_LOSS:
228                     case AudioManager.AUDIOFOCUS_NONE:
229                         return AudioManager.AUDIOFOCUS_LOSS;
230                 }
231             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:
232             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT:
233                 switch(mFocusLossReceived) {
234                     case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
235                     case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
236                     case AudioManager.AUDIOFOCUS_NONE:
237                         return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
238                     case AudioManager.AUDIOFOCUS_LOSS:
239                         return AudioManager.AUDIOFOCUS_LOSS;
240                 }
241             case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:
242                 switch(mFocusLossReceived) {
243                     case AudioManager.AUDIOFOCUS_NONE:
244                     case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
245                         return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK;
246                     case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
247                         return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
248                     case AudioManager.AUDIOFOCUS_LOSS:
249                         return AudioManager.AUDIOFOCUS_LOSS;
250                 }
251             default:
252                 Log.e(TAG, "focusLossForGainRequest() for invalid focus request "+ gainRequest);
253                         return AudioManager.AUDIOFOCUS_NONE;
254         }
255     }
256 
257     /**
258      * Called synchronized on MediaFocusControl.mAudioFocusLock
259      */
handleExternalFocusGain(int focusGain)260     void handleExternalFocusGain(int focusGain) {
261         int focusLoss = focusLossForGainRequest(focusGain);
262         handleFocusLoss(focusLoss);
263     }
264 
265     /**
266      * Called synchronized on MediaFocusControl.mAudioFocusLock
267      */
handleFocusGain(int focusGain)268     void handleFocusGain(int focusGain) {
269         try {
270             mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE;
271             mFocusController.notifyExtPolicyFocusGrant_syncAf(toAudioFocusInfo(),
272                     AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
273             if (mFocusDispatcher != null) {
274                 if (DEBUG) {
275                     Log.v(TAG, "dispatching " + focusChangeToString(focusGain) + " to "
276                         + mClientId);
277                 }
278                 mFocusDispatcher.dispatchAudioFocusChange(focusGain, mClientId);
279             }
280         } catch (android.os.RemoteException e) {
281             Log.e(TAG, "Failure to signal gain of audio focus due to: ", e);
282         }
283     }
284 
285     /**
286      * Called synchronized on MediaFocusControl.mAudioFocusLock
287      */
handleFocusLoss(int focusLoss)288     void handleFocusLoss(int focusLoss) {
289         try {
290             if (focusLoss != mFocusLossReceived) {
291                 mFocusLossReceived = focusLoss;
292                 // before dispatching a focus loss, check if the following conditions are met:
293                 // 1/ the framework is not supposed to notify the focus loser on a DUCK loss
294                 // 2/ it is a DUCK loss
295                 // 3/ the focus loser isn't flagged as pausing in a DUCK loss
296                 // if they are, do not notify the focus loser
297                 if (!mFocusController.mustNotifyFocusOwnerOnDuck()
298                         && mFocusLossReceived == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
299                         && (mGrantFlags
300                                 & AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) == 0) {
301                     if (DEBUG) {
302                         Log.v(TAG, "NOT dispatching " + focusChangeToString(mFocusLossReceived)
303                                 + " to " + mClientId + ", to be handled externally");
304                     }
305                     mFocusController.notifyExtPolicyFocusLoss_syncAf(
306                             toAudioFocusInfo(), false /* wasDispatched */);
307                     return;
308                 }
309                 if (mFocusDispatcher != null) {
310                     if (DEBUG) {
311                         Log.v(TAG, "dispatching " + focusChangeToString(mFocusLossReceived) + " to "
312                             + mClientId);
313                     }
314                     mFocusController.notifyExtPolicyFocusLoss_syncAf(
315                             toAudioFocusInfo(), true /* wasDispatched */);
316                     mFocusDispatcher.dispatchAudioFocusChange(mFocusLossReceived, mClientId);
317                 }
318             }
319         } catch (android.os.RemoteException e) {
320             Log.e(TAG, "Failure to signal loss of audio focus due to:", e);
321         }
322     }
323 
toAudioFocusInfo()324     AudioFocusInfo toAudioFocusInfo() {
325         return new AudioFocusInfo(mAttributes, mClientId, mPackageName,
326                 mFocusGainRequest, mFocusLossReceived, mGrantFlags);
327     }
328 }
329