Loading media/java/android/media/FocusRequester.java 0 → 100644 +210 −0 Original line number Diff line number Diff line /* * 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); } } } media/java/android/media/MediaFocusControl.java +35 −165 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading @@ -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: Loading @@ -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); } } } Loading @@ -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(); Loading @@ -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(); } } } Loading @@ -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 } Loading @@ -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; Loading @@ -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) { Loading Loading @@ -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); Loading @@ -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); } Loading @@ -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 Loading Loading @@ -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; } } Loading @@ -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; } Loading Loading
media/java/android/media/FocusRequester.java 0 → 100644 +210 −0 Original line number Diff line number Diff line /* * 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); } } }
media/java/android/media/MediaFocusControl.java +35 −165 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading @@ -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: Loading @@ -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); } } } Loading @@ -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(); Loading @@ -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(); } } } Loading @@ -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 } Loading @@ -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; Loading @@ -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) { Loading Loading @@ -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); Loading @@ -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); } Loading @@ -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 Loading Loading @@ -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; } } Loading @@ -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; } Loading