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