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