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