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