diff options
author | Jean-Michel Trivi <jmtrivi@google.com> | 2013-07-29 18:09:41 -0700 |
---|---|---|
committer | Jean-Michel Trivi <jmtrivi@google.com> | 2013-07-30 11:27:08 -0700 |
commit | 83283f23eb1b7c1576e253c644b8aade6f657d0a (patch) | |
tree | 3c0219a9e5f116b73088715333d49fee1c36622f /media/java | |
parent | 00bf4b18173b8921d7a5cecbd8b8d3745470b5d0 (diff) | |
download | frameworks_base-83283f23eb1b7c1576e253c644b8aade6f657d0a.zip frameworks_base-83283f23eb1b7c1576e253c644b8aade6f657d0a.tar.gz frameworks_base-83283f23eb1b7c1576e253c644b8aade6f657d0a.tar.bz2 |
Audio focus request managed by FocusRequester class
Move all audio focus request functionality under a new class/file.
Clean up encapsulation of data related to the request.
Change-Id: I989796e1ee1a5fc99799a64e1612294e0e40fa6d
Diffstat (limited to 'media/java')
-rw-r--r-- | media/java/android/media/FocusRequester.java | 210 | ||||
-rw-r--r-- | media/java/android/media/MediaFocusControl.java | 200 |
2 files changed, 245 insertions, 165 deletions
diff --git a/media/java/android/media/FocusRequester.java b/media/java/android/media/FocusRequester.java new file mode 100644 index 0000000..e1a175d --- /dev/null +++ b/media/java/android/media/FocusRequester.java @@ -0,0 +1,210 @@ +/* + * 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 { + + /** + * Used to indicate no audio focus has been gained or lost. + */ + private static final int AUDIOFOCUS_NONE = 0; + + // on purpose not using this classe's name, as it will only be used from MediaFocusControl + private static final String TAG = "MediaFocusControl"; + + private AudioFocusDeathHandler mDeathHandler; + private final IAudioFocusDispatcher mFocusDispatcher; + 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 MediaFocusControl.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 = AUDIOFOCUS_NONE; + } + + + boolean canDispatchFocus() { + return (mFocusDispatcher != null); + } + + 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 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_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. + * @param gainRequest + * @return the audio focus loss type that matches the gain request + */ + private int focusLossForGainRequest(int gainRequest) { + // FIXME to be updated to take into account mFocusLossReceived + return -gainRequest; // focus loss codes are the inverse of the gain codes + } + + void handleExternalFocusGain(int focusGain) { + try { + int focusLoss = focusLossForGainRequest(focusGain); + mFocusDispatcher.dispatchAudioFocusChange(focusLoss, mClientId); + mFocusLossReceived = focusLoss; + } catch (android.os.RemoteException e) { + Log.e(TAG, "Failure to signal loss of focus: ", e); + } + } + + void handleFocusGain(int focusGain) { + try { + mFocusDispatcher.dispatchAudioFocusChange(focusGain, mClientId); + mFocusLossReceived = 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 { + mFocusDispatcher.dispatchAudioFocusChange( + focusLoss, mClientId); + } catch (android.os.RemoteException e) { + Log.e(TAG, "Failure to signal loss of audio focus due to:", e); + } + } + +} diff --git a/media/java/android/media/MediaFocusControl.java b/media/java/android/media/MediaFocusControl.java index 2b9d809..ca57b92 100644 --- a/media/java/android/media/MediaFocusControl.java +++ b/media/java/android/media/MediaFocusControl.java @@ -66,11 +66,6 @@ public class MediaFocusControl implements OnFinished { /** Debug volumes */ protected static final boolean DEBUG_VOL = false; - /** - * Used to indicate no audio focus has been gained or lost. - */ - private static final int AUDIOFOCUS_NONE = 0; - /** Used to alter media button redirection when the phone is ringing. */ private boolean mIsRinging = false; @@ -270,11 +265,11 @@ public class MediaFocusControl implements OnFinished { */ protected void discardAudioFocusOwner() { synchronized(mAudioFocusLock) { - if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) { + if (!mFocusStack.empty() && mFocusStack.peek().canDispatchFocus()) { // notify the current focus owner it lost focus after removing it from stack - final FocusStackEntry exFocusOwner = mFocusStack.pop(); + final FocusRequester exFocusOwner = mFocusStack.pop(); exFocusOwner.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS); - exFocusOwner.unlinkToDeath(); + exFocusOwner.release(); // clear RCD synchronized(mRCStack) { clearRemoteControlDisplay_syncAfRcs(); @@ -285,128 +280,14 @@ public class MediaFocusControl implements OnFinished { private void notifyTopOfAudioFocusStack() { // notify the top of the stack it gained focus - if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) { + if (!mFocusStack.empty() && mFocusStack.peek().canDispatchFocus()) { if (canReassignAudioFocus()) { mFocusStack.peek().handleFocusGain(AudioManager.AUDIOFOCUS_GAIN); } } } - - private static class FocusStackEntry { - int mStreamType = -1;// no stream type - IAudioFocusDispatcher mFocusDispatcher = null; - IBinder mSourceRef = null; - String mClientId; - /** the audio focus gain request that caused the addition of this entry in the stack */ - int mFocusGainRequest; - int mFocusLossReceived; - AudioFocusDeathHandler mHandler; - String mPackageName; - int mCallingUid; - - static String focusChangeToString(int focus) { - switch(focus) { - 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_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 + "]"; - } - } - - String focusGainToString() { - return focusChangeToString(mFocusGainRequest); - } - - String focusLossToString() { - if (mFocusLossReceived == 0) { - return "none"; - } else { - return focusChangeToString(mFocusLossReceived); - } - } - - FocusStackEntry(int streamType, int focusRequest, - IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr, - String pn, int uid) { - mStreamType = streamType; - mFocusDispatcher = afl; - mSourceRef = source; - mClientId = id; - mFocusGainRequest = focusRequest; - mFocusLossReceived = AUDIOFOCUS_NONE; - mHandler = hdlr; - mPackageName = pn; - mCallingUid = uid; - } - - void unlinkToDeath() { - try { - if (mSourceRef != null && mHandler != null) { - mSourceRef.unlinkToDeath(mHandler, 0); - mHandler = null; - } - } catch (java.util.NoSuchElementException e) { - Log.e(TAG, "Encountered " + e + " in FocusStackEntry.unlinkToDeath()"); - } - } - - @Override - protected void finalize() throws Throwable { - unlinkToDeath(); // unlink exception handled inside method - super.finalize(); - } - - /** - * For a given audio focus gain request, return the audio focus loss type that will result - * from it. - * @param gainRequest - * @return the audio focus loss type that matches the gain request - */ - int focusLossForGainRequest(int gainRequest) { - return -1 * gainRequest; // focus loss codes are the inverse of the gain codes - } - - void handleExternalFocusGain(int focusGain) { - try { - int focusLoss = focusLossForGainRequest(focusGain); - mFocusDispatcher.dispatchAudioFocusChange(focusLoss, mClientId); - mFocusLossReceived = focusLoss; - } catch (RemoteException e) { - Log.e(TAG, " Failure to signal loss of focus: ", e); - } - } - - void handleFocusGain(int focusGain) { - try { - mFocusDispatcher.dispatchAudioFocusChange(focusGain, mClientId); - mFocusLossReceived = AUDIOFOCUS_NONE; - } catch (RemoteException e) { - Log.e(TAG, " Failure to signal gain of audio focus due to: ", e); - } - } - - void handleFocusLoss(int focusLoss) { - try { - mFocusDispatcher.dispatchAudioFocusChange( - focusLoss, mClientId); - } catch (RemoteException e) { - Log.e(TAG, "Failure to signal loss of audio focus due to:", e); - } - } - } - - private final Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>(); + private final Stack<FocusRequester> mFocusStack = new Stack<FocusRequester>(); /** * Helper function: @@ -415,16 +296,9 @@ public class MediaFocusControl implements OnFinished { private void dumpFocusStack(PrintWriter pw) { pw.println("\nAudio Focus stack entries (last is top of stack):"); synchronized(mAudioFocusLock) { - Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator(); + Iterator<FocusRequester> stackIterator = mFocusStack.iterator(); while(stackIterator.hasNext()) { - FocusStackEntry fse = stackIterator.next(); - pw.println(" source:" + fse.mSourceRef - + " -- pack: " + fse.mPackageName - + " -- client: " + fse.mClientId - + " -- gain: " + fse.focusGainToString() - + " -- loss: " + fse.focusLossToString() - + " -- uid: " + fse.mCallingUid - + " -- stream: " + fse.mStreamType); + stackIterator.next().dump(pw); } } } @@ -439,11 +313,11 @@ public class MediaFocusControl implements OnFinished { */ private void removeFocusStackEntry(String clientToRemove, boolean signal) { // is the current top of the focus stack abandoning focus? (because of request, not death) - if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove)) + if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientToRemove)) { //Log.i(TAG, " removeFocusStackEntry() removing top of stack"); - FocusStackEntry fse = mFocusStack.pop(); - fse.unlinkToDeath(); + FocusRequester fr = mFocusStack.pop(); + fr.release(); if (signal) { // notify the new top of the stack it gained focus notifyTopOfAudioFocusStack(); @@ -457,14 +331,14 @@ public class MediaFocusControl implements OnFinished { // no need to update focus. // (using an iterator on the stack so we can safely remove an entry after having // evaluated it, traversal order doesn't matter here) - Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator(); + Iterator<FocusRequester> stackIterator = mFocusStack.iterator(); while(stackIterator.hasNext()) { - FocusStackEntry fse = (FocusStackEntry)stackIterator.next(); - if(fse.mClientId.equals(clientToRemove)) { + FocusRequester fr = (FocusRequester)stackIterator.next(); + if(fr.hasSameClient(clientToRemove)) { Log.i(TAG, "AudioFocus removeFocusStackEntry(): removing entry for " - + fse.mClientId); + + clientToRemove); stackIterator.remove(); - fse.unlinkToDeath(); + fr.release(); } } } @@ -478,15 +352,14 @@ public class MediaFocusControl implements OnFinished { private void removeFocusStackEntryForClient(IBinder cb) { // is the owner of the audio focus part of the client to remove? boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() && - mFocusStack.peek().mSourceRef.equals(cb); + mFocusStack.peek().hasSameBinder(cb); // (using an iterator on the stack so we can safely remove an entry after having // evaluated it, traversal order doesn't matter here) - Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator(); + Iterator<FocusRequester> stackIterator = mFocusStack.iterator(); while(stackIterator.hasNext()) { - FocusStackEntry fse = (FocusStackEntry)stackIterator.next(); - if(fse.mSourceRef.equals(cb)) { - Log.i(TAG, "AudioFocus removeFocusStackEntry(): removing entry for " - + fse.mClientId); + FocusRequester fr = (FocusRequester)stackIterator.next(); + if(fr.hasSameBinder(cb)) { + Log.i(TAG, "AudioFocus removeFocusStackEntry(): removing entry for " + cb); stackIterator.remove(); // the client just died, no need to unlink to its death } @@ -509,7 +382,7 @@ public class MediaFocusControl implements OnFinished { private boolean canReassignAudioFocus() { // focus requests are rejected during a phone call or when the phone is ringing // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus - if (!mFocusStack.isEmpty() && IN_VOICE_COMM_FOCUS_ID.equals(mFocusStack.peek().mClientId)) { + if (!mFocusStack.isEmpty() && mFocusStack.peek().hasSameClient(IN_VOICE_COMM_FOCUS_ID)) { return false; } return true; @@ -519,7 +392,7 @@ public class MediaFocusControl implements OnFinished { * Inner class to monitor audio focus client deaths, and remove them from the audio focus * stack if necessary. */ - private class AudioFocusDeathHandler implements IBinder.DeathRecipient { + protected class AudioFocusDeathHandler implements IBinder.DeathRecipient { private IBinder mCb; // To be notified of client's death AudioFocusDeathHandler(IBinder cb) { @@ -574,10 +447,10 @@ public class MediaFocusControl implements OnFinished { return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } - if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) { + if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientId)) { // if focus is already owned by this client and the reason for acquiring the focus // hasn't changed, don't do anything - if (mFocusStack.peek().mFocusGainRequest == focusChangeHint) { + if (mFocusStack.peek().getGainRequest() == focusChangeHint) { // unlink death handler so it can be gc'ed. // linkToDeath() creates a JNI global reference preventing collection. cb.unlinkToDeath(afdh, 0); @@ -585,12 +458,12 @@ public class MediaFocusControl implements OnFinished { } // the reason for the audio focus request has changed: remove the current top of // stack and respond as if we had a new focus owner - FocusStackEntry fse = mFocusStack.pop(); - fse.unlinkToDeath(); + FocusRequester fr = mFocusStack.pop(); + fr.release(); } // notify current top of stack it is losing focus - if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) { + if (!mFocusStack.empty() && mFocusStack.peek().canDispatchFocus()) { mFocusStack.peek().handleExternalFocusGain(focusChangeHint); } @@ -598,7 +471,7 @@ public class MediaFocusControl implements OnFinished { removeFocusStackEntry(clientId, false /* signal */); // push focus requester at the top of the audio focus stack - mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, fd, cb, + mFocusStack.push(new FocusRequester(mainStreamType, focusChangeHint, fd, cb, clientId, afdh, callingPackageName, Binder.getCallingUid())); // there's a new top of the stack, let the remote control know @@ -1609,13 +1482,13 @@ public class MediaFocusControl implements OnFinished { // characteristics: // - focus gain on STREAM_MUSIC stream // - non-transient focus gain on a stream other than music - FocusStackEntry af = null; + FocusRequester af = null; try { for (int index = mFocusStack.size()-1; index >= 0; index--) { - FocusStackEntry fse = mFocusStack.elementAt(index); - if ((fse.mStreamType == AudioManager.STREAM_MUSIC) - || (fse.mFocusGainRequest == AudioManager.AUDIOFOCUS_GAIN)) { - af = fse; + FocusRequester fr = mFocusStack.elementAt(index); + if ((fr.getStreamType() == AudioManager.STREAM_MUSIC) + || (fr.getGainRequest() == AudioManager.AUDIOFOCUS_GAIN)) { + af = fr; break; } } @@ -1629,16 +1502,13 @@ public class MediaFocusControl implements OnFinished { } // if the audio focus and RC owners belong to different packages, there is a mismatch, clear - if ((mRCStack.peek().mCallingPackageName != null) - && (af.mPackageName != null) - && !(mRCStack.peek().mCallingPackageName.compareTo( - af.mPackageName) == 0)) { + if (!af.hasSamePackage(mRCStack.peek().mCallingPackageName)) { clearRemoteControlDisplay_syncAfRcs(); return; } // if the audio focus didn't originate from the same Uid as the one in which the remote // control information will be retrieved, clear - if (mRCStack.peek().mCallingUid != af.mCallingUid) { + if (!af.hasSameUid(mRCStack.peek().mCallingUid)) { clearRemoteControlDisplay_syncAfRcs(); return; } |