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