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.media.AudioAttributes; 21 import android.media.AudioFocusInfo; 22 import android.media.AudioManager; 23 import android.media.IAudioFocusDispatcher; 24 import android.os.IBinder; 25 import android.util.Log; 26 27 import com.android.server.audio.MediaFocusControl.AudioFocusDeathHandler; 28 29 import java.io.PrintWriter; 30 31 /** 32 * @hide 33 * Class to handle all the information about a user of audio focus. The lifecycle of each 34 * instance is managed by android.media.MediaFocusControl, from its addition to the audio focus 35 * stack to its release. 36 */ 37 public class FocusRequester { 38 39 // on purpose not using this classe's name, as it will only be used from MediaFocusControl 40 private static final String TAG = "MediaFocusControl"; 41 private static final boolean DEBUG = false; 42 43 private AudioFocusDeathHandler mDeathHandler; 44 private final IAudioFocusDispatcher mFocusDispatcher; // may be null 45 private final IBinder mSourceRef; 46 private final String mClientId; 47 private final String mPackageName; 48 private final int mCallingUid; 49 private final MediaFocusControl mFocusController; // never null 50 /** 51 * the audio focus gain request that caused the addition of this object in the focus stack. 52 */ 53 private final int mFocusGainRequest; 54 /** 55 * the flags associated with the gain request that qualify the type of grant (e.g. accepting 56 * delay vs grant must be immediate) 57 */ 58 private final int mGrantFlags; 59 /** 60 * the audio focus loss received my mFocusDispatcher, is AudioManager.AUDIOFOCUS_NONE if 61 * it never lost focus. 62 */ 63 private int mFocusLossReceived; 64 /** 65 * the audio attributes associated with the focus request 66 */ 67 private final AudioAttributes mAttributes; 68 69 /** 70 * Class constructor 71 * @param aa 72 * @param focusRequest 73 * @param grantFlags 74 * @param afl 75 * @param source 76 * @param id 77 * @param hdlr 78 * @param pn 79 * @param uid 80 * @param ctlr cannot be null 81 */ FocusRequester(AudioAttributes aa, int focusRequest, int grantFlags, IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr, String pn, int uid, @NonNull MediaFocusControl ctlr)82 FocusRequester(AudioAttributes aa, int focusRequest, int grantFlags, 83 IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr, 84 String pn, int uid, @NonNull MediaFocusControl ctlr) { 85 mAttributes = aa; 86 mFocusDispatcher = afl; 87 mSourceRef = source; 88 mClientId = id; 89 mDeathHandler = hdlr; 90 mPackageName = pn; 91 mCallingUid = uid; 92 mFocusGainRequest = focusRequest; 93 mGrantFlags = grantFlags; 94 mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE; 95 mFocusController = ctlr; 96 } 97 98 hasSameClient(String otherClient)99 boolean hasSameClient(String otherClient) { 100 try { 101 return mClientId.compareTo(otherClient) == 0; 102 } catch (NullPointerException e) { 103 return false; 104 } 105 } 106 isLockedFocusOwner()107 boolean isLockedFocusOwner() { 108 return ((mGrantFlags & AudioManager.AUDIOFOCUS_FLAG_LOCK) != 0); 109 } 110 hasSameBinder(IBinder ib)111 boolean hasSameBinder(IBinder ib) { 112 return (mSourceRef != null) && mSourceRef.equals(ib); 113 } 114 hasSamePackage(String pack)115 boolean hasSamePackage(String pack) { 116 try { 117 return mPackageName.compareTo(pack) == 0; 118 } catch (NullPointerException e) { 119 return false; 120 } 121 } 122 hasSameUid(int uid)123 boolean hasSameUid(int uid) { 124 return mCallingUid == uid; 125 } 126 getClientId()127 String getClientId() { 128 return mClientId; 129 } 130 getGainRequest()131 int getGainRequest() { 132 return mFocusGainRequest; 133 } 134 getGrantFlags()135 int getGrantFlags() { 136 return mGrantFlags; 137 } 138 getAudioAttributes()139 AudioAttributes getAudioAttributes() { 140 return mAttributes; 141 } 142 143 focusChangeToString(int focus)144 private static String focusChangeToString(int focus) { 145 switch(focus) { 146 case AudioManager.AUDIOFOCUS_NONE: 147 return "none"; 148 case AudioManager.AUDIOFOCUS_GAIN: 149 return "GAIN"; 150 case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT: 151 return "GAIN_TRANSIENT"; 152 case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK: 153 return "GAIN_TRANSIENT_MAY_DUCK"; 154 case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE: 155 return "GAIN_TRANSIENT_EXCLUSIVE"; 156 case AudioManager.AUDIOFOCUS_LOSS: 157 return "LOSS"; 158 case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: 159 return "LOSS_TRANSIENT"; 160 case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: 161 return "LOSS_TRANSIENT_CAN_DUCK"; 162 default: 163 return "[invalid focus change" + focus + "]"; 164 } 165 } 166 focusGainToString()167 private String focusGainToString() { 168 return focusChangeToString(mFocusGainRequest); 169 } 170 focusLossToString()171 private String focusLossToString() { 172 return focusChangeToString(mFocusLossReceived); 173 } 174 flagsToString(int flags)175 private static String flagsToString(int flags) { 176 String msg = new String(); 177 if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) != 0) { 178 msg += "DELAY_OK"; 179 } 180 if ((flags & AudioManager.AUDIOFOCUS_FLAG_LOCK) != 0) { 181 if (!msg.isEmpty()) { msg += "|"; } 182 msg += "LOCK"; 183 } 184 if ((flags & AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) != 0) { 185 if (!msg.isEmpty()) { msg += "|"; } 186 msg += "PAUSES_ON_DUCKABLE_LOSS"; 187 } 188 return msg; 189 } 190 dump(PrintWriter pw)191 void dump(PrintWriter pw) { 192 pw.println(" source:" + mSourceRef 193 + " -- pack: " + mPackageName 194 + " -- client: " + mClientId 195 + " -- gain: " + focusGainToString() 196 + " -- flags: " + flagsToString(mGrantFlags) 197 + " -- loss: " + focusLossToString() 198 + " -- uid: " + mCallingUid 199 + " -- attr: " + mAttributes); 200 } 201 202 release()203 void release() { 204 try { 205 if (mSourceRef != null && mDeathHandler != null) { 206 mSourceRef.unlinkToDeath(mDeathHandler, 0); 207 mDeathHandler = null; 208 } 209 } catch (java.util.NoSuchElementException e) { 210 Log.e(TAG, "FocusRequester.release() hit ", e); 211 } 212 } 213 214 @Override finalize()215 protected void finalize() throws Throwable { 216 release(); 217 super.finalize(); 218 } 219 220 /** 221 * For a given audio focus gain request, return the audio focus loss type that will result 222 * from it, taking into account any previous focus loss. 223 * @param gainRequest 224 * @return the audio focus loss type that matches the gain request 225 */ focusLossForGainRequest(int gainRequest)226 private int focusLossForGainRequest(int gainRequest) { 227 switch(gainRequest) { 228 case AudioManager.AUDIOFOCUS_GAIN: 229 switch(mFocusLossReceived) { 230 case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: 231 case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: 232 case AudioManager.AUDIOFOCUS_LOSS: 233 case AudioManager.AUDIOFOCUS_NONE: 234 return AudioManager.AUDIOFOCUS_LOSS; 235 } 236 case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE: 237 case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT: 238 switch(mFocusLossReceived) { 239 case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: 240 case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: 241 case AudioManager.AUDIOFOCUS_NONE: 242 return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT; 243 case AudioManager.AUDIOFOCUS_LOSS: 244 return AudioManager.AUDIOFOCUS_LOSS; 245 } 246 case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK: 247 switch(mFocusLossReceived) { 248 case AudioManager.AUDIOFOCUS_NONE: 249 case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: 250 return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK; 251 case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: 252 return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT; 253 case AudioManager.AUDIOFOCUS_LOSS: 254 return AudioManager.AUDIOFOCUS_LOSS; 255 } 256 default: 257 Log.e(TAG, "focusLossForGainRequest() for invalid focus request "+ gainRequest); 258 return AudioManager.AUDIOFOCUS_NONE; 259 } 260 } 261 262 /** 263 * Called synchronized on MediaFocusControl.mAudioFocusLock 264 */ handleExternalFocusGain(int focusGain)265 void handleExternalFocusGain(int focusGain) { 266 int focusLoss = focusLossForGainRequest(focusGain); 267 handleFocusLoss(focusLoss); 268 } 269 270 /** 271 * Called synchronized on MediaFocusControl.mAudioFocusLock 272 */ handleFocusGain(int focusGain)273 void handleFocusGain(int focusGain) { 274 try { 275 mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE; 276 mFocusController.notifyExtPolicyFocusGrant_syncAf(toAudioFocusInfo(), 277 AudioManager.AUDIOFOCUS_REQUEST_GRANTED); 278 if (mFocusDispatcher != null) { 279 if (DEBUG) { 280 Log.v(TAG, "dispatching " + focusChangeToString(focusGain) + " to " 281 + mClientId); 282 } 283 mFocusDispatcher.dispatchAudioFocusChange(focusGain, mClientId); 284 } 285 } catch (android.os.RemoteException e) { 286 Log.e(TAG, "Failure to signal gain of audio focus due to: ", e); 287 } 288 } 289 290 /** 291 * Called synchronized on MediaFocusControl.mAudioFocusLock 292 */ handleFocusLoss(int focusLoss)293 void handleFocusLoss(int focusLoss) { 294 try { 295 if (focusLoss != mFocusLossReceived) { 296 mFocusLossReceived = focusLoss; 297 // before dispatching a focus loss, check if the following conditions are met: 298 // 1/ the framework is not supposed to notify the focus loser on a DUCK loss 299 // 2/ it is a DUCK loss 300 // 3/ the focus loser isn't flagged as pausing in a DUCK loss 301 // if they are, do not notify the focus loser 302 if (!mFocusController.mustNotifyFocusOwnerOnDuck() 303 && mFocusLossReceived == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK 304 && (mGrantFlags 305 & AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) == 0) { 306 if (DEBUG) { 307 Log.v(TAG, "NOT dispatching " + focusChangeToString(mFocusLossReceived) 308 + " to " + mClientId + ", to be handled externally"); 309 } 310 mFocusController.notifyExtPolicyFocusLoss_syncAf( 311 toAudioFocusInfo(), false /* wasDispatched */); 312 return; 313 } 314 if (mFocusDispatcher != null) { 315 if (DEBUG) { 316 Log.v(TAG, "dispatching " + focusChangeToString(mFocusLossReceived) + " to " 317 + mClientId); 318 } 319 mFocusController.notifyExtPolicyFocusLoss_syncAf( 320 toAudioFocusInfo(), true /* wasDispatched */); 321 mFocusDispatcher.dispatchAudioFocusChange(mFocusLossReceived, mClientId); 322 } 323 } 324 } catch (android.os.RemoteException e) { 325 Log.e(TAG, "Failure to signal loss of audio focus due to:", e); 326 } 327 } 328 toAudioFocusInfo()329 AudioFocusInfo toAudioFocusInfo() { 330 return new AudioFocusInfo(mAttributes, mClientId, mPackageName, 331 mFocusGainRequest, mFocusLossReceived, mGrantFlags); 332 } 333 } 334