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