/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.media; import android.media.MediaFocusControl.AudioFocusDeathHandler; import android.os.IBinder; import android.util.Log; import java.io.PrintWriter; /** * @hide * Class to handle all the information about a user of audio focus. The lifecycle of each * instance is managed by android.media.MediaFocusControl, from its addition to the audio focus * stack to its release. */ class FocusRequester { // on purpose not using this classe's name, as it will only be used from MediaFocusControl private static final String TAG = "MediaFocusControl"; private static final boolean DEBUG = false; private AudioFocusDeathHandler mDeathHandler; private final IAudioFocusDispatcher mFocusDispatcher; // may be null private final IBinder mSourceRef; private final String mClientId; private final String mPackageName; private final int mCallingUid; /** * the audio focus gain request that caused the addition of this object in the focus stack. */ private final int mFocusGainRequest; /** * the audio focus loss received my mFocusDispatcher, is AudioManager.AUDIOFOCUS_NONE if * it never lost focus. */ private int mFocusLossReceived; /** * the stream type associated with the focus request */ private final int mStreamType; FocusRequester(int streamType, int focusRequest, IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr, String pn, int uid) { mStreamType = streamType; mFocusDispatcher = afl; mSourceRef = source; mClientId = id; mDeathHandler = hdlr; mPackageName = pn; mCallingUid = uid; mFocusGainRequest = focusRequest; mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE; } boolean hasSameClient(String otherClient) { try { return mClientId.compareTo(otherClient) == 0; } catch (NullPointerException e) { return false; } } boolean hasSameBinder(IBinder ib) { return (mSourceRef != null) && mSourceRef.equals(ib); } boolean hasSamePackage(String pack) { try { return mPackageName.compareTo(pack) == 0; } catch (NullPointerException e) { return false; } } boolean hasSameUid(int uid) { return mCallingUid == uid; } int getGainRequest() { return mFocusGainRequest; } int getStreamType() { return mStreamType; } private static String focusChangeToString(int focus) { switch(focus) { case AudioManager.AUDIOFOCUS_NONE: return "none"; case AudioManager.AUDIOFOCUS_GAIN: return "GAIN"; case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT: return "GAIN_TRANSIENT"; case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK: return "GAIN_TRANSIENT_MAY_DUCK"; case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE: return "GAIN_TRANSIENT_EXCLUSIVE"; case AudioManager.AUDIOFOCUS_LOSS: return "LOSS"; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: return "LOSS_TRANSIENT"; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: return "LOSS_TRANSIENT_CAN_DUCK"; default: return "[invalid focus change" + focus + "]"; } } private String focusGainToString() { return focusChangeToString(mFocusGainRequest); } private String focusLossToString() { return focusChangeToString(mFocusLossReceived); } void dump(PrintWriter pw) { pw.println(" source:" + mSourceRef + " -- pack: " + mPackageName + " -- client: " + mClientId + " -- gain: " + focusGainToString() + " -- loss: " + focusLossToString() + " -- uid: " + mCallingUid + " -- stream: " + mStreamType); } void release() { try { if (mSourceRef != null && mDeathHandler != null) { mSourceRef.unlinkToDeath(mDeathHandler, 0); mDeathHandler = null; } } catch (java.util.NoSuchElementException e) { Log.e(TAG, "FocusRequester.release() hit ", e); } } @Override protected void finalize() throws Throwable { release(); super.finalize(); } /** * For a given audio focus gain request, return the audio focus loss type that will result * from it, taking into account any previous focus loss. * @param gainRequest * @return the audio focus loss type that matches the gain request */ private int focusLossForGainRequest(int gainRequest) { switch(gainRequest) { case AudioManager.AUDIOFOCUS_GAIN: switch(mFocusLossReceived) { case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: case AudioManager.AUDIOFOCUS_LOSS: case AudioManager.AUDIOFOCUS_NONE: return AudioManager.AUDIOFOCUS_LOSS; } case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE: case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT: switch(mFocusLossReceived) { case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: case AudioManager.AUDIOFOCUS_NONE: return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT; case AudioManager.AUDIOFOCUS_LOSS: return AudioManager.AUDIOFOCUS_LOSS; } case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK: switch(mFocusLossReceived) { case AudioManager.AUDIOFOCUS_NONE: case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: return AudioManager.AUDIOFOCUS_LOSS_TRANSIENT; case AudioManager.AUDIOFOCUS_LOSS: return AudioManager.AUDIOFOCUS_LOSS; } default: Log.e(TAG, "focusLossForGainRequest() for invalid focus request "+ gainRequest); return AudioManager.AUDIOFOCUS_NONE; } } void handleExternalFocusGain(int focusGain) { int focusLoss = focusLossForGainRequest(focusGain); handleFocusLoss(focusLoss); } void handleFocusGain(int focusGain) { try { if (mFocusDispatcher != null) { if (DEBUG) { Log.v(TAG, "dispatching " + focusChangeToString(focusGain) + " to " + mClientId); } mFocusDispatcher.dispatchAudioFocusChange(focusGain, mClientId); } mFocusLossReceived = AudioManager.AUDIOFOCUS_NONE; } catch (android.os.RemoteException e) { Log.e(TAG, "Failure to signal gain of audio focus due to: ", e); } } void handleFocusLoss(int focusLoss) { try { if (focusLoss != mFocusLossReceived) { if (mFocusDispatcher != null) { if (DEBUG) { Log.v(TAG, "dispatching " + focusChangeToString(focusLoss) + " to " + mClientId); } mFocusDispatcher.dispatchAudioFocusChange(focusLoss, mClientId); } mFocusLossReceived = focusLoss; } } catch (android.os.RemoteException e) { Log.e(TAG, "Failure to signal loss of audio focus due to:", e); } } }