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.app.AppOpsManager; 21 import android.content.Context; 22 import android.media.AudioAttributes; 23 import android.media.AudioFocusInfo; 24 import android.media.AudioManager; 25 import android.media.AudioSystem; 26 import android.media.IAudioFocusDispatcher; 27 import android.media.audiopolicy.AudioPolicy; 28 import android.media.audiopolicy.IAudioPolicyCallback; 29 import android.os.Binder; 30 import android.os.Build; 31 import android.os.IBinder; 32 import android.os.RemoteException; 33 import android.util.Log; 34 35 import com.android.internal.annotations.GuardedBy; 36 37 import java.io.PrintWriter; 38 import java.util.ArrayList; 39 import java.util.Date; 40 import java.util.HashMap; 41 import java.util.Iterator; 42 import java.util.LinkedList; 43 import java.util.List; 44 import java.util.Map.Entry; 45 import java.util.Set; 46 import java.util.Stack; 47 import java.text.DateFormat; 48 49 /** 50 * @hide 51 * 52 */ 53 public class MediaFocusControl implements PlayerFocusEnforcer { 54 55 private static final String TAG = "MediaFocusControl"; 56 static final boolean DEBUG = false; 57 58 /** 59 * set to true so the framework enforces ducking itself, without communicating to apps 60 * that they lost focus for most use cases. 61 */ 62 static final boolean ENFORCE_DUCKING = true; 63 /** 64 * set to true to the framework enforces ducking itself only with apps above a given SDK 65 * target level. Is ignored if ENFORCE_DUCKING is false. 66 */ 67 static final boolean ENFORCE_DUCKING_FOR_NEW = true; 68 /** 69 * the SDK level (included) up to which the framework doesn't enforce ducking itself. Is ignored 70 * if ENFORCE_DUCKING_FOR_NEW is false; 71 */ 72 // automatic ducking was introduced for Android O 73 static final int DUCKING_IN_APP_SDK_LEVEL = Build.VERSION_CODES.N_MR1; 74 /** 75 * set to true so the framework enforces muting media/game itself when the device is ringing 76 * or in a call. 77 */ 78 static final boolean ENFORCE_MUTING_FOR_RING_OR_CALL = true; 79 80 private final Context mContext; 81 private final AppOpsManager mAppOps; 82 private PlayerFocusEnforcer mFocusEnforcer; // never null 83 84 private boolean mRingOrCallActive = false; 85 86 private final Object mExtFocusChangeLock = new Object(); 87 @GuardedBy("mExtFocusChangeLock") 88 private long mExtFocusChangeCounter; 89 90 protected MediaFocusControl(Context cntxt, PlayerFocusEnforcer pfe) { 91 mContext = cntxt; 92 mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE); 93 mFocusEnforcer = pfe; 94 } 95 96 protected void dump(PrintWriter pw) { 97 pw.println("\nMediaFocusControl dump time: " 98 + DateFormat.getTimeInstance().format(new Date())); 99 dumpFocusStack(pw); 100 pw.println("\n"); 101 // log 102 mEventLogger.dump(pw); 103 } 104 105 //================================================================= 106 // PlayerFocusEnforcer implementation 107 @Override 108 public boolean duckPlayers(FocusRequester winner, FocusRequester loser, boolean forceDuck) { 109 return mFocusEnforcer.duckPlayers(winner, loser, forceDuck); 110 } 111 112 @Override 113 public void unduckPlayers(FocusRequester winner) { 114 mFocusEnforcer.unduckPlayers(winner); 115 } 116 117 @Override 118 public void mutePlayersForCall(int[] usagesToMute) { 119 mFocusEnforcer.mutePlayersForCall(usagesToMute); 120 } 121 122 @Override 123 public void unmutePlayersForCall() { 124 mFocusEnforcer.unmutePlayersForCall(); 125 } 126 127 //========================================================================================== 128 // AudioFocus 129 //========================================================================================== 130 131 private final static Object mAudioFocusLock = new Object(); 132 133 /** 134 * Arbitrary maximum size of audio focus stack to prevent apps OOM'ing this process. 135 */ 136 private static final int MAX_STACK_SIZE = 100; 137 138 private static final AudioEventLogger mEventLogger = new AudioEventLogger(50, 139 "focus commands as seen by MediaFocusControl"); 140 141 /** 142 * Discard the current audio focus owner. 143 * Notify top of audio focus stack that it lost focus (regardless of possibility to reassign 144 * focus), remove it from the stack, and clear the remote control display. 145 */ 146 protected void discardAudioFocusOwner() { 147 synchronized(mAudioFocusLock) { 148 if (!mFocusStack.empty()) { 149 // notify the current focus owner it lost focus after removing it from stack 150 final FocusRequester exFocusOwner = mFocusStack.pop(); 151 exFocusOwner.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null, 152 false /*forceDuck*/); 153 exFocusOwner.release(); 154 } 155 } 156 } 157 158 @GuardedBy("mAudioFocusLock") 159 private void notifyTopOfAudioFocusStack() { 160 // notify the top of the stack it gained focus 161 if (!mFocusStack.empty()) { 162 if (canReassignAudioFocus()) { 163 mFocusStack.peek().handleFocusGain(AudioManager.AUDIOFOCUS_GAIN); 164 } 165 } 166 } 167 168 /** 169 * Focus is requested, propagate the associated loss throughout the stack. 170 * Will also remove entries in the stack that have just received a definitive loss of focus. 171 * @param focusGain the new focus gain that will later be added at the top of the stack 172 */ 173 @GuardedBy("mAudioFocusLock") 174 private void propagateFocusLossFromGain_syncAf(int focusGain, final FocusRequester fr, 175 boolean forceDuck) { 176 final List<String> clientsToRemove = new LinkedList<String>(); 177 // going through the audio focus stack to signal new focus, traversing order doesn't 178 // matter as all entries respond to the same external focus gain 179 for (FocusRequester focusLoser : mFocusStack) { 180 final boolean isDefinitiveLoss = 181 focusLoser.handleFocusLossFromGain(focusGain, fr, forceDuck); 182 if (isDefinitiveLoss) { 183 clientsToRemove.add(focusLoser.getClientId()); 184 } 185 } 186 for (String clientToRemove : clientsToRemove) { 187 removeFocusStackEntry(clientToRemove, false /*signal*/, 188 true /*notifyFocusFollowers*/); 189 } 190 } 191 192 private final Stack<FocusRequester> mFocusStack = new Stack<FocusRequester>(); 193 194 /** 195 * Helper function: 196 * Display in the log the current entries in the audio focus stack 197 */ 198 private void dumpFocusStack(PrintWriter pw) { 199 pw.println("\nAudio Focus stack entries (last is top of stack):"); 200 synchronized(mAudioFocusLock) { 201 Iterator<FocusRequester> stackIterator = mFocusStack.iterator(); 202 while(stackIterator.hasNext()) { 203 stackIterator.next().dump(pw); 204 } 205 pw.println("\n"); 206 if (mFocusPolicy == null) { 207 pw.println("No external focus policy\n"); 208 } else { 209 pw.println("External focus policy: "+ mFocusPolicy + ", focus owners:\n"); 210 dumpExtFocusPolicyFocusOwners(pw); 211 } 212 } 213 pw.println("\n"); 214 pw.println(" Notify on duck: " + mNotifyFocusOwnerOnDuck + "\n"); 215 pw.println(" In ring or call: " + mRingOrCallActive + "\n"); 216 } 217 218 /** 219 * Remove a focus listener from the focus stack. 220 * @param clientToRemove the focus listener 221 * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding 222 * focus, notify the next item in the stack it gained focus. 223 */ 224 @GuardedBy("mAudioFocusLock") 225 private void removeFocusStackEntry(String clientToRemove, boolean signal, 226 boolean notifyFocusFollowers) { 227 // is the current top of the focus stack abandoning focus? (because of request, not death) 228 if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientToRemove)) 229 { 230 //Log.i(TAG, " removeFocusStackEntry() removing top of stack"); 231 FocusRequester fr = mFocusStack.pop(); 232 fr.release(); 233 if (notifyFocusFollowers) { 234 final AudioFocusInfo afi = fr.toAudioFocusInfo(); 235 afi.clearLossReceived(); 236 notifyExtPolicyFocusLoss_syncAf(afi, false); 237 } 238 if (signal) { 239 // notify the new top of the stack it gained focus 240 notifyTopOfAudioFocusStack(); 241 } 242 } else { 243 // focus is abandoned by a client that's not at the top of the stack, 244 // no need to update focus. 245 // (using an iterator on the stack so we can safely remove an entry after having 246 // evaluated it, traversal order doesn't matter here) 247 Iterator<FocusRequester> stackIterator = mFocusStack.iterator(); 248 while(stackIterator.hasNext()) { 249 FocusRequester fr = stackIterator.next(); 250 if(fr.hasSameClient(clientToRemove)) { 251 Log.i(TAG, "AudioFocus removeFocusStackEntry(): removing entry for " 252 + clientToRemove); 253 stackIterator.remove(); 254 // stack entry not used anymore, clear references 255 fr.release(); 256 } 257 } 258 } 259 } 260 261 /** 262 * Remove focus listeners from the focus stack for a particular client when it has died. 263 */ 264 @GuardedBy("mAudioFocusLock") 265 private void removeFocusStackEntryOnDeath(IBinder cb) { 266 // is the owner of the audio focus part of the client to remove? 267 boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() && 268 mFocusStack.peek().hasSameBinder(cb); 269 // (using an iterator on the stack so we can safely remove an entry after having 270 // evaluated it, traversal order doesn't matter here) 271 Iterator<FocusRequester> stackIterator = mFocusStack.iterator(); 272 while(stackIterator.hasNext()) { 273 FocusRequester fr = stackIterator.next(); 274 if(fr.hasSameBinder(cb)) { 275 Log.i(TAG, "AudioFocus removeFocusStackEntryOnDeath(): removing entry for " + cb); 276 stackIterator.remove(); 277 // stack entry not used anymore, clear references 278 fr.release(); 279 } 280 } 281 if (isTopOfStackForClientToRemove) { 282 // we removed an entry at the top of the stack: 283 // notify the new top of the stack it gained focus. 284 notifyTopOfAudioFocusStack(); 285 } 286 } 287 288 /** 289 * Helper function for external focus policy: 290 * Remove focus listeners from the list of potential focus owners for a particular client when 291 * it has died. 292 */ 293 @GuardedBy("mAudioFocusLock") 294 private void removeFocusEntryForExtPolicy(IBinder cb) { 295 if (mFocusOwnersForFocusPolicy.isEmpty()) { 296 return; 297 } 298 boolean released = false; 299 final Set<Entry<String, FocusRequester>> owners = mFocusOwnersForFocusPolicy.entrySet(); 300 final Iterator<Entry<String, FocusRequester>> ownerIterator = owners.iterator(); 301 while (ownerIterator.hasNext()) { 302 final Entry<String, FocusRequester> owner = ownerIterator.next(); 303 final FocusRequester fr = owner.getValue(); 304 if (fr.hasSameBinder(cb)) { 305 ownerIterator.remove(); 306 fr.release(); 307 notifyExtFocusPolicyFocusAbandon_syncAf(fr.toAudioFocusInfo()); 308 break; 309 } 310 } 311 } 312 313 /** 314 * Helper function: 315 * Returns true if the system is in a state where the focus can be reevaluated, false otherwise. 316 * The implementation guarantees that a state where focus cannot be immediately reassigned 317 * implies that an "locked" focus owner is at the top of the focus stack. 318 * Modifications to the implementation that break this assumption will cause focus requests to 319 * misbehave when honoring the AudioManager.AUDIOFOCUS_FLAG_DELAY_OK flag. 320 */ 321 private boolean canReassignAudioFocus() { 322 // focus requests are rejected during a phone call or when the phone is ringing 323 // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus 324 if (!mFocusStack.isEmpty() && isLockedFocusOwner(mFocusStack.peek())) { 325 return false; 326 } 327 return true; 328 } 329 330 private boolean isLockedFocusOwner(FocusRequester fr) { 331 return (fr.hasSameClient(AudioSystem.IN_VOICE_COMM_FOCUS_ID) || fr.isLockedFocusOwner()); 332 } 333 334 /** 335 * Helper function 336 * Pre-conditions: focus stack is not empty, there is one or more locked focus owner 337 * at the top of the focus stack 338 * Push the focus requester onto the audio focus stack at the first position immediately 339 * following the locked focus owners. 340 * @return {@link AudioManager#AUDIOFOCUS_REQUEST_GRANTED} or 341 * {@link AudioManager#AUDIOFOCUS_REQUEST_DELAYED} 342 */ 343 @GuardedBy("mAudioFocusLock") 344 private int pushBelowLockedFocusOwners(FocusRequester nfr) { 345 int lastLockedFocusOwnerIndex = mFocusStack.size(); 346 for (int index = mFocusStack.size()-1; index >= 0; index--) { 347 if (isLockedFocusOwner(mFocusStack.elementAt(index))) { 348 lastLockedFocusOwnerIndex = index; 349 } 350 } 351 if (lastLockedFocusOwnerIndex == mFocusStack.size()) { 352 // this should not happen, but handle it and log an error 353 Log.e(TAG, "No exclusive focus owner found in propagateFocusLossFromGain_syncAf()", 354 new Exception()); 355 // no exclusive owner, push at top of stack, focus is granted, propagate change 356 propagateFocusLossFromGain_syncAf(nfr.getGainRequest(), nfr, false /*forceDuck*/); 357 mFocusStack.push(nfr); 358 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 359 } else { 360 mFocusStack.insertElementAt(nfr, lastLockedFocusOwnerIndex); 361 return AudioManager.AUDIOFOCUS_REQUEST_DELAYED; 362 } 363 } 364 365 /** 366 * Inner class to monitor audio focus client deaths, and remove them from the audio focus 367 * stack if necessary. 368 */ 369 protected class AudioFocusDeathHandler implements IBinder.DeathRecipient { 370 private IBinder mCb; // To be notified of client's death 371 372 AudioFocusDeathHandler(IBinder cb) { 373 mCb = cb; 374 } 375 376 public void binderDied() { 377 synchronized(mAudioFocusLock) { 378 if (mFocusPolicy != null) { 379 removeFocusEntryForExtPolicy(mCb); 380 } else { 381 removeFocusStackEntryOnDeath(mCb); 382 } 383 } 384 } 385 } 386 387 /** 388 * Indicates whether to notify an audio focus owner when it loses focus 389 * with {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK} if it will only duck. 390 * This variable being false indicates an AudioPolicy has been registered and has signaled 391 * it will handle audio ducking. 392 */ 393 private boolean mNotifyFocusOwnerOnDuck = true; 394 395 protected void setDuckingInExtPolicyAvailable(boolean available) { 396 mNotifyFocusOwnerOnDuck = !available; 397 } 398 399 boolean mustNotifyFocusOwnerOnDuck() { return mNotifyFocusOwnerOnDuck; } 400 401 private ArrayList<IAudioPolicyCallback> mFocusFollowers = new ArrayList<IAudioPolicyCallback>(); 402 403 void addFocusFollower(IAudioPolicyCallback ff) { 404 if (ff == null) { 405 return; 406 } 407 synchronized(mAudioFocusLock) { 408 boolean found = false; 409 for (IAudioPolicyCallback pcb : mFocusFollowers) { 410 if (pcb.asBinder().equals(ff.asBinder())) { 411 found = true; 412 break; 413 } 414 } 415 if (found) { 416 return; 417 } else { 418 mFocusFollowers.add(ff); 419 notifyExtPolicyCurrentFocusAsync(ff); 420 } 421 } 422 } 423 424 void removeFocusFollower(IAudioPolicyCallback ff) { 425 if (ff == null) { 426 return; 427 } 428 synchronized(mAudioFocusLock) { 429 for (IAudioPolicyCallback pcb : mFocusFollowers) { 430 if (pcb.asBinder().equals(ff.asBinder())) { 431 mFocusFollowers.remove(pcb); 432 break; 433 } 434 } 435 } 436 } 437 438 private IAudioPolicyCallback mFocusPolicy = null; 439 440 // Since we don't have a stack of focus owners when using an external focus policy, we keep 441 // track of all the focus requesters in this map, with their clientId as the key. This is 442 // used both for focus dispatch and death handling 443 private HashMap<String, FocusRequester> mFocusOwnersForFocusPolicy = 444 new HashMap<String, FocusRequester>(); 445 446 void setFocusPolicy(IAudioPolicyCallback policy) { 447 if (policy == null) { 448 return; 449 } 450 synchronized (mAudioFocusLock) { 451 mFocusPolicy = policy; 452 } 453 } 454 455 void unsetFocusPolicy(IAudioPolicyCallback policy) { 456 if (policy == null) { 457 return; 458 } 459 synchronized (mAudioFocusLock) { 460 if (mFocusPolicy == policy) { 461 mFocusPolicy = null; 462 } 463 } 464 } 465 466 /** 467 * @param pcb non null 468 */ 469 void notifyExtPolicyCurrentFocusAsync(IAudioPolicyCallback pcb) { 470 final IAudioPolicyCallback pcb2 = pcb; 471 final Thread thread = new Thread() { 472 @Override 473 public void run() { 474 synchronized(mAudioFocusLock) { 475 if (mFocusStack.isEmpty()) { 476 return; 477 } 478 try { 479 pcb2.notifyAudioFocusGrant(mFocusStack.peek().toAudioFocusInfo(), 480 // top of focus stack always has focus 481 AudioManager.AUDIOFOCUS_REQUEST_GRANTED); 482 } catch (RemoteException e) { 483 Log.e(TAG, "Can't call notifyAudioFocusGrant() on IAudioPolicyCallback " 484 + pcb2.asBinder(), e); 485 } 486 } 487 } 488 }; 489 thread.start(); 490 } 491 492 /** 493 * Called synchronized on mAudioFocusLock 494 */ 495 void notifyExtPolicyFocusGrant_syncAf(AudioFocusInfo afi, int requestResult) { 496 for (IAudioPolicyCallback pcb : mFocusFollowers) { 497 try { 498 // oneway 499 pcb.notifyAudioFocusGrant(afi, requestResult); 500 } catch (RemoteException e) { 501 Log.e(TAG, "Can't call notifyAudioFocusGrant() on IAudioPolicyCallback " 502 + pcb.asBinder(), e); 503 } 504 } 505 } 506 507 /** 508 * Called synchronized on mAudioFocusLock 509 */ 510 void notifyExtPolicyFocusLoss_syncAf(AudioFocusInfo afi, boolean wasDispatched) { 511 for (IAudioPolicyCallback pcb : mFocusFollowers) { 512 try { 513 // oneway 514 pcb.notifyAudioFocusLoss(afi, wasDispatched); 515 } catch (RemoteException e) { 516 Log.e(TAG, "Can't call notifyAudioFocusLoss() on IAudioPolicyCallback " 517 + pcb.asBinder(), e); 518 } 519 } 520 } 521 522 /** 523 * Called synchronized on mAudioFocusLock 524 * @param afi 525 * @param requestResult 526 * @return true if the external audio focus policy (if any) is handling the focus request 527 */ 528 boolean notifyExtFocusPolicyFocusRequest_syncAf(AudioFocusInfo afi, 529 IAudioFocusDispatcher fd, IBinder cb) { 530 if (mFocusPolicy == null) { 531 return false; 532 } 533 if (DEBUG) { 534 Log.v(TAG, "notifyExtFocusPolicyFocusRequest client="+afi.getClientId() 535 + " dispatcher=" + fd); 536 } 537 synchronized (mExtFocusChangeLock) { 538 afi.setGen(mExtFocusChangeCounter++); 539 } 540 final FocusRequester existingFr = mFocusOwnersForFocusPolicy.get(afi.getClientId()); 541 if (existingFr != null) { 542 if (!existingFr.hasSameDispatcher(fd)) { 543 existingFr.release(); 544 final AudioFocusDeathHandler hdlr = new AudioFocusDeathHandler(cb); 545 mFocusOwnersForFocusPolicy.put(afi.getClientId(), 546 new FocusRequester(afi, fd, cb, hdlr, this)); 547 } 548 } else { 549 // new focus (future) focus owner to keep track of 550 final AudioFocusDeathHandler hdlr = new AudioFocusDeathHandler(cb); 551 mFocusOwnersForFocusPolicy.put(afi.getClientId(), 552 new FocusRequester(afi, fd, cb, hdlr, this)); 553 } 554 try { 555 //oneway 556 mFocusPolicy.notifyAudioFocusRequest(afi, AudioManager.AUDIOFOCUS_REQUEST_GRANTED); 557 return true; 558 } catch (RemoteException e) { 559 Log.e(TAG, "Can't call notifyAudioFocusRequest() on IAudioPolicyCallback " 560 + mFocusPolicy.asBinder(), e); 561 } 562 return false; 563 } 564 565 void setFocusRequestResultFromExtPolicy(AudioFocusInfo afi, int requestResult) { 566 synchronized (mExtFocusChangeLock) { 567 if (afi.getGen() > mExtFocusChangeCounter) { 568 return; 569 } 570 } 571 final FocusRequester fr = mFocusOwnersForFocusPolicy.get(afi.getClientId()); 572 if (fr != null) { 573 fr.dispatchFocusResultFromExtPolicy(requestResult); 574 } 575 } 576 577 /** 578 * Called synchronized on mAudioFocusLock 579 * @param afi 580 * @param requestResult 581 * @return true if the external audio focus policy (if any) is handling the focus request 582 */ 583 boolean notifyExtFocusPolicyFocusAbandon_syncAf(AudioFocusInfo afi) { 584 if (mFocusPolicy == null) { 585 return false; 586 } 587 final FocusRequester fr = mFocusOwnersForFocusPolicy.remove(afi.getClientId()); 588 if (fr != null) { 589 fr.release(); 590 } 591 try { 592 //oneway 593 mFocusPolicy.notifyAudioFocusAbandon(afi); 594 } catch (RemoteException e) { 595 Log.e(TAG, "Can't call notifyAudioFocusAbandon() on IAudioPolicyCallback " 596 + mFocusPolicy.asBinder(), e); 597 } 598 return true; 599 } 600 601 /** see AudioManager.dispatchFocusChange(AudioFocusInfo afi, int focusChange, AudioPolicy ap) */ 602 int dispatchFocusChange(AudioFocusInfo afi, int focusChange) { 603 if (DEBUG) { 604 Log.v(TAG, "dispatchFocusChange " + focusChange + " to afi client=" 605 + afi.getClientId()); 606 } 607 synchronized (mAudioFocusLock) { 608 if (mFocusPolicy == null) { 609 if (DEBUG) { Log.v(TAG, "> failed: no focus policy" ); } 610 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 611 } 612 final FocusRequester fr; 613 if (focusChange == AudioManager.AUDIOFOCUS_LOSS) { 614 fr = mFocusOwnersForFocusPolicy.remove(afi.getClientId()); 615 } else { 616 fr = mFocusOwnersForFocusPolicy.get(afi.getClientId()); 617 } 618 if (fr == null) { 619 if (DEBUG) { Log.v(TAG, "> failed: no such focus requester known" ); } 620 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 621 } 622 return fr.dispatchFocusChange(focusChange); 623 } 624 } 625 626 private void dumpExtFocusPolicyFocusOwners(PrintWriter pw) { 627 final Set<Entry<String, FocusRequester>> owners = mFocusOwnersForFocusPolicy.entrySet(); 628 final Iterator<Entry<String, FocusRequester>> ownerIterator = owners.iterator(); 629 while (ownerIterator.hasNext()) { 630 final Entry<String, FocusRequester> owner = ownerIterator.next(); 631 final FocusRequester fr = owner.getValue(); 632 fr.dump(pw); 633 } 634 } 635 636 protected int getCurrentAudioFocus() { 637 synchronized(mAudioFocusLock) { 638 if (mFocusStack.empty()) { 639 return AudioManager.AUDIOFOCUS_NONE; 640 } else { 641 return mFocusStack.peek().getGainRequest(); 642 } 643 } 644 } 645 646 /** 647 * Delay after entering ringing or call mode after which the framework will mute streams 648 * that are still playing. 649 */ 650 private static final int RING_CALL_MUTING_ENFORCEMENT_DELAY_MS = 100; 651 652 /** 653 * Usages to mute when the device rings or is in a call 654 */ 655 private final static int[] USAGES_TO_MUTE_IN_RING_OR_CALL = 656 { AudioAttributes.USAGE_MEDIA, AudioAttributes.USAGE_GAME }; 657 658 /** 659 * Return the volume ramp time expected before playback with the given AudioAttributes would 660 * start after gaining audio focus. 661 * @param attr attributes of the sound about to start playing 662 * @return time in ms 663 */ 664 protected static int getFocusRampTimeMs(int focusGain, AudioAttributes attr) { 665 switch (attr.getUsage()) { 666 case AudioAttributes.USAGE_MEDIA: 667 case AudioAttributes.USAGE_GAME: 668 return 1000; 669 case AudioAttributes.USAGE_ALARM: 670 case AudioAttributes.USAGE_NOTIFICATION_RINGTONE: 671 case AudioAttributes.USAGE_ASSISTANT: 672 case AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY: 673 case AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: 674 return 700; 675 case AudioAttributes.USAGE_VOICE_COMMUNICATION: 676 case AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING: 677 case AudioAttributes.USAGE_NOTIFICATION: 678 case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST: 679 case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT: 680 case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED: 681 case AudioAttributes.USAGE_NOTIFICATION_EVENT: 682 case AudioAttributes.USAGE_ASSISTANCE_SONIFICATION: 683 return 500; 684 case AudioAttributes.USAGE_UNKNOWN: 685 default: 686 return 0; 687 } 688 } 689 690 /** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int, int) */ 691 protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb, 692 IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags, 693 int sdk, boolean forceDuck) { 694 mEventLogger.log((new AudioEventLogger.StringEvent( 695 "requestAudioFocus() from uid/pid " + Binder.getCallingUid() 696 + "/" + Binder.getCallingPid() 697 + " clientId=" + clientId + " callingPack=" + callingPackageName 698 + " req=" + focusChangeHint 699 + " flags=0x" + Integer.toHexString(flags) 700 + " sdk=" + sdk)) 701 .printLog(TAG)); 702 // we need a valid binder callback for clients 703 if (!cb.pingBinder()) { 704 Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting."); 705 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 706 } 707 708 if (mAppOps.noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, Binder.getCallingUid(), 709 callingPackageName) != AppOpsManager.MODE_ALLOWED) { 710 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 711 } 712 713 synchronized(mAudioFocusLock) { 714 if (mFocusStack.size() > MAX_STACK_SIZE) { 715 Log.e(TAG, "Max AudioFocus stack size reached, failing requestAudioFocus()"); 716 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 717 } 718 719 boolean enteringRingOrCall = !mRingOrCallActive 720 & (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0); 721 if (enteringRingOrCall) { mRingOrCallActive = true; } 722 723 final AudioFocusInfo afiForExtPolicy; 724 if (mFocusPolicy != null) { 725 // construct AudioFocusInfo as it will be communicated to audio focus policy 726 afiForExtPolicy = new AudioFocusInfo(aa, Binder.getCallingUid(), 727 clientId, callingPackageName, focusChangeHint, 0 /*lossReceived*/, 728 flags, sdk); 729 } else { 730 afiForExtPolicy = null; 731 } 732 733 // handle delayed focus 734 boolean focusGrantDelayed = false; 735 if (!canReassignAudioFocus()) { 736 if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) == 0) { 737 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 738 } else { 739 // request has AUDIOFOCUS_FLAG_DELAY_OK: focus can't be 740 // granted right now, so the requester will be inserted in the focus stack 741 // to receive focus later 742 focusGrantDelayed = true; 743 } 744 } 745 746 // external focus policy? 747 if (notifyExtFocusPolicyFocusRequest_syncAf( 748 afiForExtPolicy, fd, cb)) { 749 // stop handling focus request here as it is handled by external audio focus policy 750 return AudioManager.AUDIOFOCUS_REQUEST_WAITING_FOR_EXT_POLICY; 751 } 752 753 // handle the potential premature death of the new holder of the focus 754 // (premature death == death before abandoning focus) 755 // Register for client death notification 756 AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb); 757 758 try { 759 cb.linkToDeath(afdh, 0); 760 } catch (RemoteException e) { 761 // client has already died! 762 Log.w(TAG, "AudioFocus requestAudioFocus() could not link to "+cb+" binder death"); 763 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 764 } 765 766 if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientId)) { 767 // if focus is already owned by this client and the reason for acquiring the focus 768 // hasn't changed, don't do anything 769 final FocusRequester fr = mFocusStack.peek(); 770 if (fr.getGainRequest() == focusChangeHint && fr.getGrantFlags() == flags) { 771 // unlink death handler so it can be gc'ed. 772 // linkToDeath() creates a JNI global reference preventing collection. 773 cb.unlinkToDeath(afdh, 0); 774 notifyExtPolicyFocusGrant_syncAf(fr.toAudioFocusInfo(), 775 AudioManager.AUDIOFOCUS_REQUEST_GRANTED); 776 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 777 } 778 // the reason for the audio focus request has changed: remove the current top of 779 // stack and respond as if we had a new focus owner 780 if (!focusGrantDelayed) { 781 mFocusStack.pop(); 782 // the entry that was "popped" is the same that was "peeked" above 783 fr.release(); 784 } 785 } 786 787 // focus requester might already be somewhere below in the stack, remove it 788 removeFocusStackEntry(clientId, false /* signal */, false /*notifyFocusFollowers*/); 789 790 final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb, 791 clientId, afdh, callingPackageName, Binder.getCallingUid(), this, sdk); 792 if (focusGrantDelayed) { 793 // focusGrantDelayed being true implies we can't reassign focus right now 794 // which implies the focus stack is not empty. 795 final int requestResult = pushBelowLockedFocusOwners(nfr); 796 if (requestResult != AudioManager.AUDIOFOCUS_REQUEST_FAILED) { 797 notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(), requestResult); 798 } 799 return requestResult; 800 } else { 801 // propagate the focus change through the stack 802 if (!mFocusStack.empty()) { 803 propagateFocusLossFromGain_syncAf(focusChangeHint, nfr, forceDuck); 804 } 805 806 // push focus requester at the top of the audio focus stack 807 mFocusStack.push(nfr); 808 nfr.handleFocusGainFromRequest(AudioManager.AUDIOFOCUS_REQUEST_GRANTED); 809 } 810 notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(), 811 AudioManager.AUDIOFOCUS_REQUEST_GRANTED); 812 813 if (ENFORCE_MUTING_FOR_RING_OR_CALL & enteringRingOrCall) { 814 runAudioCheckerForRingOrCallAsync(true/*enteringRingOrCall*/); 815 } 816 }//synchronized(mAudioFocusLock) 817 818 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 819 } 820 821 /** 822 * @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener, AudioAttributes) 823 * */ 824 protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa, 825 String callingPackageName) { 826 // AudioAttributes are currently ignored, to be used for zones / a11y 827 mEventLogger.log((new AudioEventLogger.StringEvent( 828 "abandonAudioFocus() from uid/pid " + Binder.getCallingUid() 829 + "/" + Binder.getCallingPid() 830 + " clientId=" + clientId)) 831 .printLog(TAG)); 832 try { 833 // this will take care of notifying the new focus owner if needed 834 synchronized(mAudioFocusLock) { 835 // external focus policy? 836 if (mFocusPolicy != null) { 837 final AudioFocusInfo afi = new AudioFocusInfo(aa, Binder.getCallingUid(), 838 clientId, callingPackageName, 0 /*gainRequest*/, 0 /*lossReceived*/, 839 0 /*flags*/, 0 /* sdk n/a here*/); 840 if (notifyExtFocusPolicyFocusAbandon_syncAf(afi)) { 841 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 842 } 843 } 844 845 boolean exitingRingOrCall = mRingOrCallActive 846 & (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0); 847 if (exitingRingOrCall) { mRingOrCallActive = false; } 848 849 removeFocusStackEntry(clientId, true /*signal*/, true /*notifyFocusFollowers*/); 850 851 if (ENFORCE_MUTING_FOR_RING_OR_CALL & exitingRingOrCall) { 852 runAudioCheckerForRingOrCallAsync(false/*enteringRingOrCall*/); 853 } 854 } 855 } catch (java.util.ConcurrentModificationException cme) { 856 // Catching this exception here is temporary. It is here just to prevent 857 // a crash seen when the "Silent" notification is played. This is believed to be fixed 858 // but this try catch block is left just to be safe. 859 Log.e(TAG, "FATAL EXCEPTION AudioFocus abandonAudioFocus() caused " + cme); 860 cme.printStackTrace(); 861 } 862 863 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 864 } 865 866 867 protected void unregisterAudioFocusClient(String clientId) { 868 synchronized(mAudioFocusLock) { 869 removeFocusStackEntry(clientId, false, true /*notifyFocusFollowers*/); 870 } 871 } 872 873 private void runAudioCheckerForRingOrCallAsync(final boolean enteringRingOrCall) { 874 new Thread() { 875 public void run() { 876 if (enteringRingOrCall) { 877 try { 878 Thread.sleep(RING_CALL_MUTING_ENFORCEMENT_DELAY_MS); 879 } catch (InterruptedException e) { 880 e.printStackTrace(); 881 } 882 } 883 synchronized (mAudioFocusLock) { 884 // since the new thread starting running the state could have changed, so 885 // we need to check again mRingOrCallActive, not enteringRingOrCall 886 if (mRingOrCallActive) { 887 mFocusEnforcer.mutePlayersForCall(USAGES_TO_MUTE_IN_RING_OR_CALL); 888 } else { 889 mFocusEnforcer.unmutePlayersForCall(); 890 } 891 } 892 } 893 }.start(); 894 } 895 } 896