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