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