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