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 MediaFocusControl(Context cntxt, PlayerFocusEnforcer pfe)90 protected MediaFocusControl(Context cntxt, PlayerFocusEnforcer pfe) { 91 mContext = cntxt; 92 mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE); 93 mFocusEnforcer = pfe; 94 } 95 dump(PrintWriter pw)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 duckPlayers(FocusRequester winner, FocusRequester loser, boolean forceDuck)108 public boolean duckPlayers(FocusRequester winner, FocusRequester loser, boolean forceDuck) { 109 return mFocusEnforcer.duckPlayers(winner, loser, forceDuck); 110 } 111 112 @Override unduckPlayers(FocusRequester winner)113 public void unduckPlayers(FocusRequester winner) { 114 mFocusEnforcer.unduckPlayers(winner); 115 } 116 117 @Override mutePlayersForCall(int[] usagesToMute)118 public void mutePlayersForCall(int[] usagesToMute) { 119 mFocusEnforcer.mutePlayersForCall(usagesToMute); 120 } 121 122 @Override unmutePlayersForCall()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 */ discardAudioFocusOwner()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") notifyTopOfAudioFocusStack()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") propagateFocusLossFromGain_syncAf(int focusGain, final FocusRequester fr, boolean forceDuck)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 */ dumpFocusStack(PrintWriter pw)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") removeFocusStackEntry(String clientToRemove, boolean signal, boolean notifyFocusFollowers)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") removeFocusStackEntryOnDeath(IBinder cb)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") removeFocusEntryForExtPolicy(IBinder cb)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 */ canReassignAudioFocus()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 isLockedFocusOwner(FocusRequester fr)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") pushBelowLockedFocusOwners(FocusRequester nfr)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 AudioFocusDeathHandler(IBinder cb)372 AudioFocusDeathHandler(IBinder cb) { 373 mCb = cb; 374 } 375 binderDied()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 setDuckingInExtPolicyAvailable(boolean available)395 protected void setDuckingInExtPolicyAvailable(boolean available) { 396 mNotifyFocusOwnerOnDuck = !available; 397 } 398 mustNotifyFocusOwnerOnDuck()399 boolean mustNotifyFocusOwnerOnDuck() { return mNotifyFocusOwnerOnDuck; } 400 401 private ArrayList<IAudioPolicyCallback> mFocusFollowers = new ArrayList<IAudioPolicyCallback>(); 402 addFocusFollower(IAudioPolicyCallback ff)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 removeFocusFollower(IAudioPolicyCallback ff)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 setFocusPolicy(IAudioPolicyCallback policy)446 void setFocusPolicy(IAudioPolicyCallback policy) { 447 if (policy == null) { 448 return; 449 } 450 synchronized (mAudioFocusLock) { 451 mFocusPolicy = policy; 452 } 453 } 454 unsetFocusPolicy(IAudioPolicyCallback policy)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 */ notifyExtPolicyCurrentFocusAsync(IAudioPolicyCallback pcb)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 */ notifyExtPolicyFocusGrant_syncAf(AudioFocusInfo afi, int requestResult)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 */ notifyExtPolicyFocusLoss_syncAf(AudioFocusInfo afi, boolean wasDispatched)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 */ notifyExtFocusPolicyFocusRequest_syncAf(AudioFocusInfo afi, IAudioFocusDispatcher fd, IBinder cb)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 setFocusRequestResultFromExtPolicy(AudioFocusInfo afi, int requestResult)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 */ notifyExtFocusPolicyFocusAbandon_syncAf(AudioFocusInfo afi)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) */ dispatchFocusChange(AudioFocusInfo afi, int focusChange)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 dumpExtFocusPolicyFocusOwners(PrintWriter pw)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 getCurrentAudioFocus()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 */ getFocusRampTimeMs(int focusGain, AudioAttributes attr)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) */ requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb, IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags, int sdk, boolean forceDuck)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 * */ abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa, String callingPackageName)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 unregisterAudioFocusClient(String clientId)867 protected void unregisterAudioFocusClient(String clientId) { 868 synchronized(mAudioFocusLock) { 869 removeFocusStackEntry(clientId, false, true /*notifyFocusFollowers*/); 870 } 871 } 872 runAudioCheckerForRingOrCallAsync(final boolean enteringRingOrCall)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