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.ContentResolver; 23 import android.content.Context; 24 import android.media.AudioAttributes; 25 import android.media.AudioFocusInfo; 26 import android.media.AudioManager; 27 import android.media.AudioSystem; 28 import android.media.IAudioFocusDispatcher; 29 import android.media.MediaMetrics; 30 import android.media.audiopolicy.AudioPolicy; 31 import android.media.audiopolicy.IAudioPolicyCallback; 32 import android.os.Binder; 33 import android.os.Build; 34 import android.os.Handler; 35 import android.os.HandlerThread; 36 import android.os.IBinder; 37 import android.os.Message; 38 import android.os.RemoteException; 39 import android.provider.Settings; 40 import android.util.Log; 41 42 import com.android.internal.annotations.GuardedBy; 43 import com.android.server.utils.EventLogger; 44 45 import java.io.PrintWriter; 46 import java.text.DateFormat; 47 import java.util.ArrayList; 48 import java.util.Date; 49 import java.util.HashMap; 50 import java.util.Iterator; 51 import java.util.LinkedList; 52 import java.util.List; 53 import java.util.Map.Entry; 54 import java.util.Set; 55 import java.util.Stack; 56 57 /** 58 * @hide 59 * 60 */ 61 public class MediaFocusControl implements PlayerFocusEnforcer { 62 63 private static final String TAG = "MediaFocusControl"; 64 static final boolean DEBUG = false; 65 66 /** 67 * set to true so the framework enforces ducking itself, without communicating to apps 68 * that they lost focus for most use cases. 69 */ 70 static final boolean ENFORCE_DUCKING = true; 71 /** 72 * set to true to the framework enforces ducking itself only with apps above a given SDK 73 * target level. Is ignored if ENFORCE_DUCKING is false. 74 */ 75 static final boolean ENFORCE_DUCKING_FOR_NEW = true; 76 /** 77 * the SDK level (included) up to which the framework doesn't enforce ducking itself. Is ignored 78 * if ENFORCE_DUCKING_FOR_NEW is false; 79 */ 80 // automatic ducking was introduced for Android O 81 static final int DUCKING_IN_APP_SDK_LEVEL = Build.VERSION_CODES.N_MR1; 82 /** 83 * set to true so the framework enforces muting media/game itself when the device is ringing 84 * or in a call. 85 */ 86 static final boolean ENFORCE_MUTING_FOR_RING_OR_CALL = true; 87 88 /** 89 * set to true so the framework enforces fading out apps that lose audio focus in a 90 * non-transient way. 91 */ 92 static final boolean ENFORCE_FADEOUT_FOR_FOCUS_LOSS = true; 93 94 private final Context mContext; 95 private final AppOpsManager mAppOps; 96 private PlayerFocusEnforcer mFocusEnforcer; // never null 97 private boolean mMultiAudioFocusEnabled = false; 98 99 private boolean mRingOrCallActive = false; 100 101 private final Object mExtFocusChangeLock = new Object(); 102 @GuardedBy("mExtFocusChangeLock") 103 private long mExtFocusChangeCounter; 104 MediaFocusControl(Context cntxt, PlayerFocusEnforcer pfe)105 protected MediaFocusControl(Context cntxt, PlayerFocusEnforcer pfe) { 106 mContext = cntxt; 107 mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE); 108 mFocusEnforcer = pfe; 109 final ContentResolver cr = mContext.getContentResolver(); 110 mMultiAudioFocusEnabled = Settings.System.getIntForUser(cr, 111 Settings.System.MULTI_AUDIO_FOCUS_ENABLED, 0, cr.getUserId()) != 0; 112 initFocusThreading(); 113 } 114 dump(PrintWriter pw)115 protected void dump(PrintWriter pw) { 116 pw.println("\nMediaFocusControl dump time: " 117 + DateFormat.getTimeInstance().format(new Date())); 118 dumpFocusStack(pw); 119 pw.println("\n"); 120 // log 121 mEventLogger.dump(pw); 122 dumpMultiAudioFocus(pw); 123 } 124 125 //================================================================= 126 // PlayerFocusEnforcer implementation 127 @Override duckPlayers(@onNull FocusRequester winner, @NonNull FocusRequester loser, boolean forceDuck)128 public boolean duckPlayers(@NonNull FocusRequester winner, @NonNull FocusRequester loser, 129 boolean forceDuck) { 130 return mFocusEnforcer.duckPlayers(winner, loser, forceDuck); 131 } 132 133 @Override restoreVShapedPlayers(@onNull FocusRequester winner)134 public void restoreVShapedPlayers(@NonNull FocusRequester winner) { 135 mFocusEnforcer.restoreVShapedPlayers(winner); 136 // remove scheduled events to unfade out offending players (if any) corresponding to 137 // this uid, as we're removing any effects of muting/ducking/fade out now 138 mFocusHandler.removeEqualMessages(MSL_L_FORGET_UID, 139 new ForgetFadeUidInfo(winner.getClientUid())); 140 141 } 142 143 @Override mutePlayersForCall(int[] usagesToMute)144 public void mutePlayersForCall(int[] usagesToMute) { 145 mFocusEnforcer.mutePlayersForCall(usagesToMute); 146 } 147 148 @Override unmutePlayersForCall()149 public void unmutePlayersForCall() { 150 mFocusEnforcer.unmutePlayersForCall(); 151 } 152 153 @Override fadeOutPlayers(@onNull FocusRequester winner, @NonNull FocusRequester loser)154 public boolean fadeOutPlayers(@NonNull FocusRequester winner, @NonNull FocusRequester loser) { 155 return mFocusEnforcer.fadeOutPlayers(winner, loser); 156 } 157 158 @Override forgetUid(int uid)159 public void forgetUid(int uid) { 160 mFocusEnforcer.forgetUid(uid); 161 } 162 163 //========================================================================================== 164 // AudioFocus 165 //========================================================================================== 166 167 private final static Object mAudioFocusLock = new Object(); 168 169 /** 170 * Arbitrary maximum size of audio focus stack to prevent apps OOM'ing this process. 171 */ 172 private static final int MAX_STACK_SIZE = 100; 173 174 private static final EventLogger 175 mEventLogger = new EventLogger(50, 176 "focus commands as seen by MediaFocusControl"); 177 178 private static final String mMetricsId = MediaMetrics.Name.AUDIO_FOCUS; 179 noFocusForSuspendedApp(@onNull String packageName, int uid)180 /*package*/ void noFocusForSuspendedApp(@NonNull String packageName, int uid) { 181 synchronized (mAudioFocusLock) { 182 final Iterator<FocusRequester> stackIterator = mFocusStack.iterator(); 183 List<String> clientsToRemove = new ArrayList<>(); 184 while (stackIterator.hasNext()) { 185 final FocusRequester focusOwner = stackIterator.next(); 186 if (focusOwner.hasSameUid(uid) && focusOwner.hasSamePackage(packageName)) { 187 clientsToRemove.add(focusOwner.getClientId()); 188 mEventLogger.enqueue((new EventLogger.StringEvent( 189 "focus owner:" + focusOwner.getClientId() 190 + " in uid:" + uid + " pack: " + packageName 191 + " getting AUDIOFOCUS_LOSS due to app suspension")) 192 .printLog(TAG)); 193 // make the suspended app lose focus through its focus listener (if any) 194 focusOwner.dispatchFocusChange(AudioManager.AUDIOFOCUS_LOSS); 195 } 196 } 197 for (String clientToRemove : clientsToRemove) { 198 // update the stack but don't signal the change. 199 removeFocusStackEntry(clientToRemove, false, true); 200 } 201 } 202 } 203 hasAudioFocusUsers()204 /*package*/ boolean hasAudioFocusUsers() { 205 synchronized (mAudioFocusLock) { 206 return !mFocusStack.empty(); 207 } 208 } 209 210 /** 211 * Discard the current audio focus owner. 212 * Notify top of audio focus stack that it lost focus (regardless of possibility to reassign 213 * focus), remove it from the stack, and clear the remote control display. 214 */ discardAudioFocusOwner()215 protected void discardAudioFocusOwner() { 216 synchronized(mAudioFocusLock) { 217 if (!mFocusStack.empty()) { 218 // notify the current focus owner it lost focus after removing it from stack 219 final FocusRequester exFocusOwner = mFocusStack.pop(); 220 exFocusOwner.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null, 221 false /*forceDuck*/); 222 exFocusOwner.release(); 223 } 224 } 225 } 226 227 /** 228 * Return a copy of the focus stack for external consumption (composed of AudioFocusInfo 229 * instead of FocusRequester instances) 230 * @return a SystemApi-friendly version of the focus stack, in the same order (last entry 231 * is top of focus stack, i.e. latest focus owner) 232 * @see AudioPolicy#getFocusStack() 233 */ getFocusStack()234 @NonNull List<AudioFocusInfo> getFocusStack() { 235 synchronized (mAudioFocusLock) { 236 final ArrayList<AudioFocusInfo> stack = new ArrayList<>(mFocusStack.size()); 237 for (FocusRequester fr : mFocusStack) { 238 stack.add(fr.toAudioFocusInfo()); 239 } 240 return stack; 241 } 242 } 243 244 /** 245 * Send AUDIOFOCUS_LOSS to a specific stack entry. 246 * Note this method is supporting an external API, and is restricted to LOSS in order to 247 * prevent allowing the stack to be in an invalid state (e.g. entry inside stack has focus) 248 * @param focusLoser the stack entry that is exiting the stack through a focus loss 249 * @return false if the focusLoser wasn't found in the stack, true otherwise 250 * @see AudioPolicy#sendFocusLoss(AudioFocusInfo) 251 */ sendFocusLoss(@onNull AudioFocusInfo focusLoser)252 boolean sendFocusLoss(@NonNull AudioFocusInfo focusLoser) { 253 synchronized (mAudioFocusLock) { 254 FocusRequester loserToRemove = null; 255 for (FocusRequester fr : mFocusStack) { 256 if (fr.getClientId().equals(focusLoser.getClientId())) { 257 fr.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null, 258 false /*forceDuck*/); 259 loserToRemove = fr; 260 break; 261 } 262 } 263 if (loserToRemove != null) { 264 mFocusStack.remove(loserToRemove); 265 loserToRemove.release(); 266 return true; 267 } 268 } 269 return false; 270 } 271 272 @GuardedBy("mAudioFocusLock") notifyTopOfAudioFocusStack()273 private void notifyTopOfAudioFocusStack() { 274 // notify the top of the stack it gained focus 275 if (!mFocusStack.empty()) { 276 if (canReassignAudioFocus()) { 277 mFocusStack.peek().handleFocusGain(AudioManager.AUDIOFOCUS_GAIN); 278 } 279 } 280 281 if (mMultiAudioFocusEnabled && !mMultiAudioFocusList.isEmpty()) { 282 for (FocusRequester multifr : mMultiAudioFocusList) { 283 if (isLockedFocusOwner(multifr)) { 284 multifr.handleFocusGain(AudioManager.AUDIOFOCUS_GAIN); 285 } 286 } 287 } 288 } 289 290 /** 291 * Focus is requested, propagate the associated loss throughout the stack. 292 * Will also remove entries in the stack that have just received a definitive loss of focus. 293 * @param focusGain the new focus gain that will later be added at the top of the stack 294 */ 295 @GuardedBy("mAudioFocusLock") propagateFocusLossFromGain_syncAf(int focusGain, final FocusRequester fr, boolean forceDuck)296 private void propagateFocusLossFromGain_syncAf(int focusGain, final FocusRequester fr, 297 boolean forceDuck) { 298 final List<String> clientsToRemove = new LinkedList<String>(); 299 // going through the audio focus stack to signal new focus, traversing order doesn't 300 // matter as all entries respond to the same external focus gain 301 if (!mFocusStack.empty()) { 302 for (FocusRequester focusLoser : mFocusStack) { 303 final boolean isDefinitiveLoss = 304 focusLoser.handleFocusLossFromGain(focusGain, fr, forceDuck); 305 if (isDefinitiveLoss) { 306 clientsToRemove.add(focusLoser.getClientId()); 307 } 308 } 309 } 310 311 if (mMultiAudioFocusEnabled && !mMultiAudioFocusList.isEmpty()) { 312 for (FocusRequester multifocusLoser : mMultiAudioFocusList) { 313 final boolean isDefinitiveLoss = 314 multifocusLoser.handleFocusLossFromGain(focusGain, fr, forceDuck); 315 if (isDefinitiveLoss) { 316 clientsToRemove.add(multifocusLoser.getClientId()); 317 } 318 } 319 } 320 321 for (String clientToRemove : clientsToRemove) { 322 removeFocusStackEntry(clientToRemove, false /*signal*/, 323 true /*notifyFocusFollowers*/); 324 } 325 } 326 327 private final Stack<FocusRequester> mFocusStack = new Stack<FocusRequester>(); 328 329 ArrayList<FocusRequester> mMultiAudioFocusList = new ArrayList<FocusRequester>(); 330 331 /** 332 * Helper function: 333 * Display in the log the current entries in the audio focus stack 334 */ dumpFocusStack(PrintWriter pw)335 private void dumpFocusStack(PrintWriter pw) { 336 pw.println("\nAudio Focus stack entries (last is top of stack):"); 337 synchronized(mAudioFocusLock) { 338 Iterator<FocusRequester> stackIterator = mFocusStack.iterator(); 339 while(stackIterator.hasNext()) { 340 stackIterator.next().dump(pw); 341 } 342 pw.println("\n"); 343 if (mFocusPolicy == null) { 344 pw.println("No external focus policy\n"); 345 } else { 346 pw.println("External focus policy: "+ mFocusPolicy + ", focus owners:\n"); 347 dumpExtFocusPolicyFocusOwners(pw); 348 } 349 } 350 pw.println("\n"); 351 pw.println(" Notify on duck: " + mNotifyFocusOwnerOnDuck + "\n"); 352 pw.println(" In ring or call: " + mRingOrCallActive + "\n"); 353 } 354 355 /** 356 * Remove a focus listener from the focus stack. 357 * @param clientToRemove the focus listener 358 * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding 359 * focus, notify the next item in the stack it gained focus. 360 */ 361 @GuardedBy("mAudioFocusLock") removeFocusStackEntry(String clientToRemove, boolean signal, boolean notifyFocusFollowers)362 private void removeFocusStackEntry(String clientToRemove, boolean signal, 363 boolean notifyFocusFollowers) { 364 AudioFocusInfo abandonSource = null; 365 // is the current top of the focus stack abandoning focus? (because of request, not death) 366 if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientToRemove)) 367 { 368 //Log.i(TAG, " removeFocusStackEntry() removing top of stack"); 369 FocusRequester fr = mFocusStack.pop(); 370 fr.maybeRelease(); 371 if (notifyFocusFollowers) { 372 abandonSource = fr.toAudioFocusInfo(); 373 } 374 if (signal) { 375 // notify the new top of the stack it gained focus 376 notifyTopOfAudioFocusStack(); 377 } 378 } else { 379 // focus is abandoned by a client that's not at the top of the stack, 380 // no need to update focus. 381 // (using an iterator on the stack so we can safely remove an entry after having 382 // evaluated it, traversal order doesn't matter here) 383 Iterator<FocusRequester> stackIterator = mFocusStack.iterator(); 384 while(stackIterator.hasNext()) { 385 FocusRequester fr = stackIterator.next(); 386 if(fr.hasSameClient(clientToRemove)) { 387 Log.i(TAG, "AudioFocus removeFocusStackEntry(): removing entry for " 388 + clientToRemove); 389 stackIterator.remove(); 390 if (notifyFocusFollowers) { 391 abandonSource = fr.toAudioFocusInfo(); 392 } 393 // stack entry not used anymore, clear references 394 fr.maybeRelease(); 395 } 396 } 397 } 398 // focus followers still want to know focus was abandoned, handled as a loss 399 if (abandonSource != null) { 400 abandonSource.clearLossReceived(); 401 notifyExtPolicyFocusLoss_syncAf(abandonSource, false); 402 } 403 404 if (mMultiAudioFocusEnabled && !mMultiAudioFocusList.isEmpty()) { 405 Iterator<FocusRequester> listIterator = mMultiAudioFocusList.iterator(); 406 while (listIterator.hasNext()) { 407 FocusRequester fr = listIterator.next(); 408 if (fr.hasSameClient(clientToRemove)) { 409 listIterator.remove(); 410 fr.release(); 411 } 412 } 413 414 if (signal) { 415 // notify the new top of the stack it gained focus 416 notifyTopOfAudioFocusStack(); 417 } 418 } 419 } 420 421 /** 422 * Remove focus listeners from the focus stack for a particular client when it has died. 423 */ 424 @GuardedBy("mAudioFocusLock") removeFocusStackEntryOnDeath(IBinder cb)425 private void removeFocusStackEntryOnDeath(IBinder cb) { 426 // is the owner of the audio focus part of the client to remove? 427 boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() && 428 mFocusStack.peek().hasSameBinder(cb); 429 // (using an iterator on the stack so we can safely remove an entry after having 430 // evaluated it, traversal order doesn't matter here) 431 Iterator<FocusRequester> stackIterator = mFocusStack.iterator(); 432 while(stackIterator.hasNext()) { 433 FocusRequester fr = stackIterator.next(); 434 if(fr.hasSameBinder(cb)) { 435 Log.i(TAG, "AudioFocus removeFocusStackEntryOnDeath(): removing entry for " + cb); 436 mEventLogger.enqueue(new EventLogger.StringEvent( 437 "focus requester:" + fr.getClientId() 438 + " in uid:" + fr.getClientUid() 439 + " pack:" + fr.getPackageName() 440 + " died")); 441 notifyExtPolicyFocusLoss_syncAf(fr.toAudioFocusInfo(), false); 442 443 stackIterator.remove(); 444 // stack entry not used anymore, clear references 445 fr.release(); 446 } 447 } 448 if (isTopOfStackForClientToRemove) { 449 // we removed an entry at the top of the stack: 450 // notify the new top of the stack it gained focus. 451 notifyTopOfAudioFocusStack(); 452 } 453 } 454 455 /** 456 * Helper function for external focus policy: 457 * Remove focus listeners from the list of potential focus owners for a particular client when 458 * it has died. 459 */ 460 @GuardedBy("mAudioFocusLock") removeFocusEntryForExtPolicyOnDeath(IBinder cb)461 private void removeFocusEntryForExtPolicyOnDeath(IBinder cb) { 462 if (mFocusOwnersForFocusPolicy.isEmpty()) { 463 return; 464 } 465 boolean released = false; 466 final Set<Entry<String, FocusRequester>> owners = mFocusOwnersForFocusPolicy.entrySet(); 467 final Iterator<Entry<String, FocusRequester>> ownerIterator = owners.iterator(); 468 while (ownerIterator.hasNext()) { 469 final Entry<String, FocusRequester> owner = ownerIterator.next(); 470 final FocusRequester fr = owner.getValue(); 471 if (fr.hasSameBinder(cb)) { 472 ownerIterator.remove(); 473 mEventLogger.enqueue(new EventLogger.StringEvent( 474 "focus requester:" + fr.getClientId() 475 + " in uid:" + fr.getClientUid() 476 + " pack:" + fr.getPackageName() 477 + " died")); 478 fr.release(); 479 notifyExtFocusPolicyFocusAbandon_syncAf(fr.toAudioFocusInfo()); 480 break; 481 } 482 } 483 } 484 485 /** 486 * Helper function: 487 * Returns true if the system is in a state where the focus can be reevaluated, false otherwise. 488 * The implementation guarantees that a state where focus cannot be immediately reassigned 489 * implies that an "locked" focus owner is at the top of the focus stack. 490 * Modifications to the implementation that break this assumption will cause focus requests to 491 * misbehave when honoring the AudioManager.AUDIOFOCUS_FLAG_DELAY_OK flag. 492 */ canReassignAudioFocus()493 private boolean canReassignAudioFocus() { 494 // focus requests are rejected during a phone call or when the phone is ringing 495 // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus 496 if (!mFocusStack.isEmpty() && isLockedFocusOwner(mFocusStack.peek())) { 497 return false; 498 } 499 return true; 500 } 501 isLockedFocusOwner(FocusRequester fr)502 private boolean isLockedFocusOwner(FocusRequester fr) { 503 return (fr.hasSameClient(AudioSystem.IN_VOICE_COMM_FOCUS_ID) || fr.isLockedFocusOwner()); 504 } 505 506 /** 507 * Helper function 508 * Pre-conditions: focus stack is not empty, there is one or more locked focus owner 509 * at the top of the focus stack 510 * Push the focus requester onto the audio focus stack at the first position immediately 511 * following the locked focus owners. 512 * Propagate through the stack the changes that the new (future) focus owner causes. 513 * @param nfr the future focus owner that will gain focus when the locked focus owners are 514 * removed from the stack 515 * @return {@link AudioManager#AUDIOFOCUS_REQUEST_GRANTED} or 516 * {@link AudioManager#AUDIOFOCUS_REQUEST_DELAYED} 517 */ 518 @GuardedBy("mAudioFocusLock") pushBelowLockedFocusOwnersAndPropagate(FocusRequester nfr)519 private int pushBelowLockedFocusOwnersAndPropagate(FocusRequester nfr) { 520 if (DEBUG) { 521 Log.v(TAG, "pushBelowLockedFocusOwnersAndPropagate client=" + nfr.getClientId()); 522 } 523 int lastLockedFocusOwnerIndex = mFocusStack.size(); 524 for (int index = mFocusStack.size() - 1; index >= 0; index--) { 525 if (isLockedFocusOwner(mFocusStack.elementAt(index))) { 526 lastLockedFocusOwnerIndex = index; 527 } 528 } 529 if (lastLockedFocusOwnerIndex == mFocusStack.size()) { 530 // this should not happen, but handle it and log an error 531 Log.e(TAG, "No exclusive focus owner found in propagateFocusLossFromGain_syncAf()", 532 new Exception()); 533 // no exclusive owner, push at top of stack, focus is granted, propagate change 534 propagateFocusLossFromGain_syncAf(nfr.getGainRequest(), nfr, false /*forceDuck*/); 535 mFocusStack.push(nfr); 536 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 537 } 538 539 if (DEBUG) { 540 Log.v(TAG, "> lastLockedFocusOwnerIndex=" + lastLockedFocusOwnerIndex); 541 } 542 mFocusStack.insertElementAt(nfr, lastLockedFocusOwnerIndex); 543 544 // propagate potential focus loss (and removal from stack) after the newly 545 // inserted FocusRequester (at index lastLockedFocusOwnerIndex-1) 546 final List<String> clientsToRemove = new LinkedList<String>(); 547 for (int index = lastLockedFocusOwnerIndex - 1; index >= 0; index--) { 548 final boolean isDefinitiveLoss = 549 mFocusStack.elementAt(index).handleFocusLossFromGain( 550 nfr.getGainRequest(), nfr, false /*forceDuck*/); 551 if (isDefinitiveLoss) { 552 clientsToRemove.add(mFocusStack.elementAt(index).getClientId()); 553 } 554 } 555 for (String clientToRemove : clientsToRemove) { 556 if (DEBUG) { 557 Log.v(TAG, "> removing focus client " + clientToRemove); 558 } 559 removeFocusStackEntry(clientToRemove, false /*signal*/, 560 true /*notifyFocusFollowers*/); 561 } 562 563 return AudioManager.AUDIOFOCUS_REQUEST_DELAYED; 564 } 565 566 /** 567 * Inner class to monitor audio focus client deaths, and remove them from the audio focus 568 * stack if necessary. 569 */ 570 protected class AudioFocusDeathHandler implements IBinder.DeathRecipient { 571 private IBinder mCb; // To be notified of client's death 572 AudioFocusDeathHandler(IBinder cb)573 AudioFocusDeathHandler(IBinder cb) { 574 mCb = cb; 575 } 576 binderDied()577 public void binderDied() { 578 synchronized(mAudioFocusLock) { 579 if (mFocusPolicy != null) { 580 removeFocusEntryForExtPolicyOnDeath(mCb); 581 } else { 582 removeFocusStackEntryOnDeath(mCb); 583 if (mMultiAudioFocusEnabled && !mMultiAudioFocusList.isEmpty()) { 584 Iterator<FocusRequester> listIterator = mMultiAudioFocusList.iterator(); 585 while (listIterator.hasNext()) { 586 FocusRequester fr = listIterator.next(); 587 if (fr.hasSameBinder(mCb)) { 588 listIterator.remove(); 589 fr.release(); 590 } 591 } 592 } 593 } 594 } 595 } 596 } 597 598 /** 599 * Indicates whether to notify an audio focus owner when it loses focus 600 * with {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK} if it will only duck. 601 * This variable being false indicates an AudioPolicy has been registered and has signaled 602 * it will handle audio ducking. 603 */ 604 private boolean mNotifyFocusOwnerOnDuck = true; 605 setDuckingInExtPolicyAvailable(boolean available)606 protected void setDuckingInExtPolicyAvailable(boolean available) { 607 mNotifyFocusOwnerOnDuck = !available; 608 } 609 mustNotifyFocusOwnerOnDuck()610 boolean mustNotifyFocusOwnerOnDuck() { return mNotifyFocusOwnerOnDuck; } 611 612 private ArrayList<IAudioPolicyCallback> mFocusFollowers = new ArrayList<IAudioPolicyCallback>(); 613 addFocusFollower(IAudioPolicyCallback ff)614 void addFocusFollower(IAudioPolicyCallback ff) { 615 if (ff == null) { 616 return; 617 } 618 synchronized(mAudioFocusLock) { 619 boolean found = false; 620 for (IAudioPolicyCallback pcb : mFocusFollowers) { 621 if (pcb.asBinder().equals(ff.asBinder())) { 622 found = true; 623 break; 624 } 625 } 626 if (found) { 627 return; 628 } else { 629 mFocusFollowers.add(ff); 630 notifyExtPolicyCurrentFocusAsync(ff); 631 } 632 } 633 } 634 removeFocusFollower(IAudioPolicyCallback ff)635 void removeFocusFollower(IAudioPolicyCallback ff) { 636 if (ff == null) { 637 return; 638 } 639 synchronized(mAudioFocusLock) { 640 for (IAudioPolicyCallback pcb : mFocusFollowers) { 641 if (pcb.asBinder().equals(ff.asBinder())) { 642 mFocusFollowers.remove(pcb); 643 break; 644 } 645 } 646 } 647 } 648 649 /** The current audio focus policy */ 650 @GuardedBy("mAudioFocusLock") 651 @Nullable private IAudioPolicyCallback mFocusPolicy = null; 652 /** 653 * The audio focus policy that was registered before a test focus policy was registered 654 * during a test 655 */ 656 @GuardedBy("mAudioFocusLock") 657 @Nullable private IAudioPolicyCallback mPreviousFocusPolicy = null; 658 659 // Since we don't have a stack of focus owners when using an external focus policy, we keep 660 // track of all the focus requesters in this map, with their clientId as the key. This is 661 // used both for focus dispatch and death handling 662 private HashMap<String, FocusRequester> mFocusOwnersForFocusPolicy = 663 new HashMap<String, FocusRequester>(); 664 setFocusPolicy(IAudioPolicyCallback policy, boolean isTestFocusPolicy)665 void setFocusPolicy(IAudioPolicyCallback policy, boolean isTestFocusPolicy) { 666 if (policy == null) { 667 return; 668 } 669 synchronized (mAudioFocusLock) { 670 if (isTestFocusPolicy) { 671 mPreviousFocusPolicy = mFocusPolicy; 672 } 673 mFocusPolicy = policy; 674 } 675 } 676 unsetFocusPolicy(IAudioPolicyCallback policy, boolean isTestFocusPolicy)677 void unsetFocusPolicy(IAudioPolicyCallback policy, boolean isTestFocusPolicy) { 678 if (policy == null) { 679 return; 680 } 681 synchronized (mAudioFocusLock) { 682 if (mFocusPolicy == policy) { 683 if (isTestFocusPolicy) { 684 // restore the focus policy that was there before the focus policy test started 685 mFocusPolicy = mPreviousFocusPolicy; 686 } else { 687 mFocusPolicy = null; 688 } 689 } 690 } 691 } 692 693 /** 694 * @param pcb non null 695 */ notifyExtPolicyCurrentFocusAsync(IAudioPolicyCallback pcb)696 void notifyExtPolicyCurrentFocusAsync(IAudioPolicyCallback pcb) { 697 final IAudioPolicyCallback pcb2 = pcb; 698 final Thread thread = new Thread() { 699 @Override 700 public void run() { 701 synchronized(mAudioFocusLock) { 702 if (mFocusStack.isEmpty()) { 703 return; 704 } 705 try { 706 pcb2.notifyAudioFocusGrant(mFocusStack.peek().toAudioFocusInfo(), 707 // top of focus stack always has focus 708 AudioManager.AUDIOFOCUS_REQUEST_GRANTED); 709 } catch (RemoteException e) { 710 Log.e(TAG, "Can't call notifyAudioFocusGrant() on IAudioPolicyCallback " 711 + pcb2.asBinder(), e); 712 } 713 } 714 } 715 }; 716 thread.start(); 717 } 718 719 /** 720 * Called synchronized on mAudioFocusLock 721 */ notifyExtPolicyFocusGrant_syncAf(AudioFocusInfo afi, int requestResult)722 void notifyExtPolicyFocusGrant_syncAf(AudioFocusInfo afi, int requestResult) { 723 for (IAudioPolicyCallback pcb : mFocusFollowers) { 724 try { 725 // oneway 726 pcb.notifyAudioFocusGrant(afi, requestResult); 727 } catch (RemoteException e) { 728 Log.e(TAG, "Can't call notifyAudioFocusGrant() on IAudioPolicyCallback " 729 + pcb.asBinder(), e); 730 } 731 } 732 } 733 734 /** 735 * Called synchronized on mAudioFocusLock 736 */ notifyExtPolicyFocusLoss_syncAf(AudioFocusInfo afi, boolean wasDispatched)737 void notifyExtPolicyFocusLoss_syncAf(AudioFocusInfo afi, boolean wasDispatched) { 738 for (IAudioPolicyCallback pcb : mFocusFollowers) { 739 try { 740 // oneway 741 pcb.notifyAudioFocusLoss(afi, wasDispatched); 742 } catch (RemoteException e) { 743 Log.e(TAG, "Can't call notifyAudioFocusLoss() on IAudioPolicyCallback " 744 + pcb.asBinder(), e); 745 } 746 } 747 } 748 749 /** 750 * Called synchronized on mAudioFocusLock. 751 * Can only be called with an external focus policy installed (mFocusPolicy != null) 752 * @param afi 753 * @param fd 754 * @param cb binder of the focus requester 755 * @return true if the external audio focus policy (if any) can handle the focus request, 756 * and false if there was any error handling the request (e.g. error talking to policy, 757 * focus requester is already dead) 758 */ notifyExtFocusPolicyFocusRequest_syncAf(AudioFocusInfo afi, IAudioFocusDispatcher fd, @NonNull IBinder cb)759 boolean notifyExtFocusPolicyFocusRequest_syncAf(AudioFocusInfo afi, 760 IAudioFocusDispatcher fd, @NonNull IBinder cb) { 761 if (DEBUG) { 762 Log.v(TAG, "notifyExtFocusPolicyFocusRequest client="+afi.getClientId() 763 + " dispatcher=" + fd); 764 } 765 synchronized (mExtFocusChangeLock) { 766 afi.setGen(mExtFocusChangeCounter++); 767 } 768 final FocusRequester existingFr = mFocusOwnersForFocusPolicy.get(afi.getClientId()); 769 boolean keepTrack = false; 770 if (existingFr != null) { 771 if (!existingFr.hasSameDispatcher(fd)) { 772 existingFr.release(); 773 keepTrack = true; 774 } 775 } else { 776 keepTrack = true; 777 } 778 if (keepTrack) { 779 final AudioFocusDeathHandler hdlr = new AudioFocusDeathHandler(cb); 780 try { 781 cb.linkToDeath(hdlr, 0); 782 } catch (RemoteException e) { 783 // client has already died! 784 return false; 785 } 786 // new focus (future) focus owner to keep track of 787 mFocusOwnersForFocusPolicy.put(afi.getClientId(), 788 new FocusRequester(afi, fd, cb, hdlr, this)); 789 } 790 791 try { 792 //oneway 793 mFocusPolicy.notifyAudioFocusRequest(afi, AudioManager.AUDIOFOCUS_REQUEST_GRANTED); 794 return true; 795 } catch (RemoteException e) { 796 Log.e(TAG, "Can't call notifyAudioFocusRequest() on IAudioPolicyCallback " 797 + mFocusPolicy.asBinder(), e); 798 } 799 return false; 800 } 801 setFocusRequestResultFromExtPolicy(AudioFocusInfo afi, int requestResult)802 void setFocusRequestResultFromExtPolicy(AudioFocusInfo afi, int requestResult) { 803 synchronized (mExtFocusChangeLock) { 804 if (afi.getGen() > mExtFocusChangeCounter) { 805 return; 806 } 807 } 808 final FocusRequester fr; 809 if (requestResult == AudioManager.AUDIOFOCUS_REQUEST_FAILED) { 810 fr = mFocusOwnersForFocusPolicy.remove(afi.getClientId()); 811 } else { 812 fr = mFocusOwnersForFocusPolicy.get(afi.getClientId()); 813 } 814 if (fr != null) { 815 fr.dispatchFocusResultFromExtPolicy(requestResult); 816 } 817 } 818 819 /** 820 * Called synchronized on mAudioFocusLock 821 * @param afi 822 * @return true if the external audio focus policy (if any) is handling the focus request 823 */ notifyExtFocusPolicyFocusAbandon_syncAf(AudioFocusInfo afi)824 boolean notifyExtFocusPolicyFocusAbandon_syncAf(AudioFocusInfo afi) { 825 if (mFocusPolicy == null) { 826 return false; 827 } 828 final FocusRequester fr = mFocusOwnersForFocusPolicy.remove(afi.getClientId()); 829 if (fr != null) { 830 fr.release(); 831 } 832 try { 833 //oneway 834 mFocusPolicy.notifyAudioFocusAbandon(afi); 835 } catch (RemoteException e) { 836 Log.e(TAG, "Can't call notifyAudioFocusAbandon() on IAudioPolicyCallback " 837 + mFocusPolicy.asBinder(), e); 838 } 839 return true; 840 } 841 842 /** see AudioManager.dispatchFocusChange(AudioFocusInfo afi, int focusChange, AudioPolicy ap) */ dispatchFocusChange(AudioFocusInfo afi, int focusChange)843 int dispatchFocusChange(AudioFocusInfo afi, int focusChange) { 844 if (DEBUG) { 845 Log.v(TAG, "dispatchFocusChange " + focusChange + " to afi client=" 846 + afi.getClientId()); 847 } 848 synchronized (mAudioFocusLock) { 849 if (mFocusPolicy == null) { 850 if (DEBUG) { Log.v(TAG, "> failed: no focus policy" ); } 851 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 852 } 853 final FocusRequester fr; 854 if (focusChange == AudioManager.AUDIOFOCUS_LOSS) { 855 fr = mFocusOwnersForFocusPolicy.remove(afi.getClientId()); 856 } else { 857 fr = mFocusOwnersForFocusPolicy.get(afi.getClientId()); 858 } 859 if (fr == null) { 860 if (DEBUG) { Log.v(TAG, "> failed: no such focus requester known" ); } 861 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 862 } 863 return fr.dispatchFocusChange(focusChange); 864 } 865 } 866 dumpExtFocusPolicyFocusOwners(PrintWriter pw)867 private void dumpExtFocusPolicyFocusOwners(PrintWriter pw) { 868 final Set<Entry<String, FocusRequester>> owners = mFocusOwnersForFocusPolicy.entrySet(); 869 final Iterator<Entry<String, FocusRequester>> ownerIterator = owners.iterator(); 870 while (ownerIterator.hasNext()) { 871 final Entry<String, FocusRequester> owner = ownerIterator.next(); 872 final FocusRequester fr = owner.getValue(); 873 fr.dump(pw); 874 } 875 } 876 getCurrentAudioFocus()877 protected int getCurrentAudioFocus() { 878 synchronized(mAudioFocusLock) { 879 if (mFocusStack.empty()) { 880 return AudioManager.AUDIOFOCUS_NONE; 881 } else { 882 return mFocusStack.peek().getGainRequest(); 883 } 884 } 885 } 886 887 /** 888 * Delay after entering ringing or call mode after which the framework will mute streams 889 * that are still playing. 890 */ 891 private static final int RING_CALL_MUTING_ENFORCEMENT_DELAY_MS = 100; 892 893 /** 894 * Usages to mute when the device rings or is in a call 895 */ 896 private final static int[] USAGES_TO_MUTE_IN_RING_OR_CALL = 897 { AudioAttributes.USAGE_MEDIA, AudioAttributes.USAGE_GAME }; 898 899 /** 900 * Return the volume ramp time expected before playback with the given AudioAttributes would 901 * start after gaining audio focus. 902 * @param attr attributes of the sound about to start playing 903 * @return time in ms 904 */ getFocusRampTimeMs(int focusGain, AudioAttributes attr)905 protected static int getFocusRampTimeMs(int focusGain, AudioAttributes attr) { 906 switch (attr.getUsage()) { 907 case AudioAttributes.USAGE_MEDIA: 908 case AudioAttributes.USAGE_GAME: 909 return 1000; 910 case AudioAttributes.USAGE_ALARM: 911 case AudioAttributes.USAGE_NOTIFICATION_RINGTONE: 912 case AudioAttributes.USAGE_ASSISTANT: 913 case AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY: 914 case AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: 915 case AudioAttributes.USAGE_ANNOUNCEMENT: 916 return 700; 917 case AudioAttributes.USAGE_VOICE_COMMUNICATION: 918 case AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING: 919 case AudioAttributes.USAGE_NOTIFICATION: 920 case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST: 921 case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT: 922 case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED: 923 case AudioAttributes.USAGE_NOTIFICATION_EVENT: 924 case AudioAttributes.USAGE_ASSISTANCE_SONIFICATION: 925 case AudioAttributes.USAGE_VEHICLE_STATUS: 926 return 500; 927 case AudioAttributes.USAGE_EMERGENCY: 928 case AudioAttributes.USAGE_SAFETY: 929 case AudioAttributes.USAGE_UNKNOWN: 930 default: 931 return 0; 932 } 933 } 934 935 /** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int, int) 936 * @param aa 937 * @param focusChangeHint 938 * @param cb 939 * @param fd 940 * @param clientId 941 * @param callingPackageName 942 * @param attributionTag 943 * @param flags 944 * @param sdk 945 * @param forceDuck only true if 946 * {@link android.media.AudioFocusRequest.Builder#setFocusGain(int)} was set to true for 947 * accessibility. 948 * @param testUid ignored if flags doesn't contain AudioManager.AUDIOFOCUS_FLAG_TEST 949 * otherwise the UID being injected for testing 950 * @return 951 */ requestAudioFocus(@onNull AudioAttributes aa, int focusChangeHint, IBinder cb, IAudioFocusDispatcher fd, @NonNull String clientId, @NonNull String callingPackageName, String attributionTag, int flags, int sdk, boolean forceDuck, int testUid)952 protected int requestAudioFocus(@NonNull AudioAttributes aa, int focusChangeHint, IBinder cb, 953 IAudioFocusDispatcher fd, @NonNull String clientId, @NonNull String callingPackageName, 954 String attributionTag, int flags, int sdk, boolean forceDuck, int testUid) { 955 new MediaMetrics.Item(mMetricsId) 956 .setUid(Binder.getCallingUid()) 957 .set(MediaMetrics.Property.CALLING_PACKAGE, callingPackageName) 958 .set(MediaMetrics.Property.CLIENT_NAME, clientId) 959 .set(MediaMetrics.Property.EVENT, "requestAudioFocus") 960 .set(MediaMetrics.Property.FLAGS, flags) 961 .set(MediaMetrics.Property.FOCUS_CHANGE_HINT, 962 AudioManager.audioFocusToString(focusChangeHint)) 963 //.set(MediaMetrics.Property.SDK, sdk) 964 .record(); 965 966 // when using the test API, a fake UID can be injected (testUid is ignored otherwise) 967 // note that the test on flags is not a mask test on purpose, AUDIOFOCUS_FLAG_TEST is 968 // supposed to be alone in bitfield 969 final int uid = (flags == AudioManager.AUDIOFOCUS_FLAG_TEST) 970 ? testUid : Binder.getCallingUid(); 971 mEventLogger.enqueue((new EventLogger.StringEvent( 972 "requestAudioFocus() from uid/pid " + uid 973 + "/" + Binder.getCallingPid() 974 + " AA=" + aa.usageToString() + "/" + aa.contentTypeToString() 975 + " clientId=" + clientId + " callingPack=" + callingPackageName 976 + " req=" + focusChangeHint 977 + " flags=0x" + Integer.toHexString(flags) 978 + " sdk=" + sdk)) 979 .printLog(TAG)); 980 // we need a valid binder callback for clients 981 if (!cb.pingBinder()) { 982 Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting."); 983 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 984 } 985 986 if ((flags != AudioManager.AUDIOFOCUS_FLAG_TEST) 987 // note we're using the real uid for appOp evaluation 988 && (mAppOps.noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, Binder.getCallingUid(), 989 callingPackageName, attributionTag, null) != AppOpsManager.MODE_ALLOWED)) { 990 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 991 } 992 993 synchronized(mAudioFocusLock) { 994 if (mFocusStack.size() > MAX_STACK_SIZE) { 995 Log.e(TAG, "Max AudioFocus stack size reached, failing requestAudioFocus()"); 996 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 997 } 998 999 boolean enteringRingOrCall = !mRingOrCallActive 1000 & (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0); 1001 if (enteringRingOrCall) { mRingOrCallActive = true; } 1002 1003 final AudioFocusInfo afiForExtPolicy; 1004 if (mFocusPolicy != null) { 1005 // construct AudioFocusInfo as it will be communicated to audio focus policy 1006 afiForExtPolicy = new AudioFocusInfo(aa, uid, 1007 clientId, callingPackageName, focusChangeHint, 0 /*lossReceived*/, 1008 flags, sdk); 1009 } else { 1010 afiForExtPolicy = null; 1011 } 1012 1013 // handle delayed focus 1014 boolean focusGrantDelayed = false; 1015 if (!canReassignAudioFocus()) { 1016 if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) == 0) { 1017 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 1018 } else { 1019 // request has AUDIOFOCUS_FLAG_DELAY_OK: focus can't be 1020 // granted right now, so the requester will be inserted in the focus stack 1021 // to receive focus later 1022 focusGrantDelayed = true; 1023 } 1024 } 1025 1026 // external focus policy? 1027 if (mFocusPolicy != null) { 1028 if (notifyExtFocusPolicyFocusRequest_syncAf(afiForExtPolicy, fd, cb)) { 1029 // stop handling focus request here as it is handled by external audio 1030 // focus policy (return code will be handled in AudioManager) 1031 return AudioManager.AUDIOFOCUS_REQUEST_WAITING_FOR_EXT_POLICY; 1032 } else { 1033 // an error occured, client already dead, bail early 1034 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 1035 } 1036 } 1037 1038 // handle the potential premature death of the new holder of the focus 1039 // (premature death == death before abandoning focus) 1040 // Register for client death notification 1041 AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb); 1042 1043 try { 1044 cb.linkToDeath(afdh, 0); 1045 } catch (RemoteException e) { 1046 // client has already died! 1047 Log.w(TAG, "AudioFocus requestAudioFocus() could not link to "+cb+" binder death"); 1048 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 1049 } 1050 1051 if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientId)) { 1052 // if focus is already owned by this client and the reason for acquiring the focus 1053 // hasn't changed, don't do anything 1054 final FocusRequester fr = mFocusStack.peek(); 1055 if (fr.getGainRequest() == focusChangeHint && fr.getGrantFlags() == flags) { 1056 // unlink death handler so it can be gc'ed. 1057 // linkToDeath() creates a JNI global reference preventing collection. 1058 cb.unlinkToDeath(afdh, 0); 1059 notifyExtPolicyFocusGrant_syncAf(fr.toAudioFocusInfo(), 1060 AudioManager.AUDIOFOCUS_REQUEST_GRANTED); 1061 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 1062 } 1063 // the reason for the audio focus request has changed: remove the current top of 1064 // stack and respond as if we had a new focus owner 1065 if (!focusGrantDelayed) { 1066 mFocusStack.pop(); 1067 // the entry that was "popped" is the same that was "peeked" above 1068 fr.release(); 1069 } 1070 } 1071 1072 // focus requester might already be somewhere below in the stack, remove it 1073 removeFocusStackEntry(clientId, false /* signal */, false /*notifyFocusFollowers*/); 1074 1075 final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb, 1076 clientId, afdh, callingPackageName, uid, this, sdk); 1077 1078 if (mMultiAudioFocusEnabled 1079 && (focusChangeHint == AudioManager.AUDIOFOCUS_GAIN)) { 1080 if (enteringRingOrCall) { 1081 if (!mMultiAudioFocusList.isEmpty()) { 1082 for (FocusRequester multifr : mMultiAudioFocusList) { 1083 multifr.handleFocusLossFromGain(focusChangeHint, nfr, forceDuck); 1084 } 1085 } 1086 } else { 1087 boolean needAdd = true; 1088 if (!mMultiAudioFocusList.isEmpty()) { 1089 for (FocusRequester multifr : mMultiAudioFocusList) { 1090 if (multifr.getClientUid() == Binder.getCallingUid()) { 1091 needAdd = false; 1092 break; 1093 } 1094 } 1095 } 1096 if (needAdd) { 1097 mMultiAudioFocusList.add(nfr); 1098 } 1099 nfr.handleFocusGainFromRequest(AudioManager.AUDIOFOCUS_REQUEST_GRANTED); 1100 notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(), 1101 AudioManager.AUDIOFOCUS_REQUEST_GRANTED); 1102 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 1103 } 1104 } 1105 1106 if (focusGrantDelayed) { 1107 // focusGrantDelayed being true implies we can't reassign focus right now 1108 // which implies the focus stack is not empty. 1109 final int requestResult = pushBelowLockedFocusOwnersAndPropagate(nfr); 1110 if (requestResult != AudioManager.AUDIOFOCUS_REQUEST_FAILED) { 1111 notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(), requestResult); 1112 } 1113 return requestResult; 1114 } else { 1115 // propagate the focus change through the stack 1116 propagateFocusLossFromGain_syncAf(focusChangeHint, nfr, forceDuck); 1117 1118 // push focus requester at the top of the audio focus stack 1119 mFocusStack.push(nfr); 1120 nfr.handleFocusGainFromRequest(AudioManager.AUDIOFOCUS_REQUEST_GRANTED); 1121 } 1122 notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(), 1123 AudioManager.AUDIOFOCUS_REQUEST_GRANTED); 1124 1125 if (ENFORCE_MUTING_FOR_RING_OR_CALL & enteringRingOrCall) { 1126 runAudioCheckerForRingOrCallAsync(true/*enteringRingOrCall*/); 1127 } 1128 }//synchronized(mAudioFocusLock) 1129 1130 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 1131 } 1132 1133 /** 1134 * @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener, AudioAttributes) 1135 * */ abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa, String callingPackageName)1136 protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa, 1137 String callingPackageName) { 1138 new MediaMetrics.Item(mMetricsId) 1139 .setUid(Binder.getCallingUid()) 1140 .set(MediaMetrics.Property.CALLING_PACKAGE, callingPackageName) 1141 .set(MediaMetrics.Property.CLIENT_NAME, clientId) 1142 .set(MediaMetrics.Property.EVENT, "abandonAudioFocus") 1143 .record(); 1144 1145 // AudioAttributes are currently ignored, to be used for zones / a11y 1146 mEventLogger.enqueue((new EventLogger.StringEvent( 1147 "abandonAudioFocus() from uid/pid " + Binder.getCallingUid() 1148 + "/" + Binder.getCallingPid() 1149 + " clientId=" + clientId)) 1150 .printLog(TAG)); 1151 try { 1152 // this will take care of notifying the new focus owner if needed 1153 synchronized(mAudioFocusLock) { 1154 // external focus policy? 1155 if (mFocusPolicy != null) { 1156 final AudioFocusInfo afi = new AudioFocusInfo(aa, Binder.getCallingUid(), 1157 clientId, callingPackageName, 0 /*gainRequest*/, 0 /*lossReceived*/, 1158 0 /*flags*/, 0 /* sdk n/a here*/); 1159 if (notifyExtFocusPolicyFocusAbandon_syncAf(afi)) { 1160 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 1161 } 1162 } 1163 1164 boolean exitingRingOrCall = mRingOrCallActive 1165 & (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0); 1166 if (exitingRingOrCall) { mRingOrCallActive = false; } 1167 1168 removeFocusStackEntry(clientId, true /*signal*/, true /*notifyFocusFollowers*/); 1169 1170 if (ENFORCE_MUTING_FOR_RING_OR_CALL & exitingRingOrCall) { 1171 runAudioCheckerForRingOrCallAsync(false/*enteringRingOrCall*/); 1172 } 1173 } 1174 } catch (java.util.ConcurrentModificationException cme) { 1175 // Catching this exception here is temporary. It is here just to prevent 1176 // a crash seen when the "Silent" notification is played. This is believed to be fixed 1177 // but this try catch block is left just to be safe. 1178 Log.e(TAG, "FATAL EXCEPTION AudioFocus abandonAudioFocus() caused " + cme); 1179 cme.printStackTrace(); 1180 } 1181 1182 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 1183 } 1184 1185 unregisterAudioFocusClient(String clientId)1186 protected void unregisterAudioFocusClient(String clientId) { 1187 synchronized(mAudioFocusLock) { 1188 removeFocusStackEntry(clientId, false, true /*notifyFocusFollowers*/); 1189 } 1190 } 1191 runAudioCheckerForRingOrCallAsync(final boolean enteringRingOrCall)1192 private void runAudioCheckerForRingOrCallAsync(final boolean enteringRingOrCall) { 1193 new Thread() { 1194 public void run() { 1195 if (enteringRingOrCall) { 1196 try { 1197 Thread.sleep(RING_CALL_MUTING_ENFORCEMENT_DELAY_MS); 1198 } catch (InterruptedException e) { 1199 e.printStackTrace(); 1200 } 1201 } 1202 synchronized (mAudioFocusLock) { 1203 // since the new thread starting running the state could have changed, so 1204 // we need to check again mRingOrCallActive, not enteringRingOrCall 1205 if (mRingOrCallActive) { 1206 mFocusEnforcer.mutePlayersForCall(USAGES_TO_MUTE_IN_RING_OR_CALL); 1207 } else { 1208 mFocusEnforcer.unmutePlayersForCall(); 1209 } 1210 } 1211 } 1212 }.start(); 1213 } 1214 updateMultiAudioFocus(boolean enabled)1215 public void updateMultiAudioFocus(boolean enabled) { 1216 Log.d(TAG, "updateMultiAudioFocus( " + enabled + " )"); 1217 mMultiAudioFocusEnabled = enabled; 1218 final ContentResolver cr = mContext.getContentResolver(); 1219 Settings.System.putIntForUser(cr, 1220 Settings.System.MULTI_AUDIO_FOCUS_ENABLED, enabled ? 1 : 0, cr.getUserId()); 1221 if (!mFocusStack.isEmpty()) { 1222 final FocusRequester fr = mFocusStack.peek(); 1223 fr.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null, false); 1224 } 1225 if (!enabled) { 1226 if (!mMultiAudioFocusList.isEmpty()) { 1227 for (FocusRequester multifr : mMultiAudioFocusList) { 1228 multifr.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null, false); 1229 } 1230 mMultiAudioFocusList.clear(); 1231 } 1232 } 1233 } 1234 getMultiAudioFocusEnabled()1235 public boolean getMultiAudioFocusEnabled() { 1236 return mMultiAudioFocusEnabled; 1237 } 1238 getFadeOutDurationOnFocusLossMillis(AudioAttributes aa)1239 /*package*/ long getFadeOutDurationOnFocusLossMillis(AudioAttributes aa) { 1240 if (!ENFORCE_FADEOUT_FOR_FOCUS_LOSS) { 1241 return 0; 1242 } 1243 return FadeOutManager.getFadeOutDurationOnFocusLossMillis(aa); 1244 } 1245 dumpMultiAudioFocus(PrintWriter pw)1246 private void dumpMultiAudioFocus(PrintWriter pw) { 1247 pw.println("Multi Audio Focus enabled :" + mMultiAudioFocusEnabled); 1248 if (!mMultiAudioFocusList.isEmpty()) { 1249 pw.println("Multi Audio Focus List:"); 1250 pw.println("------------------------------"); 1251 for (FocusRequester multifr : mMultiAudioFocusList) { 1252 multifr.dump(pw); 1253 } 1254 pw.println("------------------------------"); 1255 } 1256 } 1257 1258 //================================================================= 1259 // Async focus events postDelayedLossAfterFade(FocusRequester focusLoser, long delayMs)1260 void postDelayedLossAfterFade(FocusRequester focusLoser, long delayMs) { 1261 if (DEBUG) { 1262 Log.v(TAG, "postDelayedLossAfterFade loser=" + focusLoser.getPackageName()); 1263 } 1264 mFocusHandler.sendMessageDelayed( 1265 mFocusHandler.obtainMessage(MSG_L_FOCUS_LOSS_AFTER_FADE, focusLoser), 1266 FadeOutManager.FADE_OUT_DURATION_MS); 1267 } 1268 postForgetUidLater(int uid)1269 private void postForgetUidLater(int uid) { 1270 mFocusHandler.sendMessageDelayed( 1271 mFocusHandler.obtainMessage(MSL_L_FORGET_UID, new ForgetFadeUidInfo(uid)), 1272 FadeOutManager.DELAY_FADE_IN_OFFENDERS_MS); 1273 } 1274 1275 //================================================================= 1276 // Message handling 1277 private Handler mFocusHandler; 1278 private HandlerThread mFocusThread; 1279 1280 /** 1281 * dispatch a focus loss after an app has been faded out. Focus loser is to be released 1282 * after dispatch as it has already left the stack 1283 * args: 1284 * msg.obj: the audio focus loser 1285 * type:FocusRequester 1286 */ 1287 private static final int MSG_L_FOCUS_LOSS_AFTER_FADE = 1; 1288 1289 private static final int MSL_L_FORGET_UID = 2; 1290 initFocusThreading()1291 private void initFocusThreading() { 1292 mFocusThread = new HandlerThread(TAG); 1293 mFocusThread.start(); 1294 mFocusHandler = new Handler(mFocusThread.getLooper()) { 1295 @Override 1296 public void handleMessage(Message msg) { 1297 switch (msg.what) { 1298 case MSG_L_FOCUS_LOSS_AFTER_FADE: 1299 if (DEBUG) { 1300 Log.d(TAG, "MSG_L_FOCUS_LOSS_AFTER_FADE loser=" 1301 + ((FocusRequester) msg.obj).getPackageName()); 1302 } 1303 synchronized (mAudioFocusLock) { 1304 final FocusRequester loser = (FocusRequester) msg.obj; 1305 if (loser.isInFocusLossLimbo()) { 1306 loser.dispatchFocusChange(AudioManager.AUDIOFOCUS_LOSS); 1307 loser.release(); 1308 postForgetUidLater(loser.getClientUid()); 1309 } 1310 } 1311 break; 1312 1313 case MSL_L_FORGET_UID: 1314 final int uid = ((ForgetFadeUidInfo) msg.obj).mUid; 1315 if (DEBUG) { 1316 Log.d(TAG, "MSL_L_FORGET_UID uid=" + uid); 1317 } 1318 mFocusEnforcer.forgetUid(uid); 1319 break; 1320 default: 1321 break; 1322 } 1323 } 1324 }; 1325 } 1326 1327 /** 1328 * Class to associate a UID with a scheduled event to "forget" a UID for the fade out behavior. 1329 * Having a class with an equals() override allows using Handler.removeEqualsMessage() to 1330 * unschedule events when needed. Here we need to unschedule the "unfading out" == "forget uid" 1331 * whenever a new, more recent, focus related event happens before this one is handled. 1332 */ 1333 private static final class ForgetFadeUidInfo { 1334 private final int mUid; 1335 ForgetFadeUidInfo(int uid)1336 ForgetFadeUidInfo(int uid) { 1337 mUid = uid; 1338 } 1339 1340 @Override equals(Object o)1341 public boolean equals(Object o) { 1342 if (this == o) { 1343 return true; 1344 } 1345 if (o == null || getClass() != o.getClass()) { 1346 return false; 1347 } 1348 final ForgetFadeUidInfo f = (ForgetFadeUidInfo) o; 1349 if (f.mUid != mUid) { 1350 return false; 1351 } 1352 return true; 1353 } 1354 1355 @Override hashCode()1356 public int hashCode() { 1357 return mUid; 1358 } 1359 } 1360 } 1361