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