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