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.media.AudioAttributes; 22 import android.media.AudioFocusInfo; 23 import android.media.AudioManager; 24 import android.media.IAudioFocusDispatcher; 25 import android.os.IBinder; 26 import android.util.Log; 27 28 import com.android.internal.annotations.GuardedBy; 29 import com.android.server.audio.MediaFocusControl.AudioFocusDeathHandler; 30 31 import java.io.PrintWriter; 32 33 /** 34 * @hide 35 * Class to handle all the information about a user of audio focus. The lifecycle of each 36 * instance is managed by android.media.MediaFocusControl, from its addition to the audio focus 37 * stack, or the map of focus owners for an external focus policy, to its release. 38 */ 39 public class FocusRequester { 40 41 // on purpose not using this classe's name, as it will only be used from MediaFocusControl 42 private static final String TAG = "MediaFocusControl"; 43 private static final boolean DEBUG = false; 44 45 private AudioFocusDeathHandler mDeathHandler; // may be null 46 private IAudioFocusDispatcher mFocusDispatcher; // may be null 47 private final IBinder mSourceRef; // may be null 48 private final String mClientId; 49 private final String mPackageName; 50 private final int mCallingUid; 51 private final MediaFocusControl mFocusController; // never null 52 private final int mSdkTarget; 53 54 /** 55 * the audio focus gain request that caused the addition of this object in the focus stack. 56 */ 57 private final int mFocusGainRequest; 58 /** 59 * the flags associated with the gain request that qualify the type of grant (e.g. accepting 60 * delay vs grant must be immediate) 61 */ 62 private final int mGrantFlags; 63 /** 64 * the audio focus loss received my mFocusDispatcher, is AudioManager.AUDIOFOCUS_NONE if 65 * it never lost focus. 66 */ 67 private int mFocusLossReceived; 68 /** 69 * whether this focus owner listener was notified when it lost focus 70 */ 71 private boolean mFocusLossWasNotified; 72 /** 73 * the audio attributes associated with the focus request 74 */ 75 private final AudioAttributes mAttributes; 76 77 /** 78 * Class constructor 79 * @param aa 80 * @param focusRequest 81 * @param grantFlags 82 * @param afl 83 * @param source 84 * @param id 85 * @param hdlr 86 * @param pn 87 * @param uid 88 * @param ctlr cannot be null 89 */ FocusRequester(AudioAttributes aa, int focusRequest, int grantFlags, IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr, String pn, int uid, @NonNull MediaFocusControl ctlr, int sdk)90 FocusRequester(AudioAttributes aa, int focusRequest, int grantFlags, 91 IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr, 92 String pn, int uid, @NonNull MediaFocusControl ctlr, int sdk) { 93 mAttributes = aa; 94 mFocusDispatcher = afl; 95 mSourceRef = source; 96 mClientId = id; 97 mDeathHandler = hdlr; 98 mPackageName = pn; 99 mCallingUid = uid; 100 mFocusGainRequest = focusRequest; 101 mGrantFlags = grantFlags; 102 mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE; 103 mFocusLossWasNotified = true; 104 mFocusController = ctlr; 105 mSdkTarget = sdk; 106 } 107 FocusRequester(AudioFocusInfo afi, IAudioFocusDispatcher afl, IBinder source, AudioFocusDeathHandler hdlr, @NonNull MediaFocusControl ctlr)108 FocusRequester(AudioFocusInfo afi, IAudioFocusDispatcher afl, 109 IBinder source, AudioFocusDeathHandler hdlr, @NonNull MediaFocusControl ctlr) { 110 mAttributes = afi.getAttributes(); 111 mClientId = afi.getClientId(); 112 mPackageName = afi.getPackageName(); 113 mCallingUid = afi.getClientUid(); 114 mFocusGainRequest = afi.getGainRequest(); 115 mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE; 116 mFocusLossWasNotified = true; 117 mGrantFlags = afi.getFlags(); 118 mSdkTarget = afi.getSdkTarget(); 119 120 mFocusDispatcher = afl; 121 mSourceRef = source; 122 mDeathHandler = hdlr; 123 mFocusController = ctlr; 124 } 125 hasSameClient(String otherClient)126 boolean hasSameClient(String otherClient) { 127 try { 128 return mClientId.compareTo(otherClient) == 0; 129 } catch (NullPointerException e) { 130 return false; 131 } 132 } 133 isLockedFocusOwner()134 boolean isLockedFocusOwner() { 135 return ((mGrantFlags & AudioManager.AUDIOFOCUS_FLAG_LOCK) != 0); 136 } 137 hasSameBinder(IBinder ib)138 boolean hasSameBinder(IBinder ib) { 139 return (mSourceRef != null) && mSourceRef.equals(ib); 140 } 141 hasSameDispatcher(IAudioFocusDispatcher fd)142 boolean hasSameDispatcher(IAudioFocusDispatcher fd) { 143 return (mFocusDispatcher != null) && mFocusDispatcher.equals(fd); 144 } 145 hasSamePackage(String pack)146 boolean hasSamePackage(String pack) { 147 try { 148 return mPackageName.compareTo(pack) == 0; 149 } catch (NullPointerException e) { 150 return false; 151 } 152 } 153 hasSameUid(int uid)154 boolean hasSameUid(int uid) { 155 return mCallingUid == uid; 156 } 157 getClientUid()158 int getClientUid() { 159 return mCallingUid; 160 } 161 getClientId()162 String getClientId() { 163 return mClientId; 164 } 165 getGainRequest()166 int getGainRequest() { 167 return mFocusGainRequest; 168 } 169 getGrantFlags()170 int getGrantFlags() { 171 return mGrantFlags; 172 } 173 getAudioAttributes()174 AudioAttributes getAudioAttributes() { 175 return mAttributes; 176 } 177 getSdkTarget()178 int getSdkTarget() { 179 return mSdkTarget; 180 } 181 focusChangeToString(int focus)182 private static String focusChangeToString(int focus) { 183 switch(focus) { 184 case AudioManager.AUDIOFOCUS_NONE: 185 return "none"; 186 case AudioManager.AUDIOFOCUS_GAIN: 187 return "GAIN"; 188 case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT: 189 return "GAIN_TRANSIENT"; 190 case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK: 191 return "GAIN_TRANSIENT_MAY_DUCK"; 192 case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE: 193 return "GAIN_TRANSIENT_EXCLUSIVE"; 194 case AudioManager.AUDIOFOCUS_LOSS: 195 return "LOSS"; 196 case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: 197 return "LOSS_TRANSIENT"; 198 case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: 199 return "LOSS_TRANSIENT_CAN_DUCK"; 200 default: 201 return "[invalid focus change" + focus + "]"; 202 } 203 } 204 focusGainToString()205 private String focusGainToString() { 206 return focusChangeToString(mFocusGainRequest); 207 } 208 focusLossToString()209 private String focusLossToString() { 210 return focusChangeToString(mFocusLossReceived); 211 } 212 flagsToString(int flags)213 private static String flagsToString(int flags) { 214 String msg = new String(); 215 if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) != 0) { 216 msg += "DELAY_OK"; 217 } 218 if ((flags & AudioManager.AUDIOFOCUS_FLAG_LOCK) != 0) { 219 if (!msg.isEmpty()) { msg += "|"; } 220 msg += "LOCK"; 221 } 222 if ((flags & AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) != 0) { 223 if (!msg.isEmpty()) { msg += "|"; } 224 msg += "PAUSES_ON_DUCKABLE_LOSS"; 225 } 226 return msg; 227 } 228 dump(PrintWriter pw)229 void dump(PrintWriter pw) { 230 pw.println(" source:" + mSourceRef 231 + " -- pack: " + mPackageName 232 + " -- client: " + mClientId 233 + " -- gain: " + focusGainToString() 234 + " -- flags: " + flagsToString(mGrantFlags) 235 + " -- loss: " + focusLossToString() 236 + " -- notified: " + mFocusLossWasNotified 237 + " -- uid: " + mCallingUid 238 + " -- attr: " + mAttributes 239 + " -- sdk:" + mSdkTarget); 240 } 241 242 release()243 void release() { 244 final IBinder srcRef = mSourceRef; 245 final AudioFocusDeathHandler deathHdlr = mDeathHandler; 246 try { 247 if (srcRef != null && deathHdlr != null) { 248 srcRef.unlinkToDeath(deathHdlr, 0); 249 } 250 } catch (java.util.NoSuchElementException e) { } 251 mDeathHandler = null; 252 mFocusDispatcher = null; 253 } 254 255 @Override finalize()256 protected void finalize() throws Throwable { 257 release(); 258 super.finalize(); 259 } 260 261 /** 262 * For a given audio focus gain request, return the audio focus loss type that will result 263 * from it, taking into account any previous focus loss. 264 * @param gainRequest 265 * @return the audio focus loss type that matches the gain request 266 */ focusLossForGainRequest(int gainRequest)267 private int focusLossForGainRequest(int gainRequest) { 268 switch(gainRequest) { 269 case AudioManager.AUDIOFOCUS_GAIN: 270 switch(mFocusLossReceived) { 271 case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: 272 case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: 273 case AudioManager.AUDIOFOCUS_LOSS: 274 case AudioManager.AUDIOFOCUS_NONE: 275 return AudioManager.AUDIOFOCUS_LOSS; 276 } 277 case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE: 278 case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT: 279 switch(mFocusLossReceived) { 280 case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: 281 case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: 282 case AudioManager.AUDIOFOCUS_NONE: 283 return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT; 284 case AudioManager.AUDIOFOCUS_LOSS: 285 return AudioManager.AUDIOFOCUS_LOSS; 286 } 287 case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK: 288 switch(mFocusLossReceived) { 289 case AudioManager.AUDIOFOCUS_NONE: 290 case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: 291 return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK; 292 case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: 293 return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT; 294 case AudioManager.AUDIOFOCUS_LOSS: 295 return AudioManager.AUDIOFOCUS_LOSS; 296 } 297 default: 298 Log.e(TAG, "focusLossForGainRequest() for invalid focus request "+ gainRequest); 299 return AudioManager.AUDIOFOCUS_NONE; 300 } 301 } 302 303 /** 304 * Handle the loss of focus resulting from a given focus gain. 305 * @param focusGain the focus gain from which the loss of focus is resulting 306 * @param frWinner the new focus owner 307 * @return true if the focus loss is definitive, false otherwise. 308 */ 309 @GuardedBy("MediaFocusControl.mAudioFocusLock") handleFocusLossFromGain(int focusGain, final FocusRequester frWinner, boolean forceDuck)310 boolean handleFocusLossFromGain(int focusGain, final FocusRequester frWinner, boolean forceDuck) 311 { 312 final int focusLoss = focusLossForGainRequest(focusGain); 313 handleFocusLoss(focusLoss, frWinner, forceDuck); 314 return (focusLoss == AudioManager.AUDIOFOCUS_LOSS); 315 } 316 317 @GuardedBy("MediaFocusControl.mAudioFocusLock") handleFocusGain(int focusGain)318 void handleFocusGain(int focusGain) { 319 try { 320 mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE; 321 mFocusController.notifyExtPolicyFocusGrant_syncAf(toAudioFocusInfo(), 322 AudioManager.AUDIOFOCUS_REQUEST_GRANTED); 323 final IAudioFocusDispatcher fd = mFocusDispatcher; 324 if (fd != null) { 325 if (DEBUG) { 326 Log.v(TAG, "dispatching " + focusChangeToString(focusGain) + " to " 327 + mClientId); 328 } 329 if (mFocusLossWasNotified) { 330 fd.dispatchAudioFocusChange(focusGain, mClientId); 331 } 332 } 333 mFocusController.unduckPlayers(this); 334 } catch (android.os.RemoteException e) { 335 Log.e(TAG, "Failure to signal gain of audio focus due to: ", e); 336 } 337 } 338 339 @GuardedBy("MediaFocusControl.mAudioFocusLock") handleFocusGainFromRequest(int focusRequestResult)340 void handleFocusGainFromRequest(int focusRequestResult) { 341 if (focusRequestResult == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { 342 mFocusController.unduckPlayers(this); 343 } 344 } 345 346 @GuardedBy("MediaFocusControl.mAudioFocusLock") handleFocusLoss(int focusLoss, @Nullable final FocusRequester frWinner, boolean forceDuck)347 void handleFocusLoss(int focusLoss, @Nullable final FocusRequester frWinner, boolean forceDuck) 348 { 349 try { 350 if (focusLoss != mFocusLossReceived) { 351 mFocusLossReceived = focusLoss; 352 mFocusLossWasNotified = false; 353 // before dispatching a focus loss, check if the following conditions are met: 354 // 1/ the framework is not supposed to notify the focus loser on a DUCK loss 355 // (i.e. it has a focus controller that implements a ducking policy) 356 // 2/ it is a DUCK loss 357 // 3/ the focus loser isn't flagged as pausing in a DUCK loss 358 // if they are, do not notify the focus loser 359 if (!mFocusController.mustNotifyFocusOwnerOnDuck() 360 && mFocusLossReceived == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK 361 && (mGrantFlags 362 & AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) == 0) { 363 if (DEBUG) { 364 Log.v(TAG, "NOT dispatching " + focusChangeToString(mFocusLossReceived) 365 + " to " + mClientId + ", to be handled externally"); 366 } 367 mFocusController.notifyExtPolicyFocusLoss_syncAf( 368 toAudioFocusInfo(), false /* wasDispatched */); 369 return; 370 } 371 372 // check enforcement by the framework 373 boolean handled = false; 374 if (focusLoss == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK 375 && MediaFocusControl.ENFORCE_DUCKING 376 && frWinner != null) { 377 // candidate for enforcement by the framework 378 if (frWinner.mCallingUid != this.mCallingUid) { 379 if (!forceDuck && ((mGrantFlags 380 & AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) != 0)) { 381 // the focus loser declared it would pause instead of duck, let it 382 // handle it (the framework doesn't pause for apps) 383 handled = false; 384 Log.v(TAG, "not ducking uid " + this.mCallingUid + " - flags"); 385 } else if (!forceDuck && (MediaFocusControl.ENFORCE_DUCKING_FOR_NEW && 386 this.getSdkTarget() <= MediaFocusControl.DUCKING_IN_APP_SDK_LEVEL)) 387 { 388 // legacy behavior, apps used to be notified when they should be ducking 389 handled = false; 390 Log.v(TAG, "not ducking uid " + this.mCallingUid + " - old SDK"); 391 } else { 392 handled = mFocusController.duckPlayers(frWinner, this, forceDuck); 393 } 394 } // else: the focus change is within the same app, so let the dispatching 395 // happen as if the framework was not involved. 396 } 397 398 if (handled) { 399 if (DEBUG) { 400 Log.v(TAG, "NOT dispatching " + focusChangeToString(mFocusLossReceived) 401 + " to " + mClientId + ", ducking implemented by framework"); 402 } 403 mFocusController.notifyExtPolicyFocusLoss_syncAf( 404 toAudioFocusInfo(), false /* wasDispatched */); 405 return; // with mFocusLossWasNotified = false 406 } 407 408 final IAudioFocusDispatcher fd = mFocusDispatcher; 409 if (fd != null) { 410 if (DEBUG) { 411 Log.v(TAG, "dispatching " + focusChangeToString(mFocusLossReceived) + " to " 412 + mClientId); 413 } 414 mFocusController.notifyExtPolicyFocusLoss_syncAf( 415 toAudioFocusInfo(), true /* wasDispatched */); 416 mFocusLossWasNotified = true; 417 fd.dispatchAudioFocusChange(mFocusLossReceived, mClientId); 418 } 419 } 420 } catch (android.os.RemoteException e) { 421 Log.e(TAG, "Failure to signal loss of audio focus due to:", e); 422 } 423 } 424 dispatchFocusChange(int focusChange)425 int dispatchFocusChange(int focusChange) { 426 if (mFocusDispatcher == null) { 427 if (MediaFocusControl.DEBUG) { Log.e(TAG, "dispatchFocusChange: no focus dispatcher"); } 428 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 429 } 430 if (focusChange == AudioManager.AUDIOFOCUS_NONE) { 431 if (MediaFocusControl.DEBUG) { Log.v(TAG, "dispatchFocusChange: AUDIOFOCUS_NONE"); } 432 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 433 } else if ((focusChange == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 434 || focusChange == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE 435 || focusChange == AudioManager.AUDIOFOCUS_GAIN_TRANSIENT 436 || focusChange == AudioManager.AUDIOFOCUS_GAIN) 437 && (mFocusGainRequest != focusChange)){ 438 Log.w(TAG, "focus gain was requested with " + mFocusGainRequest 439 + ", dispatching " + focusChange); 440 } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK 441 || focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT 442 || focusChange == AudioManager.AUDIOFOCUS_LOSS) { 443 mFocusLossReceived = focusChange; 444 } 445 try { 446 mFocusDispatcher.dispatchAudioFocusChange(focusChange, mClientId); 447 } catch (android.os.RemoteException e) { 448 Log.e(TAG, "dispatchFocusChange: error talking to focus listener " + mClientId, e); 449 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 450 } 451 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 452 } 453 dispatchFocusResultFromExtPolicy(int requestResult)454 void dispatchFocusResultFromExtPolicy(int requestResult) { 455 if (mFocusDispatcher == null) { 456 if (MediaFocusControl.DEBUG) { 457 Log.e(TAG, "dispatchFocusResultFromExtPolicy: no focus dispatcher"); 458 } 459 } 460 if (DEBUG) { 461 Log.v(TAG, "dispatching result" + requestResult + " to " + mClientId); 462 } 463 try { 464 mFocusDispatcher.dispatchFocusResultFromExtPolicy(requestResult, mClientId); 465 } catch (android.os.RemoteException e) { 466 Log.e(TAG, "dispatchFocusResultFromExtPolicy: error talking to focus listener" 467 + mClientId, e); 468 } 469 } 470 toAudioFocusInfo()471 AudioFocusInfo toAudioFocusInfo() { 472 return new AudioFocusInfo(mAttributes, mCallingUid, mClientId, mPackageName, 473 mFocusGainRequest, mFocusLossReceived, mGrantFlags, mSdkTarget); 474 } 475 } 476