Fix bug 2670395 and 2599698

When the user selects a "Silent" notification sound, the Uri encoded
    path is an empty string. Setting this Uri as the data source of the
    MediaPlayer used to play notifications caused the completion listener
    to not be called, which with the AudioFocus logic causes the Music
    app to pause and never resume. The NotificationPlayer modifications
    cause the MediaPlayer for the notification to only request audio
    focus when the data source is not empty.
    The audio focus code in AudioService is defensively synchronized
    against a unique lock, and the exception observed in bug 2670395
    is explicitely caught in case another edge case wasn't caught by
    this fix.
    The AudioFocus handling in AudioManager is modified so only the
    requestAudioFocus and abandonAudioFocus methods are meant to be
    used, as registerAudioFocusListener and unregisterAudioFocusListener
    provided no additional functionality over the request/abandon
    methods. abandonAudioFocus() also removes the listener from the
    map in AudioManager since after abandonning focus, the listener
    would no longer be called.

Change-Id: I3b553ee8a8163c25e01117d7e5479dd5fdfa7c6b
This commit is contained in:
Jean-Michel Trivi
2010-05-10 20:02:46 -07:00
parent f78acacb0d
commit 392a2bbb52
3 changed files with 34 additions and 28 deletions

View File

@@ -1382,7 +1382,7 @@ public class AudioManager {
}
/**
* Register a listener for audio focus updates.
* TODO hide
*/
public void registerAudioFocusListener(OnAudioFocusChangeListener l) {
synchronized(mFocusListenerLock) {
@@ -1394,16 +1394,10 @@ public class AudioManager {
}
/**
* TODO document for SDK
* TODO hide
*/
public void unregisterAudioFocusListener(OnAudioFocusChangeListener l) {
// notify service to remove it from audio focus stack
IAudioService service = getService();
try {
service.unregisterAudioFocusClient(getIdForAudioFocusListener(l));
} catch (RemoteException e) {
Log.e(TAG, "Can't call unregisterFocusClient() from AudioService due to "+e);
}
// remove locally
synchronized(mFocusListenerLock) {
mAudioFocusIdListenerMap.remove(getIdForAudioFocusListener(l));
@@ -1462,7 +1456,7 @@ public class AudioManager {
*/
public int abandonAudioFocus(OnAudioFocusChangeListener l) {
int status = AUDIOFOCUS_REQUEST_FAILED;
registerAudioFocusListener(l);
unregisterAudioFocusListener(l);
IAudioService service = getService();
try {
status = service.abandonAudioFocus(mAudioFocusDispatcher,

View File

@@ -1930,6 +1930,8 @@ public class AudioService extends IAudioService.Stub {
*/
private final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls";
private final static Object mAudioFocusLock = new Object();
private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
@@ -2000,7 +2002,7 @@ public class AudioService extends IAudioService.Stub {
*/
private void dumpFocusStack(PrintWriter pw) {
pw.println("\nAudio Focus stack entries:");
synchronized(mFocusStack) {
synchronized(mAudioFocusLock) {
Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
while(stackIterator.hasNext()) {
FocusStackEntry fse = stackIterator.next();
@@ -2091,7 +2093,7 @@ public class AudioService extends IAudioService.Stub {
}
public void binderDied() {
synchronized(mFocusStack) {
synchronized(mAudioFocusLock) {
Log.w(TAG, " AudioFocus audio focus client died");
removeFocusStackEntryForClient(mCb);
}
@@ -2117,11 +2119,11 @@ public class AudioService extends IAudioService.Stub {
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
if (!canReassignAudioFocus()) {
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
synchronized(mAudioFocusLock) {
if (!canReassignAudioFocus()) {
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
synchronized(mFocusStack) {
if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) {
// if focus is already owned by this client and the reason for acquiring the focus
// hasn't changed, don't do anything
@@ -2151,7 +2153,7 @@ public class AudioService extends IAudioService.Stub {
// push focus requester at the top of the audio focus stack
mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, false, fd, cb,
clientId));
}//synchronized(mFocusStack)
}//synchronized(mAudioFocusLock)
// handle the potential premature death of the new holder of the focus
// (premature death == death before abandoning focus) for a client which is not the
@@ -2173,10 +2175,17 @@ public class AudioService extends IAudioService.Stub {
/** @see AudioManager#abandonAudioFocus(IAudioFocusDispatcher) */
public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) {
Log.i(TAG, " AudioFocus abandonAudioFocus() from " + clientId);
// this will take care of notifying the new focus owner if needed
synchronized(mFocusStack) {
removeFocusStackEntry(clientId, true);
try {
// this will take care of notifying the new focus owner if needed
synchronized(mAudioFocusLock) {
removeFocusStackEntry(clientId, true);
}
} catch (java.util.ConcurrentModificationException cme) {
// Catching this exception here is temporary. It is here just to prevent
// a crash seen when the "Silent" notification is played. This is believed to be fixed
// but this try catch block is left just to be safe.
Log.e(TAG, "FATAL EXCEPTION AudioFocus abandonAudioFocus() caused " + cme);
cme.printStackTrace();
}
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
@@ -2184,7 +2193,7 @@ public class AudioService extends IAudioService.Stub {
public void unregisterAudioFocusClient(String clientId) {
synchronized(mFocusStack) {
synchronized(mAudioFocusLock) {
removeFocusStackEntry(clientId, false);
}
}

View File

@@ -88,12 +88,15 @@ public class NotificationPlayer implements OnCompletionListener {
player.setDataSource(mCmd.context, mCmd.uri);
player.setLooping(mCmd.looping);
player.prepare();
if (mCmd.looping) {
audioManager.requestAudioFocus(null, mCmd.stream,
AudioManager.AUDIOFOCUS_GAIN);
} else {
audioManager.requestAudioFocus(null, mCmd.stream,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
if ((mCmd.uri != null) && (mCmd.uri.getEncodedPath() != null)
&& (mCmd.uri.getEncodedPath().length() > 0)) {
if (mCmd.looping) {
audioManager.requestAudioFocus(null, mCmd.stream,
AudioManager.AUDIOFOCUS_GAIN);
} else {
audioManager.requestAudioFocus(null, mCmd.stream,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
}
}
player.setOnCompletionListener(NotificationPlayer.this);
player.start();