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