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 static android.media.audiopolicy.Flags.enableFadeManagerConfiguration; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.app.AppOpsManager; 24 import android.content.ContentResolver; 25 import android.content.Context; 26 import android.media.AudioAttributes; 27 import android.media.AudioFocusInfo; 28 import android.media.AudioManager; 29 import android.media.AudioSystem; 30 import android.media.IAudioFocusDispatcher; 31 import android.media.MediaMetrics; 32 import android.media.audiopolicy.AudioPolicy; 33 import android.media.audiopolicy.IAudioPolicyCallback; 34 import android.os.Binder; 35 import android.os.Build; 36 import android.os.Handler; 37 import android.os.HandlerThread; 38 import android.os.IBinder; 39 import android.os.Message; 40 import android.os.RemoteException; 41 import android.provider.Settings; 42 import android.util.Log; 43 44 import com.android.internal.annotations.GuardedBy; 45 import com.android.server.utils.EventLogger; 46 47 import java.io.PrintWriter; 48 import java.text.DateFormat; 49 import java.util.ArrayList; 50 import java.util.Arrays; 51 import java.util.Date; 52 import java.util.HashMap; 53 import java.util.Iterator; 54 import java.util.LinkedList; 55 import java.util.List; 56 import java.util.Map.Entry; 57 import java.util.Set; 58 import java.util.Stack; 59 60 /** 61 * @hide 62 * 63 */ 64 public class MediaFocusControl implements PlayerFocusEnforcer { 65 66 private static final String TAG = "MediaFocusControl"; 67 static final boolean DEBUG = false; 68 69 /** 70 * set to true so the framework enforces ducking itself, without communicating to apps 71 * that they lost focus for most use cases. 72 */ 73 static final boolean ENFORCE_DUCKING = true; 74 /** 75 * set to true to the framework enforces ducking itself only with apps above a given SDK 76 * target level. Is ignored if ENFORCE_DUCKING is false. 77 */ 78 static final boolean ENFORCE_DUCKING_FOR_NEW = true; 79 /** 80 * the SDK level (included) up to which the framework doesn't enforce ducking itself. Is ignored 81 * if ENFORCE_DUCKING_FOR_NEW is false; 82 */ 83 // automatic ducking was introduced for Android O 84 static final int DUCKING_IN_APP_SDK_LEVEL = Build.VERSION_CODES.N_MR1; 85 /** 86 * set to true so the framework enforces muting media/game itself when the device is ringing 87 * or in a call. 88 */ 89 static final boolean ENFORCE_MUTING_FOR_RING_OR_CALL = true; 90 91 /** 92 * set to true so the framework enforces fading out apps that lose audio focus in a 93 * non-transient way. 94 */ 95 static final boolean ENFORCE_FADEOUT_FOR_FOCUS_LOSS = true; 96 97 private final Context mContext; 98 private final AppOpsManager mAppOps; 99 private final @NonNull PlayerFocusEnforcer mFocusEnforcer; 100 private boolean mMultiAudioFocusEnabled = false; 101 102 private boolean mRingOrCallActive = false; 103 104 private final Object mExtFocusChangeLock = new Object(); 105 @GuardedBy("mExtFocusChangeLock") 106 private long mExtFocusChangeCounter; 107 MediaFocusControl(Context cntxt, PlayerFocusEnforcer pfe)108 protected MediaFocusControl(Context cntxt, PlayerFocusEnforcer pfe) { 109 mContext = cntxt; 110 mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE); 111 mFocusEnforcer = pfe; 112 final ContentResolver cr = mContext.getContentResolver(); 113 mMultiAudioFocusEnabled = Settings.System.getIntForUser(cr, 114 Settings.System.MULTI_AUDIO_FOCUS_ENABLED, 0, cr.getUserId()) != 0; 115 initFocusThreading(); 116 } 117 dump(PrintWriter pw)118 protected void dump(PrintWriter pw) { 119 pw.println("\nMediaFocusControl dump time: " 120 + DateFormat.getTimeInstance().format(new Date())); 121 dumpFocusStack(pw); 122 pw.println("\n"); 123 // log 124 mEventLogger.dump(pw); 125 dumpMultiAudioFocus(pw); 126 } 127 128 /** 129 * Test method to return the duration of the fade out applied on the players of a focus loser 130 * @return the fade out duration in ms 131 */ getFocusFadeOutDurationForTest()132 public long getFocusFadeOutDurationForTest() { 133 return getFadeOutDurationMillis( 134 new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build()); 135 } 136 137 /** 138 * Test method to return the length of time after a fade out before the focus loser is unmuted 139 * (and is faded back in). 140 * @return the time gap after a fade out completion on focus loss, and fade in start in ms 141 */ getFocusUnmuteDelayAfterFadeOutForTest()142 public long getFocusUnmuteDelayAfterFadeOutForTest() { 143 return getFadeInDelayForOffendersMillis( 144 new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build()); 145 } 146 147 //================================================================= 148 // PlayerFocusEnforcer implementation 149 @Override duckPlayers(@onNull FocusRequester winner, @NonNull FocusRequester loser, boolean forceDuck)150 public boolean duckPlayers(@NonNull FocusRequester winner, @NonNull FocusRequester loser, 151 boolean forceDuck) { 152 return mFocusEnforcer.duckPlayers(winner, loser, forceDuck); 153 } 154 155 @Override restoreVShapedPlayers(@onNull FocusRequester winner)156 public void restoreVShapedPlayers(@NonNull FocusRequester winner) { 157 mFocusEnforcer.restoreVShapedPlayers(winner); 158 // remove scheduled events to unfade out offending players (if any) corresponding to 159 // this uid, as we're removing any effects of muting/ducking/fade out now 160 mFocusHandler.removeEqualMessages(MSL_L_FORGET_UID, 161 new ForgetFadeUidInfo(winner.getClientUid())); 162 163 } 164 165 @Override mutePlayersForCall(int[] usagesToMute)166 public void mutePlayersForCall(int[] usagesToMute) { 167 mFocusEnforcer.mutePlayersForCall(usagesToMute); 168 } 169 170 @Override unmutePlayersForCall()171 public void unmutePlayersForCall() { 172 mFocusEnforcer.unmutePlayersForCall(); 173 } 174 175 @Override fadeOutPlayers(@onNull FocusRequester winner, @NonNull FocusRequester loser)176 public boolean fadeOutPlayers(@NonNull FocusRequester winner, @NonNull FocusRequester loser) { 177 return mFocusEnforcer.fadeOutPlayers(winner, loser); 178 } 179 180 @Override forgetUid(int uid)181 public void forgetUid(int uid) { 182 mFocusEnforcer.forgetUid(uid); 183 } 184 185 @Override getFadeOutDurationMillis(@onNull AudioAttributes aa)186 public long getFadeOutDurationMillis(@NonNull AudioAttributes aa) { 187 if (aa == null) { 188 return 0; 189 } 190 return mFocusEnforcer.getFadeOutDurationMillis(aa); 191 } 192 193 @Override getFadeInDelayForOffendersMillis(@onNull AudioAttributes aa)194 public long getFadeInDelayForOffendersMillis(@NonNull AudioAttributes aa) { 195 if (aa == null) { 196 return 0; 197 } 198 return mFocusEnforcer.getFadeInDelayForOffendersMillis(aa); 199 } 200 201 @Override shouldEnforceFade()202 public boolean shouldEnforceFade() { 203 if (!enableFadeManagerConfiguration()) { 204 return ENFORCE_FADEOUT_FOR_FOCUS_LOSS; 205 } 206 207 return mFocusEnforcer.shouldEnforceFade(); 208 } 209 //========================================================================================== 210 // AudioFocus 211 //========================================================================================== 212 213 private final static Object mAudioFocusLock = new Object(); 214 215 /** 216 * Arbitrary maximum size of audio focus stack to prevent apps OOM'ing this process. 217 */ 218 private static final int MAX_STACK_SIZE = 100; 219 220 private static final EventLogger 221 mEventLogger = new EventLogger(50, 222 "focus commands as seen by MediaFocusControl"); 223 224 private static final String mMetricsId = MediaMetrics.Name.AUDIO_FOCUS; 225 noFocusForSuspendedApp(@onNull String packageName, int uid)226 /*package*/ void noFocusForSuspendedApp(@NonNull String packageName, int uid) { 227 synchronized (mAudioFocusLock) { 228 final Iterator<FocusRequester> stackIterator = mFocusStack.iterator(); 229 List<String> clientsToRemove = new ArrayList<>(); 230 while (stackIterator.hasNext()) { 231 final FocusRequester focusOwner = stackIterator.next(); 232 if (focusOwner.hasSameUid(uid) && focusOwner.hasSamePackage(packageName)) { 233 clientsToRemove.add(focusOwner.getClientId()); 234 mEventLogger.enqueue((new EventLogger.StringEvent( 235 "focus owner:" + focusOwner.getClientId() 236 + " in uid:" + uid + " pack: " + packageName 237 + " getting AUDIOFOCUS_LOSS due to app suspension")) 238 .printLog(TAG)); 239 // make the suspended app lose focus through its focus listener (if any) 240 focusOwner.dispatchFocusChange(AudioManager.AUDIOFOCUS_LOSS); 241 } 242 } 243 for (String clientToRemove : clientsToRemove) { 244 // update the stack but don't signal the change. 245 removeFocusStackEntry(clientToRemove, false, true); 246 } 247 } 248 } 249 hasAudioFocusUsers()250 /*package*/ boolean hasAudioFocusUsers() { 251 synchronized (mAudioFocusLock) { 252 return !mFocusStack.empty(); 253 } 254 } 255 256 /** 257 * Discard the current audio focus owner (unless the user is considered {@link 258 * FocusRequester#isAlwaysVisibleUser() always visible)}. 259 * Notify top of audio focus stack that it lost focus (regardless of possibility to reassign 260 * focus), remove it from the stack, and clear the remote control display. 261 * @return whether the current audio focus owner was discarded (including if there was none); 262 * returns false if it was purposefully kept 263 */ maybeDiscardAudioFocusOwner()264 protected boolean maybeDiscardAudioFocusOwner() { 265 synchronized(mAudioFocusLock) { 266 if (!mFocusStack.empty()) { 267 final FocusRequester exFocusOwner = mFocusStack.peek(); 268 if (!exFocusOwner.isAlwaysVisibleUser()) { 269 mFocusStack.pop(); 270 exFocusOwner.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null, 271 false /*forceDuck*/); 272 exFocusOwner.release(); 273 return true; 274 } else { 275 return false; 276 } 277 } 278 } 279 return true; 280 } 281 282 /** 283 * Return a copy of the focus stack for external consumption (composed of AudioFocusInfo 284 * instead of FocusRequester instances) 285 * @return a SystemApi-friendly version of the focus stack, in the same order (last entry 286 * is top of focus stack, i.e. latest focus owner) 287 * @see AudioPolicy#getFocusStack() 288 */ getFocusStack()289 @NonNull List<AudioFocusInfo> getFocusStack() { 290 synchronized (mAudioFocusLock) { 291 final ArrayList<AudioFocusInfo> stack = new ArrayList<>(mFocusStack.size()); 292 for (FocusRequester fr : mFocusStack) { 293 stack.add(fr.toAudioFocusInfo()); 294 } 295 return stack; 296 } 297 } 298 299 /** 300 * Return the UID of the focus owner that has focus with exclusive focus gain 301 * @return -1 if nobody has exclusive focus, the UID of the owner otherwise 302 */ getExclusiveFocusOwnerUid()303 protected int getExclusiveFocusOwnerUid() { 304 synchronized (mAudioFocusLock) { 305 if (mFocusStack.empty()) { 306 return -1; 307 } 308 final FocusRequester owner = mFocusStack.peek(); 309 if (owner.getGainRequest() != AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE) { 310 return -1; 311 } 312 return owner.getClientUid(); 313 } 314 } 315 316 /** 317 * Send AUDIOFOCUS_LOSS to a specific stack entry. 318 * Note this method is supporting an external API, and is restricted to LOSS in order to 319 * prevent allowing the stack to be in an invalid state (e.g. entry inside stack has focus) 320 * @param focusLoser the stack entry that is exiting the stack through a focus loss 321 * @return false if the focusLoser wasn't found in the stack, true otherwise 322 * @see AudioPolicy#sendFocusLoss(AudioFocusInfo) 323 */ sendFocusLoss(@onNull AudioFocusInfo focusLoser)324 boolean sendFocusLoss(@NonNull AudioFocusInfo focusLoser) { 325 synchronized (mAudioFocusLock) { 326 FocusRequester loserToRemove = null; 327 for (FocusRequester fr : mFocusStack) { 328 if (fr.getClientId().equals(focusLoser.getClientId())) { 329 fr.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null, 330 false /*forceDuck*/); 331 loserToRemove = fr; 332 break; 333 } 334 } 335 if (loserToRemove != null) { 336 mFocusStack.remove(loserToRemove); 337 loserToRemove.release(); 338 return true; 339 } 340 } 341 return false; 342 } 343 344 @GuardedBy("mAudioFocusLock") notifyTopOfAudioFocusStack()345 private void notifyTopOfAudioFocusStack() { 346 // notify the top of the stack it gained focus 347 if (!mFocusStack.empty()) { 348 if (canReassignAudioFocus()) { 349 mFocusStack.peek().handleFocusGain(AudioManager.AUDIOFOCUS_GAIN); 350 } 351 } 352 353 if (mMultiAudioFocusEnabled && !mMultiAudioFocusList.isEmpty()) { 354 for (FocusRequester multifr : mMultiAudioFocusList) { 355 if (isLockedFocusOwner(multifr)) { 356 multifr.handleFocusGain(AudioManager.AUDIOFOCUS_GAIN); 357 } 358 } 359 } 360 } 361 362 /** 363 * Focus is requested, propagate the associated loss throughout the stack. 364 * Will also remove entries in the stack that have just received a definitive loss of focus. 365 * @param focusGain the new focus gain that will later be added at the top of the stack 366 */ 367 @GuardedBy("mAudioFocusLock") propagateFocusLossFromGain_syncAf(int focusGain, final FocusRequester fr, boolean forceDuck)368 private void propagateFocusLossFromGain_syncAf(int focusGain, final FocusRequester fr, 369 boolean forceDuck) { 370 if (DEBUG) { 371 Log.i(TAG, "propagateFocusLossFromGain_syncAf gain:" + focusGain); 372 } 373 final List<String> clientsToRemove = new LinkedList<String>(); 374 // going through the audio focus stack to signal new focus, traversing order doesn't 375 // matter as all entries respond to the same external focus gain 376 if (!mFocusStack.empty()) { 377 for (FocusRequester focusLoser : mFocusStack) { 378 if (DEBUG) { 379 Log.i(TAG, "propagateFocusLossFromGain_syncAf checking client:" 380 + focusLoser.getClientId()); 381 } 382 final boolean isDefinitiveLoss = 383 focusLoser.handleFocusLossFromGain(focusGain, fr, forceDuck); 384 if (isDefinitiveLoss) { 385 clientsToRemove.add(focusLoser.getClientId()); 386 } 387 } 388 } else if (DEBUG) { 389 Log.i(TAG, "propagateFocusLossFromGain_syncAf empty stack"); 390 } 391 392 if (mMultiAudioFocusEnabled && !mMultiAudioFocusList.isEmpty()) { 393 for (FocusRequester multifocusLoser : mMultiAudioFocusList) { 394 final boolean isDefinitiveLoss = 395 multifocusLoser.handleFocusLossFromGain(focusGain, fr, forceDuck); 396 if (isDefinitiveLoss) { 397 clientsToRemove.add(multifocusLoser.getClientId()); 398 } 399 } 400 } 401 402 for (String clientToRemove : clientsToRemove) { 403 removeFocusStackEntry(clientToRemove, false /*signal*/, 404 true /*notifyFocusFollowers*/); 405 } 406 } 407 408 private final Stack<FocusRequester> mFocusStack = new Stack<FocusRequester>(); 409 410 ArrayList<FocusRequester> mMultiAudioFocusList = new ArrayList<FocusRequester>(); 411 412 /** 413 * Helper function: 414 * Display in the log the current entries in the audio focus stack 415 */ dumpFocusStack(PrintWriter pw)416 private void dumpFocusStack(PrintWriter pw) { 417 pw.println("\nAudio Focus stack entries (last is top of stack):"); 418 synchronized(mAudioFocusLock) { 419 Iterator<FocusRequester> stackIterator = mFocusStack.iterator(); 420 while(stackIterator.hasNext()) { 421 stackIterator.next().dump(pw); 422 } 423 pw.println("\n"); 424 if (mFocusPolicy == null) { 425 pw.println("No external focus policy\n"); 426 } else { 427 pw.println("External focus policy: "+ mFocusPolicy + ", focus owners:\n"); 428 dumpExtFocusPolicyFocusOwners(pw); 429 } 430 } 431 pw.println("\n"); 432 pw.println(" Notify on duck: " + mNotifyFocusOwnerOnDuck + "\n"); 433 pw.println(" In ring or call: " + mRingOrCallActive + "\n"); 434 } 435 436 /** 437 * Remove a focus listener from the focus stack. 438 * @param clientToRemove the focus listener 439 * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding 440 * focus, notify the next item in the stack it gained focus. 441 */ 442 @GuardedBy("mAudioFocusLock") removeFocusStackEntry(String clientToRemove, boolean signal, boolean notifyFocusFollowers)443 private void removeFocusStackEntry(String clientToRemove, boolean signal, 444 boolean notifyFocusFollowers) { 445 if (DEBUG) { 446 Log.i(TAG, "removeFocusStackEntry client:" + clientToRemove); 447 } 448 AudioFocusInfo abandonSource = null; 449 // is the current top of the focus stack abandoning focus? (because of request, not death) 450 if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientToRemove)) 451 { 452 //Log.i(TAG, " removeFocusStackEntry() removing top of stack"); 453 FocusRequester fr = mFocusStack.pop(); 454 fr.maybeRelease(); 455 if (notifyFocusFollowers) { 456 abandonSource = fr.toAudioFocusInfo(); 457 } 458 if (signal) { 459 // notify the new top of the stack it gained focus 460 notifyTopOfAudioFocusStack(); 461 } 462 } else { 463 // focus is abandoned by a client that's not at the top of the stack, 464 // no need to update focus. 465 // (using an iterator on the stack so we can safely remove an entry after having 466 // evaluated it, traversal order doesn't matter here) 467 Iterator<FocusRequester> stackIterator = mFocusStack.iterator(); 468 while(stackIterator.hasNext()) { 469 FocusRequester fr = stackIterator.next(); 470 if(fr.hasSameClient(clientToRemove)) { 471 Log.i(TAG, "AudioFocus removeFocusStackEntry(): removing entry for " 472 + clientToRemove); 473 stackIterator.remove(); 474 if (notifyFocusFollowers) { 475 abandonSource = fr.toAudioFocusInfo(); 476 } 477 // stack entry not used anymore, clear references 478 fr.maybeRelease(); 479 } 480 } 481 } 482 // focus followers still want to know focus was abandoned, handled as a loss 483 if (abandonSource != null) { 484 abandonSource.clearLossReceived(); 485 notifyExtPolicyFocusLoss_syncAf(abandonSource, false); 486 } 487 488 if (mMultiAudioFocusEnabled && !mMultiAudioFocusList.isEmpty()) { 489 Iterator<FocusRequester> listIterator = mMultiAudioFocusList.iterator(); 490 while (listIterator.hasNext()) { 491 FocusRequester fr = listIterator.next(); 492 if (fr.hasSameClient(clientToRemove)) { 493 listIterator.remove(); 494 fr.release(); 495 } 496 } 497 498 if (signal) { 499 // notify the new top of the stack it gained focus 500 notifyTopOfAudioFocusStack(); 501 } 502 } 503 } 504 505 /** 506 * Remove focus listeners from the focus stack for a particular client when it has died. 507 */ 508 @GuardedBy("mAudioFocusLock") removeFocusStackEntryOnDeath(IBinder cb)509 private void removeFocusStackEntryOnDeath(IBinder cb) { 510 // is the owner of the audio focus part of the client to remove? 511 boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() && 512 mFocusStack.peek().hasSameBinder(cb); 513 // (using an iterator on the stack so we can safely remove an entry after having 514 // evaluated it, traversal order doesn't matter here) 515 Iterator<FocusRequester> stackIterator = mFocusStack.iterator(); 516 while(stackIterator.hasNext()) { 517 FocusRequester fr = stackIterator.next(); 518 if(fr.hasSameBinder(cb)) { 519 Log.i(TAG, "AudioFocus removeFocusStackEntryOnDeath(): removing entry for " + cb); 520 mEventLogger.enqueue(new EventLogger.StringEvent( 521 "focus requester:" + fr.getClientId() 522 + " in uid:" + fr.getClientUid() 523 + " pack:" + fr.getPackageName() 524 + " died")); 525 notifyExtPolicyFocusLoss_syncAf(fr.toAudioFocusInfo(), false); 526 527 stackIterator.remove(); 528 // stack entry not used anymore, clear references 529 fr.release(); 530 } 531 } 532 if (isTopOfStackForClientToRemove) { 533 // we removed an entry at the top of the stack: 534 // notify the new top of the stack it gained focus. 535 notifyTopOfAudioFocusStack(); 536 } 537 } 538 539 /** 540 * Helper function for external focus policy: 541 * Remove focus listeners from the list of potential focus owners for a particular client when 542 * it has died. 543 */ 544 @GuardedBy("mAudioFocusLock") removeFocusEntryForExtPolicyOnDeath(IBinder cb)545 private void removeFocusEntryForExtPolicyOnDeath(IBinder cb) { 546 if (mFocusOwnersForFocusPolicy.isEmpty()) { 547 return; 548 } 549 boolean released = false; 550 final Set<Entry<String, FocusRequester>> owners = mFocusOwnersForFocusPolicy.entrySet(); 551 final Iterator<Entry<String, FocusRequester>> ownerIterator = owners.iterator(); 552 while (ownerIterator.hasNext()) { 553 final Entry<String, FocusRequester> owner = ownerIterator.next(); 554 final FocusRequester fr = owner.getValue(); 555 if (fr.hasSameBinder(cb)) { 556 ownerIterator.remove(); 557 mEventLogger.enqueue(new EventLogger.StringEvent( 558 "focus requester:" + fr.getClientId() 559 + " in uid:" + fr.getClientUid() 560 + " pack:" + fr.getPackageName() 561 + " died")); 562 fr.release(); 563 notifyExtFocusPolicyFocusAbandon_syncAf(fr.toAudioFocusInfo()); 564 break; 565 } 566 } 567 } 568 569 /** 570 * Helper function: 571 * Returns true if the system is in a state where the focus can be reevaluated, false otherwise. 572 * The implementation guarantees that a state where focus cannot be immediately reassigned 573 * implies that an "locked" focus owner is at the top of the focus stack. 574 * Modifications to the implementation that break this assumption will cause focus requests to 575 * misbehave when honoring the AudioManager.AUDIOFOCUS_FLAG_DELAY_OK flag. 576 */ canReassignAudioFocus()577 private boolean canReassignAudioFocus() { 578 // focus requests are rejected during a phone call or when the phone is ringing 579 // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus 580 if (!mFocusStack.isEmpty() && isLockedFocusOwner(mFocusStack.peek())) { 581 return false; 582 } 583 return true; 584 } 585 isLockedFocusOwner(FocusRequester fr)586 private boolean isLockedFocusOwner(FocusRequester fr) { 587 return (fr.hasSameClient(AudioSystem.IN_VOICE_COMM_FOCUS_ID) || fr.isLockedFocusOwner()); 588 } 589 590 /** 591 * Helper function 592 * Pre-conditions: focus stack is not empty, there is one or more locked focus owner 593 * at the top of the focus stack 594 * Push the focus requester onto the audio focus stack at the first position immediately 595 * following the locked focus owners. 596 * Propagate through the stack the changes that the new (future) focus owner causes. 597 * @param nfr the future focus owner that will gain focus when the locked focus owners are 598 * removed from the stack 599 * @return {@link AudioManager#AUDIOFOCUS_REQUEST_GRANTED} or 600 * {@link AudioManager#AUDIOFOCUS_REQUEST_DELAYED} 601 */ 602 @GuardedBy("mAudioFocusLock") pushBelowLockedFocusOwnersAndPropagate(FocusRequester nfr)603 private int pushBelowLockedFocusOwnersAndPropagate(FocusRequester nfr) { 604 if (DEBUG) { 605 Log.v(TAG, "pushBelowLockedFocusOwnersAndPropagate client=" + nfr.getClientId()); 606 } 607 int lastLockedFocusOwnerIndex = mFocusStack.size(); 608 for (int index = mFocusStack.size() - 1; index >= 0; index--) { 609 if (isLockedFocusOwner(mFocusStack.elementAt(index))) { 610 lastLockedFocusOwnerIndex = index; 611 } 612 } 613 if (lastLockedFocusOwnerIndex == mFocusStack.size()) { 614 // this should not happen, but handle it and log an error 615 Log.e(TAG, "No exclusive focus owner found in propagateFocusLossFromGain_syncAf()", 616 new Exception()); 617 // no exclusive owner, push at top of stack, focus is granted, propagate change 618 propagateFocusLossFromGain_syncAf(nfr.getGainRequest(), nfr, false /*forceDuck*/); 619 mFocusStack.push(nfr); 620 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 621 } 622 623 if (DEBUG) { 624 Log.v(TAG, "> lastLockedFocusOwnerIndex=" + lastLockedFocusOwnerIndex); 625 } 626 mFocusStack.insertElementAt(nfr, lastLockedFocusOwnerIndex); 627 628 // propagate potential focus loss (and removal from stack) after the newly 629 // inserted FocusRequester (at index lastLockedFocusOwnerIndex-1) 630 final List<String> clientsToRemove = new LinkedList<String>(); 631 for (int index = lastLockedFocusOwnerIndex - 1; index >= 0; index--) { 632 final boolean isDefinitiveLoss = 633 mFocusStack.elementAt(index).handleFocusLossFromGain( 634 nfr.getGainRequest(), nfr, false /*forceDuck*/); 635 if (isDefinitiveLoss) { 636 clientsToRemove.add(mFocusStack.elementAt(index).getClientId()); 637 } 638 } 639 for (String clientToRemove : clientsToRemove) { 640 if (DEBUG) { 641 Log.v(TAG, "> removing focus client " + clientToRemove); 642 } 643 removeFocusStackEntry(clientToRemove, false /*signal*/, 644 true /*notifyFocusFollowers*/); 645 } 646 647 return AudioManager.AUDIOFOCUS_REQUEST_DELAYED; 648 } 649 650 /** 651 * Inner class to monitor audio focus client deaths, and remove them from the audio focus 652 * stack if necessary. 653 */ 654 protected class AudioFocusDeathHandler implements IBinder.DeathRecipient { 655 private IBinder mCb; // To be notified of client's death 656 AudioFocusDeathHandler(IBinder cb)657 AudioFocusDeathHandler(IBinder cb) { 658 mCb = cb; 659 } 660 binderDied()661 public void binderDied() { 662 synchronized(mAudioFocusLock) { 663 if (mFocusPolicy != null) { 664 removeFocusEntryForExtPolicyOnDeath(mCb); 665 } else { 666 removeFocusStackEntryOnDeath(mCb); 667 if (mMultiAudioFocusEnabled && !mMultiAudioFocusList.isEmpty()) { 668 Iterator<FocusRequester> listIterator = mMultiAudioFocusList.iterator(); 669 while (listIterator.hasNext()) { 670 FocusRequester fr = listIterator.next(); 671 if (fr.hasSameBinder(mCb)) { 672 listIterator.remove(); 673 fr.release(); 674 } 675 } 676 } 677 } 678 } 679 } 680 } 681 682 /** 683 * Indicates whether to notify an audio focus owner when it loses focus 684 * with {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK} if it will only duck. 685 * This variable being false indicates an AudioPolicy has been registered and has signaled 686 * it will handle audio ducking. 687 */ 688 private boolean mNotifyFocusOwnerOnDuck = true; 689 setDuckingInExtPolicyAvailable(boolean available)690 protected void setDuckingInExtPolicyAvailable(boolean available) { 691 mNotifyFocusOwnerOnDuck = !available; 692 } 693 mustNotifyFocusOwnerOnDuck()694 boolean mustNotifyFocusOwnerOnDuck() { return mNotifyFocusOwnerOnDuck; } 695 696 private ArrayList<IAudioPolicyCallback> mFocusFollowers = new ArrayList<IAudioPolicyCallback>(); 697 addFocusFollower(IAudioPolicyCallback ff)698 void addFocusFollower(IAudioPolicyCallback ff) { 699 if (ff == null) { 700 return; 701 } 702 synchronized(mAudioFocusLock) { 703 boolean found = false; 704 for (IAudioPolicyCallback pcb : mFocusFollowers) { 705 if (pcb.asBinder().equals(ff.asBinder())) { 706 found = true; 707 break; 708 } 709 } 710 if (found) { 711 return; 712 } else { 713 mFocusFollowers.add(ff); 714 notifyExtPolicyCurrentFocusAsync(ff); 715 } 716 } 717 } 718 removeFocusFollower(IAudioPolicyCallback ff)719 void removeFocusFollower(IAudioPolicyCallback ff) { 720 if (ff == null) { 721 return; 722 } 723 synchronized(mAudioFocusLock) { 724 for (IAudioPolicyCallback pcb : mFocusFollowers) { 725 if (pcb.asBinder().equals(ff.asBinder())) { 726 mFocusFollowers.remove(pcb); 727 break; 728 } 729 } 730 } 731 } 732 733 /** The current audio focus policy */ 734 @GuardedBy("mAudioFocusLock") 735 @Nullable private IAudioPolicyCallback mFocusPolicy = null; 736 /** 737 * The audio focus policy that was registered before a test focus policy was registered 738 * during a test 739 */ 740 @GuardedBy("mAudioFocusLock") 741 @Nullable private IAudioPolicyCallback mPreviousFocusPolicy = null; 742 743 // Since we don't have a stack of focus owners when using an external focus policy, we keep 744 // track of all the focus requesters in this map, with their clientId as the key. This is 745 // used both for focus dispatch and death handling 746 private HashMap<String, FocusRequester> mFocusOwnersForFocusPolicy = 747 new HashMap<String, FocusRequester>(); 748 setFocusPolicy(IAudioPolicyCallback policy, boolean isTestFocusPolicy)749 void setFocusPolicy(IAudioPolicyCallback policy, boolean isTestFocusPolicy) { 750 if (policy == null) { 751 return; 752 } 753 synchronized (mAudioFocusLock) { 754 if (isTestFocusPolicy) { 755 mPreviousFocusPolicy = mFocusPolicy; 756 } 757 mFocusPolicy = policy; 758 } 759 } 760 unsetFocusPolicy(IAudioPolicyCallback policy, boolean isTestFocusPolicy)761 void unsetFocusPolicy(IAudioPolicyCallback policy, boolean isTestFocusPolicy) { 762 if (policy == null) { 763 return; 764 } 765 synchronized (mAudioFocusLock) { 766 if (mFocusPolicy == policy) { 767 if (isTestFocusPolicy) { 768 // restore the focus policy that was there before the focus policy test started 769 mFocusPolicy = mPreviousFocusPolicy; 770 } else { 771 mFocusPolicy = null; 772 } 773 } 774 } 775 } 776 777 /** 778 * @param pcb non null 779 */ notifyExtPolicyCurrentFocusAsync(IAudioPolicyCallback pcb)780 void notifyExtPolicyCurrentFocusAsync(IAudioPolicyCallback pcb) { 781 final IAudioPolicyCallback pcb2 = pcb; 782 final Thread thread = new Thread() { 783 @Override 784 public void run() { 785 synchronized(mAudioFocusLock) { 786 if (mFocusStack.isEmpty()) { 787 return; 788 } 789 try { 790 pcb2.notifyAudioFocusGrant(mFocusStack.peek().toAudioFocusInfo(), 791 // top of focus stack always has focus 792 AudioManager.AUDIOFOCUS_REQUEST_GRANTED); 793 } catch (RemoteException e) { 794 Log.e(TAG, "Can't call notifyAudioFocusGrant() on IAudioPolicyCallback " 795 + pcb2.asBinder(), e); 796 } 797 } 798 } 799 }; 800 thread.start(); 801 } 802 803 /** 804 * Called synchronized on mAudioFocusLock 805 */ notifyExtPolicyFocusGrant_syncAf(AudioFocusInfo afi, int requestResult)806 void notifyExtPolicyFocusGrant_syncAf(AudioFocusInfo afi, int requestResult) { 807 for (IAudioPolicyCallback pcb : mFocusFollowers) { 808 try { 809 // oneway 810 pcb.notifyAudioFocusGrant(afi, requestResult); 811 } catch (RemoteException e) { 812 Log.e(TAG, "Can't call notifyAudioFocusGrant() on IAudioPolicyCallback " 813 + pcb.asBinder(), e); 814 } 815 } 816 } 817 818 /** 819 * Called synchronized on mAudioFocusLock 820 */ notifyExtPolicyFocusLoss_syncAf(AudioFocusInfo afi, boolean wasDispatched)821 void notifyExtPolicyFocusLoss_syncAf(AudioFocusInfo afi, boolean wasDispatched) { 822 for (IAudioPolicyCallback pcb : mFocusFollowers) { 823 try { 824 // oneway 825 pcb.notifyAudioFocusLoss(afi, wasDispatched); 826 } catch (RemoteException e) { 827 Log.e(TAG, "Can't call notifyAudioFocusLoss() on IAudioPolicyCallback " 828 + pcb.asBinder(), e); 829 } 830 } 831 } 832 833 /** 834 * Called synchronized on mAudioFocusLock. 835 * Can only be called with an external focus policy installed (mFocusPolicy != null) 836 * @param afi 837 * @param fd 838 * @param cb binder of the focus requester 839 * @return true if the external audio focus policy (if any) can handle the focus request, 840 * and false if there was any error handling the request (e.g. error talking to policy, 841 * focus requester is already dead) 842 */ notifyExtFocusPolicyFocusRequest_syncAf(AudioFocusInfo afi, IAudioFocusDispatcher fd, @NonNull IBinder cb)843 boolean notifyExtFocusPolicyFocusRequest_syncAf(AudioFocusInfo afi, 844 IAudioFocusDispatcher fd, @NonNull IBinder cb) { 845 if (DEBUG) { 846 Log.v(TAG, "notifyExtFocusPolicyFocusRequest client="+afi.getClientId() 847 + " dispatcher=" + fd); 848 } 849 synchronized (mExtFocusChangeLock) { 850 afi.setGen(mExtFocusChangeCounter++); 851 } 852 final FocusRequester existingFr = mFocusOwnersForFocusPolicy.get(afi.getClientId()); 853 boolean keepTrack = false; 854 if (existingFr != null) { 855 if (!existingFr.hasSameDispatcher(fd)) { 856 existingFr.release(); 857 keepTrack = true; 858 } 859 } else { 860 keepTrack = true; 861 } 862 if (keepTrack) { 863 final AudioFocusDeathHandler hdlr = new AudioFocusDeathHandler(cb); 864 try { 865 cb.linkToDeath(hdlr, 0); 866 } catch (RemoteException e) { 867 // client has already died! 868 return false; 869 } 870 // new focus (future) focus owner to keep track of 871 mFocusOwnersForFocusPolicy.put(afi.getClientId(), 872 new FocusRequester(afi, fd, cb, hdlr, this)); 873 } 874 875 try { 876 //oneway 877 mFocusPolicy.notifyAudioFocusRequest(afi, AudioManager.AUDIOFOCUS_REQUEST_GRANTED); 878 return true; 879 } catch (RemoteException e) { 880 Log.e(TAG, "Can't call notifyAudioFocusRequest() on IAudioPolicyCallback " 881 + mFocusPolicy.asBinder(), e); 882 } 883 return false; 884 } 885 setFocusRequestResultFromExtPolicy(AudioFocusInfo afi, int requestResult)886 void setFocusRequestResultFromExtPolicy(AudioFocusInfo afi, int requestResult) { 887 synchronized (mExtFocusChangeLock) { 888 if (afi.getGen() > mExtFocusChangeCounter) { 889 return; 890 } 891 } 892 synchronized (mAudioFocusLock) { 893 FocusRequester fr = getFocusRequesterLocked(afi.getClientId(), 894 /* shouldRemove= */ requestResult == AudioManager.AUDIOFOCUS_REQUEST_FAILED); 895 if (fr != null) { 896 fr.dispatchFocusResultFromExtPolicy(requestResult); 897 // if fade is enabled for external focus policies, apply it when setting 898 // focus result as well 899 if (enableFadeManagerConfiguration()) { 900 fr.handleFocusGainFromRequest(requestResult); 901 } 902 } 903 } 904 } 905 906 /** 907 * Called synchronized on mAudioFocusLock 908 * @param afi 909 * @return true if the external audio focus policy (if any) is handling the focus request 910 */ notifyExtFocusPolicyFocusAbandon_syncAf(AudioFocusInfo afi)911 boolean notifyExtFocusPolicyFocusAbandon_syncAf(AudioFocusInfo afi) { 912 if (mFocusPolicy == null) { 913 return false; 914 } 915 final FocusRequester fr = mFocusOwnersForFocusPolicy.remove(afi.getClientId()); 916 if (fr != null) { 917 fr.release(); 918 } 919 try { 920 //oneway 921 mFocusPolicy.notifyAudioFocusAbandon(afi); 922 } catch (RemoteException e) { 923 Log.e(TAG, "Can't call notifyAudioFocusAbandon() on IAudioPolicyCallback " 924 + mFocusPolicy.asBinder(), e); 925 } 926 return true; 927 } 928 929 /** see AudioManager.dispatchFocusChange(AudioFocusInfo afi, int focusChange, AudioPolicy ap) */ dispatchFocusChange(AudioFocusInfo afi, int focusChange)930 int dispatchFocusChange(AudioFocusInfo afi, int focusChange) { 931 if (DEBUG) { 932 Log.v(TAG, "dispatchFocusChange " + focusChange + " to afi client=" 933 + afi.getClientId()); 934 } 935 synchronized (mAudioFocusLock) { 936 FocusRequester fr = getFocusRequesterLocked(afi.getClientId(), 937 /* shouldRemove= */ focusChange == AudioManager.AUDIOFOCUS_LOSS); 938 if (fr == null) { 939 if (DEBUG) { 940 Log.v(TAG, "> failed: no such focus requester known"); 941 } 942 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 943 } 944 return fr.dispatchFocusChange(focusChange); 945 } 946 } 947 dispatchFocusChangeWithFade(AudioFocusInfo afi, int focusChange, List<AudioFocusInfo> otherActiveAfis)948 int dispatchFocusChangeWithFade(AudioFocusInfo afi, int focusChange, 949 List<AudioFocusInfo> otherActiveAfis) { 950 if (DEBUG) { 951 Log.v(TAG, "dispatchFocusChangeWithFade " + AudioManager.audioFocusToString(focusChange) 952 + " to afi client=" + afi.getClientId() 953 + " other active afis=" + otherActiveAfis); 954 } 955 956 synchronized (mAudioFocusLock) { 957 String clientId = afi.getClientId(); 958 // do not remove the entry since it can be posted for fade 959 FocusRequester fr = getFocusRequesterLocked(clientId, /* shouldRemove= */ false); 960 if (fr == null) { 961 if (DEBUG) { 962 Log.v(TAG, "> failed: no such focus requester known"); 963 } 964 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 965 } 966 967 // convert other AudioFocusInfo to corresponding FocusRequester 968 ArrayList<FocusRequester> otherActiveFrs = new ArrayList<>(); 969 for (int index = 0; index < otherActiveAfis.size(); index++) { 970 FocusRequester otherFr = getFocusRequesterLocked( 971 otherActiveAfis.get(index).getClientId(), /* shouldRemove= */ false); 972 if (otherFr == null) { 973 continue; 974 } 975 otherActiveFrs.add(otherFr); 976 } 977 978 int status = fr.dispatchFocusChangeWithFadeLocked(focusChange, otherActiveFrs); 979 if (status != AudioManager.AUDIOFOCUS_REQUEST_DELAYED 980 && focusChange == AudioManager.AUDIOFOCUS_LOSS) { 981 mFocusOwnersForFocusPolicy.remove(clientId); 982 } 983 984 return status; 985 } 986 } 987 988 @GuardedBy("mAudioFocusLock") getFocusRequesterLocked(String clientId, boolean shouldRemove)989 private FocusRequester getFocusRequesterLocked(String clientId, boolean shouldRemove) { 990 if (mFocusPolicy == null) { 991 if (DEBUG) { 992 Log.v(TAG, "> failed: no focus policy"); 993 } 994 return null; 995 } 996 997 FocusRequester fr; 998 if (shouldRemove) { 999 fr = mFocusOwnersForFocusPolicy.remove(clientId); 1000 } else { 1001 fr = mFocusOwnersForFocusPolicy.get(clientId); 1002 } 1003 1004 if (fr == null && DEBUG) { 1005 Log.v(TAG, "> failed: no such focus requester known"); 1006 } 1007 return fr; 1008 } 1009 dumpExtFocusPolicyFocusOwners(PrintWriter pw)1010 private void dumpExtFocusPolicyFocusOwners(PrintWriter pw) { 1011 final Set<Entry<String, FocusRequester>> owners = mFocusOwnersForFocusPolicy.entrySet(); 1012 final Iterator<Entry<String, FocusRequester>> ownerIterator = owners.iterator(); 1013 while (ownerIterator.hasNext()) { 1014 final Entry<String, FocusRequester> owner = ownerIterator.next(); 1015 final FocusRequester fr = owner.getValue(); 1016 fr.dump(pw); 1017 } 1018 } 1019 getCurrentAudioFocus()1020 protected int getCurrentAudioFocus() { 1021 synchronized(mAudioFocusLock) { 1022 if (mFocusStack.empty()) { 1023 return AudioManager.AUDIOFOCUS_NONE; 1024 } else { 1025 return mFocusStack.peek().getGainRequest(); 1026 } 1027 } 1028 } 1029 1030 /** 1031 * Delay after entering ringing or call mode after which the framework will mute streams 1032 * that are still playing. 1033 */ 1034 private static final int RING_CALL_MUTING_ENFORCEMENT_DELAY_MS = 100; 1035 1036 /** 1037 * Usages to mute when the device rings or is in a call 1038 */ 1039 private final static int[] USAGES_TO_MUTE_IN_RING_OR_CALL = 1040 { AudioAttributes.USAGE_MEDIA, AudioAttributes.USAGE_GAME }; 1041 1042 /** 1043 * Return the volume ramp time expected before playback with the given AudioAttributes would 1044 * start after gaining audio focus. 1045 * @param attr attributes of the sound about to start playing 1046 * @return time in ms 1047 */ getFocusRampTimeMs(int focusGain, AudioAttributes attr)1048 protected static int getFocusRampTimeMs(int focusGain, AudioAttributes attr) { 1049 switch (attr.getUsage()) { 1050 case AudioAttributes.USAGE_MEDIA: 1051 case AudioAttributes.USAGE_GAME: 1052 return 1000; 1053 case AudioAttributes.USAGE_ALARM: 1054 case AudioAttributes.USAGE_NOTIFICATION_RINGTONE: 1055 case AudioAttributes.USAGE_ASSISTANT: 1056 case AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY: 1057 case AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: 1058 case AudioAttributes.USAGE_ANNOUNCEMENT: 1059 return 700; 1060 case AudioAttributes.USAGE_VOICE_COMMUNICATION: 1061 case AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING: 1062 case AudioAttributes.USAGE_NOTIFICATION: 1063 case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST: 1064 case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT: 1065 case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED: 1066 case AudioAttributes.USAGE_NOTIFICATION_EVENT: 1067 case AudioAttributes.USAGE_ASSISTANCE_SONIFICATION: 1068 case AudioAttributes.USAGE_VEHICLE_STATUS: 1069 return 500; 1070 case AudioAttributes.USAGE_EMERGENCY: 1071 case AudioAttributes.USAGE_SAFETY: 1072 case AudioAttributes.USAGE_UNKNOWN: 1073 default: 1074 return 0; 1075 } 1076 } 1077 1078 /** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int, int) 1079 * @param aa 1080 * @param focusChangeHint 1081 * @param cb 1082 * @param fd 1083 * @param clientId 1084 * @param callingPackageName 1085 * @param flags 1086 * @param sdk 1087 * @param forceDuck only true if 1088 * {@link android.media.AudioFocusRequest.Builder#setFocusGain(int)} was set to true for 1089 * accessibility. 1090 * @param testUid ignored if flags doesn't contain AudioManager.AUDIOFOCUS_FLAG_TEST 1091 * otherwise the UID being injected for testing 1092 * @param permissionOverridesCheck true if permission checks guaranteed that the call should 1093 * go through, false otherwise (e.g. non-privileged caller) 1094 * @return 1095 */ requestAudioFocus(@onNull AudioAttributes aa, int focusChangeHint, IBinder cb, IAudioFocusDispatcher fd, @NonNull String clientId, @NonNull String callingPackageName, int flags, int sdk, boolean forceDuck, int testUid, boolean permissionOverridesCheck)1096 protected int requestAudioFocus(@NonNull AudioAttributes aa, int focusChangeHint, IBinder cb, 1097 IAudioFocusDispatcher fd, @NonNull String clientId, @NonNull String callingPackageName, 1098 int flags, int sdk, boolean forceDuck, int testUid, 1099 boolean permissionOverridesCheck) { 1100 new MediaMetrics.Item(mMetricsId) 1101 .setUid(Binder.getCallingUid()) 1102 .set(MediaMetrics.Property.CALLING_PACKAGE, callingPackageName) 1103 .set(MediaMetrics.Property.CLIENT_NAME, clientId) 1104 .set(MediaMetrics.Property.EVENT, "requestAudioFocus") 1105 .set(MediaMetrics.Property.FLAGS, flags) 1106 .set(MediaMetrics.Property.FOCUS_CHANGE_HINT, 1107 AudioManager.audioFocusToString(focusChangeHint)) 1108 //.set(MediaMetrics.Property.SDK, sdk) 1109 .record(); 1110 1111 // when using the test API, a fake UID can be injected (testUid is ignored otherwise) 1112 // note that the test on flags is not a mask test on purpose, AUDIOFOCUS_FLAG_TEST is 1113 // supposed to be alone in bitfield 1114 final int uid = (flags == AudioManager.AUDIOFOCUS_FLAG_TEST) 1115 ? testUid : Binder.getCallingUid(); 1116 mEventLogger.enqueue((new EventLogger.StringEvent( 1117 "requestAudioFocus() from uid/pid " + uid 1118 + "/" + Binder.getCallingPid() 1119 + " AA=" + aa.usageToString() + "/" + aa.contentTypeToString() 1120 + " clientId=" + clientId + " callingPack=" + callingPackageName 1121 + " req=" + focusChangeHint 1122 + " flags=0x" + Integer.toHexString(flags) 1123 + " sdk=" + sdk)) 1124 .printLog(TAG)); 1125 // we need a valid binder callback for clients 1126 if (!cb.pingBinder()) { 1127 Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting."); 1128 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 1129 } 1130 1131 synchronized(mAudioFocusLock) { 1132 // check whether a focus freeze is in place and filter 1133 if (isFocusFrozenForTest()) { 1134 int focusRequesterUid; 1135 if ((flags & AudioManager.AUDIOFOCUS_FLAG_TEST) 1136 == AudioManager.AUDIOFOCUS_FLAG_TEST) { 1137 focusRequesterUid = testUid; 1138 } else { 1139 focusRequesterUid = Binder.getCallingUid(); 1140 } 1141 if (isFocusFrozenForTestForUid(focusRequesterUid)) { 1142 Log.i(TAG, "requestAudioFocus: focus frozen for test for uid:" 1143 + focusRequesterUid); 1144 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 1145 } 1146 Log.i(TAG, "requestAudioFocus: focus frozen for test but uid:" + focusRequesterUid 1147 + " is exempt"); 1148 } 1149 1150 if (mFocusStack.size() > MAX_STACK_SIZE) { 1151 Log.e(TAG, "Max AudioFocus stack size reached, failing requestAudioFocus()"); 1152 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 1153 } 1154 1155 boolean enteringRingOrCall = !mRingOrCallActive 1156 & (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0); 1157 if (enteringRingOrCall) { mRingOrCallActive = true; } 1158 1159 final AudioFocusInfo afiForExtPolicy; 1160 if (mFocusPolicy != null) { 1161 // construct AudioFocusInfo as it will be communicated to audio focus policy 1162 afiForExtPolicy = new AudioFocusInfo(aa, uid, 1163 clientId, callingPackageName, focusChangeHint, 0 /*lossReceived*/, 1164 flags, sdk); 1165 } else { 1166 afiForExtPolicy = null; 1167 } 1168 1169 // handle delayed focus 1170 boolean focusGrantDelayed = false; 1171 if (!canReassignAudioFocus()) { 1172 if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) == 0) { 1173 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 1174 } else { 1175 // request has AUDIOFOCUS_FLAG_DELAY_OK: focus can't be 1176 // granted right now, so the requester will be inserted in the focus stack 1177 // to receive focus later 1178 focusGrantDelayed = true; 1179 } 1180 } 1181 1182 // external focus policy? 1183 if (mFocusPolicy != null) { 1184 if (notifyExtFocusPolicyFocusRequest_syncAf(afiForExtPolicy, fd, cb)) { 1185 // stop handling focus request here as it is handled by external audio 1186 // focus policy (return code will be handled in AudioManager) 1187 return AudioManager.AUDIOFOCUS_REQUEST_WAITING_FOR_EXT_POLICY; 1188 } else { 1189 // an error occured, client already dead, bail early 1190 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 1191 } 1192 } 1193 1194 // handle the potential premature death of the new holder of the focus 1195 // (premature death == death before abandoning focus) 1196 // Register for client death notification 1197 AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb); 1198 1199 try { 1200 cb.linkToDeath(afdh, 0); 1201 } catch (RemoteException e) { 1202 // client has already died! 1203 Log.w(TAG, "AudioFocus requestAudioFocus() could not link to "+cb+" binder death"); 1204 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 1205 } 1206 1207 if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientId)) { 1208 // if focus is already owned by this client and the reason for acquiring the focus 1209 // hasn't changed, don't do anything 1210 final FocusRequester fr = mFocusStack.peek(); 1211 if (fr.getGainRequest() == focusChangeHint && fr.getGrantFlags() == flags) { 1212 // unlink death handler so it can be gc'ed. 1213 // linkToDeath() creates a JNI global reference preventing collection. 1214 cb.unlinkToDeath(afdh, 0); 1215 notifyExtPolicyFocusGrant_syncAf(fr.toAudioFocusInfo(), 1216 AudioManager.AUDIOFOCUS_REQUEST_GRANTED); 1217 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 1218 } 1219 // the reason for the audio focus request has changed: remove the current top of 1220 // stack and respond as if we had a new focus owner 1221 if (!focusGrantDelayed) { 1222 mFocusStack.pop(); 1223 // the entry that was "popped" is the same that was "peeked" above 1224 fr.release(); 1225 } 1226 } 1227 1228 // focus requester might already be somewhere below in the stack, remove it 1229 removeFocusStackEntry(clientId, false /* signal */, false /*notifyFocusFollowers*/); 1230 1231 final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb, 1232 clientId, afdh, callingPackageName, uid, this, sdk); 1233 1234 if (mMultiAudioFocusEnabled 1235 && (focusChangeHint == AudioManager.AUDIOFOCUS_GAIN)) { 1236 if (enteringRingOrCall) { 1237 if (!mMultiAudioFocusList.isEmpty()) { 1238 for (FocusRequester multifr : mMultiAudioFocusList) { 1239 multifr.handleFocusLossFromGain(focusChangeHint, nfr, forceDuck); 1240 } 1241 } 1242 } else { 1243 boolean needAdd = true; 1244 if (!mMultiAudioFocusList.isEmpty()) { 1245 for (FocusRequester multifr : mMultiAudioFocusList) { 1246 if (multifr.getClientUid() == Binder.getCallingUid()) { 1247 needAdd = false; 1248 break; 1249 } 1250 } 1251 } 1252 if (needAdd) { 1253 mMultiAudioFocusList.add(nfr); 1254 } 1255 nfr.handleFocusGainFromRequest(AudioManager.AUDIOFOCUS_REQUEST_GRANTED); 1256 notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(), 1257 AudioManager.AUDIOFOCUS_REQUEST_GRANTED); 1258 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 1259 } 1260 } 1261 1262 if (focusGrantDelayed) { 1263 // focusGrantDelayed being true implies we can't reassign focus right now 1264 // which implies the focus stack is not empty. 1265 final int requestResult = pushBelowLockedFocusOwnersAndPropagate(nfr); 1266 if (requestResult != AudioManager.AUDIOFOCUS_REQUEST_FAILED) { 1267 notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(), requestResult); 1268 } 1269 return requestResult; 1270 } else { 1271 // propagate the focus change through the stack 1272 propagateFocusLossFromGain_syncAf(focusChangeHint, nfr, forceDuck); 1273 1274 // push focus requester at the top of the audio focus stack 1275 mFocusStack.push(nfr); 1276 nfr.handleFocusGainFromRequest(AudioManager.AUDIOFOCUS_REQUEST_GRANTED); 1277 } 1278 notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(), 1279 AudioManager.AUDIOFOCUS_REQUEST_GRANTED); 1280 1281 if (ENFORCE_MUTING_FOR_RING_OR_CALL & enteringRingOrCall) { 1282 runAudioCheckerForRingOrCallAsync(true/*enteringRingOrCall*/); 1283 } 1284 }//synchronized(mAudioFocusLock) 1285 1286 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 1287 } 1288 1289 /** 1290 * @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener, AudioAttributes) 1291 * */ abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa, String callingPackageName)1292 protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa, 1293 String callingPackageName) { 1294 new MediaMetrics.Item(mMetricsId) 1295 .setUid(Binder.getCallingUid()) 1296 .set(MediaMetrics.Property.CALLING_PACKAGE, callingPackageName) 1297 .set(MediaMetrics.Property.CLIENT_NAME, clientId) 1298 .set(MediaMetrics.Property.EVENT, "abandonAudioFocus") 1299 .record(); 1300 1301 // AudioAttributes are currently ignored, to be used for zones / a11y 1302 mEventLogger.enqueue((new EventLogger.StringEvent( 1303 "abandonAudioFocus() from uid/pid " + Binder.getCallingUid() 1304 + "/" + Binder.getCallingPid() 1305 + " clientId=" + clientId)) 1306 .printLog(TAG)); 1307 try { 1308 // this will take care of notifying the new focus owner if needed 1309 synchronized(mAudioFocusLock) { 1310 // external focus policy? 1311 if (mFocusPolicy != null) { 1312 final AudioFocusInfo afi = new AudioFocusInfo(aa, Binder.getCallingUid(), 1313 clientId, callingPackageName, 0 /*gainRequest*/, 0 /*lossReceived*/, 1314 0 /*flags*/, 0 /* sdk n/a here*/); 1315 if (notifyExtFocusPolicyFocusAbandon_syncAf(afi)) { 1316 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 1317 } 1318 } 1319 1320 boolean exitingRingOrCall = mRingOrCallActive 1321 & (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0); 1322 if (exitingRingOrCall) { mRingOrCallActive = false; } 1323 1324 removeFocusStackEntry(clientId, true /*signal*/, true /*notifyFocusFollowers*/); 1325 1326 if (ENFORCE_MUTING_FOR_RING_OR_CALL & exitingRingOrCall) { 1327 runAudioCheckerForRingOrCallAsync(false/*enteringRingOrCall*/); 1328 } 1329 } 1330 } catch (java.util.ConcurrentModificationException cme) { 1331 // Catching this exception here is temporary. It is here just to prevent 1332 // a crash seen when the "Silent" notification is played. This is believed to be fixed 1333 // but this try catch block is left just to be safe. 1334 Log.e(TAG, "FATAL EXCEPTION AudioFocus abandonAudioFocus() caused " + cme); 1335 cme.printStackTrace(); 1336 } 1337 1338 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 1339 } 1340 1341 /** 1342 * Reference to the caller of {@link #enterAudioFocusFreezeForTest(IBinder, int[])} 1343 * Will be null when there is no focus freeze for test 1344 */ 1345 @GuardedBy("mAudioFocusLock") 1346 @Nullable 1347 private IBinder mFocusFreezerForTest = null; 1348 1349 /** 1350 * The death handler for {@link #mFocusFreezerForTest} 1351 * Will be null when there is no focus freeze for test 1352 */ 1353 @GuardedBy("mAudioFocusLock") 1354 @Nullable 1355 private IBinder.DeathRecipient mFocusFreezerDeathHandler = null; 1356 1357 /** 1358 * Array of UIDs exempt from focus freeze when focus is frozen for test, null during normal 1359 * operations. 1360 * Will be null when there is no focus freeze for test 1361 */ 1362 @GuardedBy("mAudioFocusLock") 1363 @Nullable 1364 private int[] mFocusFreezeExemptUids = null; 1365 1366 @GuardedBy("mAudioFocusLock") isFocusFrozenForTest()1367 private boolean isFocusFrozenForTest() { 1368 return (mFocusFreezerForTest != null); 1369 } 1370 1371 /** 1372 * Checks if the given UID can request focus when a focus freeze is in place for a test. 1373 * Focus can be requested if focus is not frozen or if it's frozen but the UID is exempt. 1374 * @param uidToCheck 1375 * @return true if that UID is barred from requesting focus, false if its focus request 1376 * can proceed being processed 1377 */ 1378 @GuardedBy("mAudioFocusLock") isFocusFrozenForTestForUid(int uidToCheck)1379 private boolean isFocusFrozenForTestForUid(int uidToCheck) { 1380 if (isFocusFrozenForTest()) { 1381 return false; 1382 } 1383 // check the list of exempts (array is not null because we're in a freeze for test 1384 for (int uid : mFocusFreezeExemptUids) { 1385 if (uid == uidToCheck) { 1386 return false; 1387 } 1388 } 1389 // uid was not found in the exempt list, its focus request is denied 1390 return true; 1391 } 1392 enterAudioFocusFreezeForTest( @onNull IBinder cb, @NonNull int[] exemptedUids)1393 protected boolean enterAudioFocusFreezeForTest( 1394 @NonNull IBinder cb, @NonNull int[] exemptedUids) { 1395 Log.i(TAG, "enterAudioFocusFreezeForTest UIDs exempt:" + Arrays.toString(exemptedUids)); 1396 synchronized (mAudioFocusLock) { 1397 if (mFocusFreezerForTest != null) { 1398 Log.e(TAG, "Error enterAudioFocusFreezeForTest: focus already frozen"); 1399 return false; 1400 } 1401 // new focus freeze, register death handler 1402 try { 1403 mFocusFreezerDeathHandler = new IBinder.DeathRecipient() { 1404 @Override 1405 public void binderDied() { 1406 Log.i(TAG, "Audio focus freezer died, exiting focus freeze for test"); 1407 releaseFocusFreeze(); 1408 } 1409 }; 1410 cb.linkToDeath(mFocusFreezerDeathHandler, 0); 1411 mFocusFreezerForTest = cb; 1412 mFocusFreezeExemptUids = exemptedUids.clone(); 1413 } catch (RemoteException e) { 1414 // client has already died! 1415 mFocusFreezerForTest = null; 1416 mFocusFreezeExemptUids = null; 1417 return false; 1418 } 1419 } 1420 return true; 1421 } 1422 exitAudioFocusFreezeForTest(@onNull IBinder cb)1423 protected boolean exitAudioFocusFreezeForTest(@NonNull IBinder cb) { 1424 synchronized (mAudioFocusLock) { 1425 if (mFocusFreezerForTest != cb) { 1426 Log.e(TAG, "Error exitAudioFocusFreezeForTest: " 1427 + ((mFocusFreezerForTest == null) 1428 ? "call to exit while not frozen" 1429 : "call to exit not coming from freeze owner")); 1430 return false; 1431 } 1432 mFocusFreezerForTest.unlinkToDeath(mFocusFreezerDeathHandler, 0); 1433 releaseFocusFreeze(); 1434 } 1435 return true; 1436 } 1437 releaseFocusFreeze()1438 private void releaseFocusFreeze() { 1439 synchronized (mAudioFocusLock) { 1440 mFocusFreezerDeathHandler = null; 1441 mFocusFreezeExemptUids = null; 1442 mFocusFreezerForTest = null; 1443 } 1444 } 1445 unregisterAudioFocusClient(String clientId)1446 protected void unregisterAudioFocusClient(String clientId) { 1447 synchronized(mAudioFocusLock) { 1448 removeFocusStackEntry(clientId, false, true /*notifyFocusFollowers*/); 1449 } 1450 } 1451 runAudioCheckerForRingOrCallAsync(final boolean enteringRingOrCall)1452 private void runAudioCheckerForRingOrCallAsync(final boolean enteringRingOrCall) { 1453 new Thread() { 1454 public void run() { 1455 if (enteringRingOrCall) { 1456 try { 1457 Thread.sleep(RING_CALL_MUTING_ENFORCEMENT_DELAY_MS); 1458 } catch (InterruptedException e) { 1459 e.printStackTrace(); 1460 } 1461 } 1462 synchronized (mAudioFocusLock) { 1463 // since the new thread starting running the state could have changed, so 1464 // we need to check again mRingOrCallActive, not enteringRingOrCall 1465 if (mRingOrCallActive) { 1466 mFocusEnforcer.mutePlayersForCall(USAGES_TO_MUTE_IN_RING_OR_CALL); 1467 } else { 1468 mFocusEnforcer.unmutePlayersForCall(); 1469 } 1470 } 1471 } 1472 }.start(); 1473 } 1474 updateMultiAudioFocus(boolean enabled)1475 public void updateMultiAudioFocus(boolean enabled) { 1476 Log.d(TAG, "updateMultiAudioFocus( " + enabled + " )"); 1477 mMultiAudioFocusEnabled = enabled; 1478 final ContentResolver cr = mContext.getContentResolver(); 1479 Settings.System.putIntForUser(cr, 1480 Settings.System.MULTI_AUDIO_FOCUS_ENABLED, enabled ? 1 : 0, cr.getUserId()); 1481 if (!mFocusStack.isEmpty()) { 1482 final FocusRequester fr = mFocusStack.peek(); 1483 fr.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null, false); 1484 } 1485 if (!enabled) { 1486 if (!mMultiAudioFocusList.isEmpty()) { 1487 for (FocusRequester multifr : mMultiAudioFocusList) { 1488 multifr.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null, false); 1489 } 1490 mMultiAudioFocusList.clear(); 1491 } 1492 } 1493 } 1494 getMultiAudioFocusEnabled()1495 public boolean getMultiAudioFocusEnabled() { 1496 return mMultiAudioFocusEnabled; 1497 } 1498 getFadeOutDurationOnFocusLossMillis(AudioAttributes aa)1499 /*package*/ long getFadeOutDurationOnFocusLossMillis(AudioAttributes aa) { 1500 if (!ENFORCE_FADEOUT_FOR_FOCUS_LOSS) { 1501 return 0; 1502 } 1503 return getFadeOutDurationMillis(aa); 1504 } 1505 dumpMultiAudioFocus(PrintWriter pw)1506 private void dumpMultiAudioFocus(PrintWriter pw) { 1507 pw.println("Multi Audio Focus enabled :" + mMultiAudioFocusEnabled); 1508 if (!mMultiAudioFocusList.isEmpty()) { 1509 pw.println("Multi Audio Focus List:"); 1510 pw.println("------------------------------"); 1511 for (FocusRequester multifr : mMultiAudioFocusList) { 1512 multifr.dump(pw); 1513 } 1514 pw.println("------------------------------"); 1515 } 1516 } 1517 1518 //================================================================= 1519 // Async focus events postDelayedLossAfterFade(FocusRequester focusLoser, long delayMs)1520 void postDelayedLossAfterFade(FocusRequester focusLoser, long delayMs) { 1521 if (DEBUG) { 1522 Log.v(TAG, "postDelayedLossAfterFade loser=" + focusLoser.getPackageName()); 1523 } 1524 mFocusHandler.sendMessageDelayed( 1525 mFocusHandler.obtainMessage(MSG_L_FOCUS_LOSS_AFTER_FADE, focusLoser), delayMs); 1526 } 1527 postForgetUidLater(FocusRequester focusRequester)1528 private void postForgetUidLater(FocusRequester focusRequester) { 1529 mFocusHandler.sendMessageDelayed( 1530 mFocusHandler.obtainMessage(MSL_L_FORGET_UID, 1531 new ForgetFadeUidInfo(focusRequester.getClientUid())), 1532 getFadeInDelayForOffendersMillis(focusRequester.getAudioAttributes())); 1533 } 1534 1535 //================================================================= 1536 // Message handling 1537 private Handler mFocusHandler; 1538 private HandlerThread mFocusThread; 1539 1540 /** 1541 * dispatch a focus loss after an app has been faded out. Focus loser is to be released 1542 * after dispatch as it has already left the stack 1543 * args: 1544 * msg.obj: the audio focus loser 1545 * type:FocusRequester 1546 */ 1547 private static final int MSG_L_FOCUS_LOSS_AFTER_FADE = 1; 1548 1549 private static final int MSL_L_FORGET_UID = 2; 1550 initFocusThreading()1551 private void initFocusThreading() { 1552 mFocusThread = new HandlerThread(TAG); 1553 mFocusThread.start(); 1554 mFocusHandler = new Handler(mFocusThread.getLooper()) { 1555 @Override 1556 public void handleMessage(Message msg) { 1557 switch (msg.what) { 1558 case MSG_L_FOCUS_LOSS_AFTER_FADE: 1559 if (DEBUG) { 1560 Log.d(TAG, "MSG_L_FOCUS_LOSS_AFTER_FADE loser=" 1561 + ((FocusRequester) msg.obj).getPackageName()); 1562 } 1563 synchronized (mAudioFocusLock) { 1564 final FocusRequester loser = (FocusRequester) msg.obj; 1565 if (loser.isInFocusLossLimbo()) { 1566 loser.dispatchFocusChange(AudioManager.AUDIOFOCUS_LOSS); 1567 loser.release(); 1568 postForgetUidLater(loser); 1569 } 1570 } 1571 break; 1572 1573 case MSL_L_FORGET_UID: 1574 final int uid = ((ForgetFadeUidInfo) msg.obj).mUid; 1575 if (DEBUG) { 1576 Log.d(TAG, "MSL_L_FORGET_UID uid=" + uid); 1577 } 1578 mFocusEnforcer.forgetUid(uid); 1579 break; 1580 default: 1581 break; 1582 } 1583 } 1584 }; 1585 } 1586 1587 /** 1588 * Class to associate a UID with a scheduled event to "forget" a UID for the fade out behavior. 1589 * Having a class with an equals() override allows using Handler.removeEqualsMessage() to 1590 * unschedule events when needed. Here we need to unschedule the "unfading out" == "forget uid" 1591 * whenever a new, more recent, focus related event happens before this one is handled. 1592 */ 1593 private static final class ForgetFadeUidInfo { 1594 private final int mUid; 1595 ForgetFadeUidInfo(int uid)1596 ForgetFadeUidInfo(int uid) { 1597 mUid = uid; 1598 } 1599 1600 @Override equals(Object o)1601 public boolean equals(Object o) { 1602 if (this == o) { 1603 return true; 1604 } 1605 if (o == null || getClass() != o.getClass()) { 1606 return false; 1607 } 1608 final ForgetFadeUidInfo f = (ForgetFadeUidInfo) o; 1609 if (f.mUid != mUid) { 1610 return false; 1611 } 1612 return true; 1613 } 1614 1615 @Override hashCode()1616 public int hashCode() { 1617 return mUid; 1618 } 1619 } 1620 } 1621