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.app.AppOpsManager; 20 import android.content.Context; 21 import android.media.AudioAttributes; 22 import android.media.AudioFocusInfo; 23 import android.media.AudioManager; 24 import android.media.AudioSystem; 25 import android.media.IAudioFocusDispatcher; 26 import android.media.audiopolicy.IAudioPolicyCallback; 27 import android.os.Binder; 28 import android.os.IBinder; 29 import android.os.RemoteException; 30 import android.util.Log; 31 32 import java.io.PrintWriter; 33 import java.util.ArrayList; 34 import java.util.Date; 35 import java.util.Iterator; 36 import java.util.Stack; 37 import java.text.DateFormat; 38 39 /** 40 * @hide 41 * 42 */ 43 public class MediaFocusControl { 44 45 private static final String TAG = "MediaFocusControl"; 46 47 private final Context mContext; 48 private final AppOpsManager mAppOps; 49 50 protected MediaFocusControl(Context cntxt) { 51 mContext = cntxt; 52 mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE); 53 } 54 55 protected void dump(PrintWriter pw) { 56 pw.println("\nMediaFocusControl dump time: " 57 + DateFormat.getTimeInstance().format(new Date())); 58 dumpFocusStack(pw); 59 } 60 61 62 //========================================================================================== 63 // AudioFocus 64 //========================================================================================== 65 66 private final static Object mAudioFocusLock = new Object(); 67 68 /** 69 * Discard the current audio focus owner. 70 * Notify top of audio focus stack that it lost focus (regardless of possibility to reassign 71 * focus), remove it from the stack, and clear the remote control display. 72 */ 73 protected void discardAudioFocusOwner() { 74 synchronized(mAudioFocusLock) { 75 if (!mFocusStack.empty()) { 76 // notify the current focus owner it lost focus after removing it from stack 77 final FocusRequester exFocusOwner = mFocusStack.pop(); 78 exFocusOwner.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS); 79 exFocusOwner.release(); 80 } 81 } 82 } 83 84 /** 85 * Called synchronized on mAudioFocusLock 86 */ 87 private void notifyTopOfAudioFocusStack() { 88 // notify the top of the stack it gained focus 89 if (!mFocusStack.empty()) { 90 if (canReassignAudioFocus()) { 91 mFocusStack.peek().handleFocusGain(AudioManager.AUDIOFOCUS_GAIN); 92 } 93 } 94 } 95 96 /** 97 * Focus is requested, propagate the associated loss throughout the stack. 98 * @param focusGain the new focus gain that will later be added at the top of the stack 99 */ 100 private void propagateFocusLossFromGain_syncAf(int focusGain) { 101 // going through the audio focus stack to signal new focus, traversing order doesn't 102 // matter as all entries respond to the same external focus gain 103 Iterator<FocusRequester> stackIterator = mFocusStack.iterator(); 104 while(stackIterator.hasNext()) { 105 stackIterator.next().handleExternalFocusGain(focusGain); 106 } 107 } 108 109 private final Stack<FocusRequester> mFocusStack = new Stack<FocusRequester>(); 110 111 /** 112 * Helper function: 113 * Display in the log the current entries in the audio focus stack 114 */ 115 private void dumpFocusStack(PrintWriter pw) { 116 pw.println("\nAudio Focus stack entries (last is top of stack):"); 117 synchronized(mAudioFocusLock) { 118 Iterator<FocusRequester> stackIterator = mFocusStack.iterator(); 119 while(stackIterator.hasNext()) { 120 stackIterator.next().dump(pw); 121 } 122 } 123 pw.println("\n Notify on duck: " + mNotifyFocusOwnerOnDuck +"\n"); 124 } 125 126 /** 127 * Helper function: 128 * Called synchronized on mAudioFocusLock 129 * Remove a focus listener from the focus stack. 130 * @param clientToRemove the focus listener 131 * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding 132 * focus, notify the next item in the stack it gained focus. 133 */ 134 private void removeFocusStackEntry(String clientToRemove, boolean signal, 135 boolean notifyFocusFollowers) { 136 // is the current top of the focus stack abandoning focus? (because of request, not death) 137 if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientToRemove)) 138 { 139 //Log.i(TAG, " removeFocusStackEntry() removing top of stack"); 140 FocusRequester fr = mFocusStack.pop(); 141 fr.release(); 142 if (notifyFocusFollowers) { 143 final AudioFocusInfo afi = fr.toAudioFocusInfo(); 144 afi.clearLossReceived(); 145 notifyExtPolicyFocusLoss_syncAf(afi, false); 146 } 147 if (signal) { 148 // notify the new top of the stack it gained focus 149 notifyTopOfAudioFocusStack(); 150 } 151 } else { 152 // focus is abandoned by a client that's not at the top of the stack, 153 // no need to update focus. 154 // (using an iterator on the stack so we can safely remove an entry after having 155 // evaluated it, traversal order doesn't matter here) 156 Iterator<FocusRequester> stackIterator = mFocusStack.iterator(); 157 while(stackIterator.hasNext()) { 158 FocusRequester fr = stackIterator.next(); 159 if(fr.hasSameClient(clientToRemove)) { 160 Log.i(TAG, "AudioFocus removeFocusStackEntry(): removing entry for " 161 + clientToRemove); 162 stackIterator.remove(); 163 fr.release(); 164 } 165 } 166 } 167 } 168 169 /** 170 * Helper function: 171 * Called synchronized on mAudioFocusLock 172 * Remove focus listeners from the focus stack for a particular client when it has died. 173 */ 174 private void removeFocusStackEntryForClient(IBinder cb) { 175 // is the owner of the audio focus part of the client to remove? 176 boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() && 177 mFocusStack.peek().hasSameBinder(cb); 178 // (using an iterator on the stack so we can safely remove an entry after having 179 // evaluated it, traversal order doesn't matter here) 180 Iterator<FocusRequester> stackIterator = mFocusStack.iterator(); 181 while(stackIterator.hasNext()) { 182 FocusRequester fr = stackIterator.next(); 183 if(fr.hasSameBinder(cb)) { 184 Log.i(TAG, "AudioFocus removeFocusStackEntry(): removing entry for " + cb); 185 stackIterator.remove(); 186 // the client just died, no need to unlink to its death 187 } 188 } 189 if (isTopOfStackForClientToRemove) { 190 // we removed an entry at the top of the stack: 191 // notify the new top of the stack it gained focus. 192 notifyTopOfAudioFocusStack(); 193 } 194 } 195 196 /** 197 * Helper function: 198 * Returns true if the system is in a state where the focus can be reevaluated, false otherwise. 199 * The implementation guarantees that a state where focus cannot be immediately reassigned 200 * implies that an "locked" focus owner is at the top of the focus stack. 201 * Modifications to the implementation that break this assumption will cause focus requests to 202 * misbehave when honoring the AudioManager.AUDIOFOCUS_FLAG_DELAY_OK flag. 203 */ 204 private boolean canReassignAudioFocus() { 205 // focus requests are rejected during a phone call or when the phone is ringing 206 // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus 207 if (!mFocusStack.isEmpty() && isLockedFocusOwner(mFocusStack.peek())) { 208 return false; 209 } 210 return true; 211 } 212 213 private boolean isLockedFocusOwner(FocusRequester fr) { 214 return (fr.hasSameClient(AudioSystem.IN_VOICE_COMM_FOCUS_ID) || fr.isLockedFocusOwner()); 215 } 216 217 /** 218 * Helper function 219 * Pre-conditions: focus stack is not empty, there is one or more locked focus owner 220 * at the top of the focus stack 221 * Push the focus requester onto the audio focus stack at the first position immediately 222 * following the locked focus owners. 223 * @return {@link AudioManager#AUDIOFOCUS_REQUEST_GRANTED} or 224 * {@link AudioManager#AUDIOFOCUS_REQUEST_DELAYED} 225 */ 226 private int pushBelowLockedFocusOwners(FocusRequester nfr) { 227 int lastLockedFocusOwnerIndex = mFocusStack.size(); 228 for (int index = mFocusStack.size()-1; index >= 0; index--) { 229 if (isLockedFocusOwner(mFocusStack.elementAt(index))) { 230 lastLockedFocusOwnerIndex = index; 231 } 232 } 233 if (lastLockedFocusOwnerIndex == mFocusStack.size()) { 234 // this should not happen, but handle it and log an error 235 Log.e(TAG, "No exclusive focus owner found in propagateFocusLossFromGain_syncAf()", 236 new Exception()); 237 // no exclusive owner, push at top of stack, focus is granted, propagate change 238 propagateFocusLossFromGain_syncAf(nfr.getGainRequest()); 239 mFocusStack.push(nfr); 240 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 241 } else { 242 mFocusStack.insertElementAt(nfr, lastLockedFocusOwnerIndex); 243 return AudioManager.AUDIOFOCUS_REQUEST_DELAYED; 244 } 245 } 246 247 /** 248 * Inner class to monitor audio focus client deaths, and remove them from the audio focus 249 * stack if necessary. 250 */ 251 protected class AudioFocusDeathHandler implements IBinder.DeathRecipient { 252 private IBinder mCb; // To be notified of client's death 253 254 AudioFocusDeathHandler(IBinder cb) { 255 mCb = cb; 256 } 257 258 public void binderDied() { 259 synchronized(mAudioFocusLock) { 260 Log.w(TAG, " AudioFocus audio focus client died"); 261 removeFocusStackEntryForClient(mCb); 262 } 263 } 264 265 public IBinder getBinder() { 266 return mCb; 267 } 268 } 269 270 /** 271 * Indicates whether to notify an audio focus owner when it loses focus 272 * with {@link AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK} if it will only duck. 273 * This variable being false indicates an AudioPolicy has been registered and has signaled 274 * it will handle audio ducking. 275 */ 276 private boolean mNotifyFocusOwnerOnDuck = true; 277 278 protected void setDuckingInExtPolicyAvailable(boolean available) { 279 mNotifyFocusOwnerOnDuck = !available; 280 } 281 282 boolean mustNotifyFocusOwnerOnDuck() { return mNotifyFocusOwnerOnDuck; } 283 284 private ArrayList<IAudioPolicyCallback> mFocusFollowers = new ArrayList<IAudioPolicyCallback>(); 285 286 void addFocusFollower(IAudioPolicyCallback ff) { 287 if (ff == null) { 288 return; 289 } 290 synchronized(mAudioFocusLock) { 291 boolean found = false; 292 for (IAudioPolicyCallback pcb : mFocusFollowers) { 293 if (pcb.asBinder().equals(ff.asBinder())) { 294 found = true; 295 break; 296 } 297 } 298 if (found) { 299 return; 300 } else { 301 mFocusFollowers.add(ff); 302 notifyExtPolicyCurrentFocusAsync(ff); 303 } 304 } 305 } 306 307 void removeFocusFollower(IAudioPolicyCallback ff) { 308 if (ff == null) { 309 return; 310 } 311 synchronized(mAudioFocusLock) { 312 for (IAudioPolicyCallback pcb : mFocusFollowers) { 313 if (pcb.asBinder().equals(ff.asBinder())) { 314 mFocusFollowers.remove(pcb); 315 break; 316 } 317 } 318 } 319 } 320 321 /** 322 * @param pcb non null 323 */ 324 void notifyExtPolicyCurrentFocusAsync(IAudioPolicyCallback pcb) { 325 final IAudioPolicyCallback pcb2 = pcb; 326 final Thread thread = new Thread() { 327 @Override 328 public void run() { 329 synchronized(mAudioFocusLock) { 330 if (mFocusStack.isEmpty()) { 331 return; 332 } 333 try { 334 pcb2.notifyAudioFocusGrant(mFocusStack.peek().toAudioFocusInfo(), 335 // top of focus stack always has focus 336 AudioManager.AUDIOFOCUS_REQUEST_GRANTED); 337 } catch (RemoteException e) { 338 Log.e(TAG, "Can't call notifyAudioFocusGrant() on IAudioPolicyCallback " 339 + pcb2.asBinder(), e); 340 } 341 } 342 } 343 }; 344 thread.start(); 345 } 346 347 /** 348 * Called synchronized on mAudioFocusLock 349 */ 350 void notifyExtPolicyFocusGrant_syncAf(AudioFocusInfo afi, int requestResult) { 351 for (IAudioPolicyCallback pcb : mFocusFollowers) { 352 try { 353 // oneway 354 pcb.notifyAudioFocusGrant(afi, requestResult); 355 } catch (RemoteException e) { 356 Log.e(TAG, "Can't call notifyAudioFocusGrant() on IAudioPolicyCallback " 357 + pcb.asBinder(), e); 358 } 359 } 360 } 361 362 /** 363 * Called synchronized on mAudioFocusLock 364 */ 365 void notifyExtPolicyFocusLoss_syncAf(AudioFocusInfo afi, boolean wasDispatched) { 366 for (IAudioPolicyCallback pcb : mFocusFollowers) { 367 try { 368 // oneway 369 pcb.notifyAudioFocusLoss(afi, wasDispatched); 370 } catch (RemoteException e) { 371 Log.e(TAG, "Can't call notifyAudioFocusLoss() on IAudioPolicyCallback " 372 + pcb.asBinder(), e); 373 } 374 } 375 } 376 377 protected int getCurrentAudioFocus() { 378 synchronized(mAudioFocusLock) { 379 if (mFocusStack.empty()) { 380 return AudioManager.AUDIOFOCUS_NONE; 381 } else { 382 return mFocusStack.peek().getGainRequest(); 383 } 384 } 385 } 386 387 /** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int, int) */ 388 protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb, 389 IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags) { 390 Log.i(TAG, " AudioFocus requestAudioFocus() from uid/pid " + Binder.getCallingUid() 391 + "/" + Binder.getCallingPid() 392 + " clientId=" + clientId 393 + " req=" + focusChangeHint 394 + " flags=0x" + Integer.toHexString(flags)); 395 // we need a valid binder callback for clients 396 if (!cb.pingBinder()) { 397 Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting."); 398 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 399 } 400 401 if (mAppOps.noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, Binder.getCallingUid(), 402 callingPackageName) != AppOpsManager.MODE_ALLOWED) { 403 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 404 } 405 406 synchronized(mAudioFocusLock) { 407 boolean focusGrantDelayed = false; 408 if (!canReassignAudioFocus()) { 409 if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) == 0) { 410 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 411 } else { 412 // request has AUDIOFOCUS_FLAG_DELAY_OK: focus can't be 413 // granted right now, so the requester will be inserted in the focus stack 414 // to receive focus later 415 focusGrantDelayed = true; 416 } 417 } 418 419 // handle the potential premature death of the new holder of the focus 420 // (premature death == death before abandoning focus) 421 // Register for client death notification 422 AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb); 423 try { 424 cb.linkToDeath(afdh, 0); 425 } catch (RemoteException e) { 426 // client has already died! 427 Log.w(TAG, "AudioFocus requestAudioFocus() could not link to "+cb+" binder death"); 428 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 429 } 430 431 if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientId)) { 432 // if focus is already owned by this client and the reason for acquiring the focus 433 // hasn't changed, don't do anything 434 final FocusRequester fr = mFocusStack.peek(); 435 if (fr.getGainRequest() == focusChangeHint && fr.getGrantFlags() == flags) { 436 // unlink death handler so it can be gc'ed. 437 // linkToDeath() creates a JNI global reference preventing collection. 438 cb.unlinkToDeath(afdh, 0); 439 notifyExtPolicyFocusGrant_syncAf(fr.toAudioFocusInfo(), 440 AudioManager.AUDIOFOCUS_REQUEST_GRANTED); 441 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 442 } 443 // the reason for the audio focus request has changed: remove the current top of 444 // stack and respond as if we had a new focus owner 445 if (!focusGrantDelayed) { 446 mFocusStack.pop(); 447 // the entry that was "popped" is the same that was "peeked" above 448 fr.release(); 449 } 450 } 451 452 // focus requester might already be somewhere below in the stack, remove it 453 removeFocusStackEntry(clientId, false /* signal */, false /*notifyFocusFollowers*/); 454 455 final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb, 456 clientId, afdh, callingPackageName, Binder.getCallingUid(), this); 457 if (focusGrantDelayed) { 458 // focusGrantDelayed being true implies we can't reassign focus right now 459 // which implies the focus stack is not empty. 460 final int requestResult = pushBelowLockedFocusOwners(nfr); 461 if (requestResult != AudioManager.AUDIOFOCUS_REQUEST_FAILED) { 462 notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(), requestResult); 463 } 464 return requestResult; 465 } else { 466 // propagate the focus change through the stack 467 if (!mFocusStack.empty()) { 468 propagateFocusLossFromGain_syncAf(focusChangeHint); 469 } 470 471 // push focus requester at the top of the audio focus stack 472 mFocusStack.push(nfr); 473 } 474 notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(), 475 AudioManager.AUDIOFOCUS_REQUEST_GRANTED); 476 477 }//synchronized(mAudioFocusLock) 478 479 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 480 } 481 482 /** 483 * @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener, AudioAttributes) 484 * */ 485 protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa) { 486 // AudioAttributes are currently ignored, to be used for zones 487 Log.i(TAG, " AudioFocus abandonAudioFocus() from uid/pid " + Binder.getCallingUid() 488 + "/" + Binder.getCallingPid() 489 + " clientId=" + clientId); 490 try { 491 // this will take care of notifying the new focus owner if needed 492 synchronized(mAudioFocusLock) { 493 removeFocusStackEntry(clientId, true /*signal*/, true /*notifyFocusFollowers*/); 494 } 495 } catch (java.util.ConcurrentModificationException cme) { 496 // Catching this exception here is temporary. It is here just to prevent 497 // a crash seen when the "Silent" notification is played. This is believed to be fixed 498 // but this try catch block is left just to be safe. 499 Log.e(TAG, "FATAL EXCEPTION AudioFocus abandonAudioFocus() caused " + cme); 500 cme.printStackTrace(); 501 } 502 503 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 504 } 505 506 507 protected void unregisterAudioFocusClient(String clientId) { 508 synchronized(mAudioFocusLock) { 509 removeFocusStackEntry(clientId, false, true /*notifyFocusFollowers*/); 510 } 511 } 512 513 } 514