Merge "Update AudioService to reflect phone state changes with AudioFocus. Phone calls and rings are treated as a new client, with a dedicated client ID that identifies it in the audio focus stack. When it is present in the stack, others clients cannot request audio focus (request is denied). Because the phone state is now mostly handled like a new client, there is no need monitor mode changes and to keep track of undispatched focus changes, as those will delayed focus gains will now automatically happen at the end of the call when the phone client is removed from the focus stack." into froyo

This commit is contained in:
Jean-Michel Trivi
2010-04-20 15:57:30 -07:00
committed by Android (Google) Code Review

View File

@ -41,6 +41,8 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
import android.provider.Settings.System;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.KeyEvent;
import android.view.VolumePanel;
@ -305,6 +307,11 @@ public class AudioService extends IAudioService.Stub {
intentFilter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON);
intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
context.registerReceiver(mMediaButtonReceiver, intentFilter);
// Register for phone state monitoring
TelephonyManager tmgr = (TelephonyManager)
context.getSystemService(Context.TELEPHONY_SERVICE);
tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}
private void createAudioSystemThread() {
@ -707,7 +714,6 @@ public class AudioService extends IAudioService.Stub {
}
if (mode != mMode) {
if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) {
checkForUndispatchedAudioFocusChange(mMode, mode);
mMode = mode;
synchronized(mSetModeDeathHandlers) {
@ -1903,21 +1909,36 @@ public class AudioService extends IAudioService.Stub {
//==========================================================================================
// AudioFocus
//==========================================================================================
/**
* Flag to indicate that the top of the audio focus stack needs to recover focus
* but hasn't been signaled yet.
*/
private boolean mHasUndispatchedAudioFocus = false;
private void checkForUndispatchedAudioFocusChange(int prevMode, int newMode) {
// when exiting a call
if ((prevMode == AudioSystem.MODE_IN_CALL) && (newMode != AudioSystem.MODE_IN_CALL)) {
// check for undispatched remote control focus gain
if (mHasUndispatchedAudioFocus) {
notifyTopOfAudioFocusStack();
/* constant to identify focus stack entry that is used to hold the focus while the phone
* is ringing or during a call
*/
private final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls";
private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
if (state == TelephonyManager.CALL_STATE_RINGING) {
//Log.v(TAG, " CALL_STATE_RINGING");
int ringVolume = AudioService.this.getStreamVolume(AudioManager.STREAM_RING);
if (ringVolume > 0) {
requestAudioFocus(AudioManager.STREAM_RING,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,
null, null /* both allowed to be null only for this clientId */,
IN_VOICE_COMM_FOCUS_ID /*clientId*/);
}
} else if (state == TelephonyManager.CALL_STATE_OFFHOOK) {
//Log.v(TAG, " CALL_STATE_OFFHOOK");
requestAudioFocus(AudioManager.STREAM_RING,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,
null, null /* both allowed to be null only for this clientId */,
IN_VOICE_COMM_FOCUS_ID /*clientId*/);
} else if (state == TelephonyManager.CALL_STATE_IDLE) {
//Log.v(TAG, " CALL_STATE_IDLE");
abandonAudioFocus(null, IN_VOICE_COMM_FOCUS_ID);
}
}
}
};
private void notifyTopOfAudioFocusStack() {
// notify the top of the stack it gained focus
@ -1926,13 +1947,10 @@ public class AudioService extends IAudioService.Stub {
try {
mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId);
mHasUndispatchedAudioFocus = false;
} catch (RemoteException e) {
Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e);
e.printStackTrace();
}
} else {
mHasUndispatchedAudioFocus = true;
}
}
}
@ -1966,7 +1984,7 @@ public class AudioService extends IAudioService.Stub {
* Display in the log the current entries in the audio focus stack
*/
private void dumpFocusStack(PrintWriter pw) {
pw.println("Audio Focus stack entries:");
pw.println("\nAudio Focus stack entries:");
synchronized(mFocusStack) {
Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
while(stackIterator.hasNext()) {
@ -2038,9 +2056,9 @@ public class AudioService extends IAudioService.Stub {
* Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
*/
private boolean canReassignAudioFocus() {
// focus requests are rejected during a phone call
if (getMode() == AudioSystem.MODE_IN_CALL) {
Log.i(TAG, " AudioFocus can't be reassigned during a call, exiting");
// 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)) {
return false;
}
return true;
@ -2077,7 +2095,9 @@ public class AudioService extends IAudioService.Stub {
// the main stream type for the audio focus request is currently not used. It may
// potentially be used to handle multiple stream type-dependent audio focuses.
if ((cb == null) || !cb.pingBinder()) {
// we need a valid binder callback for clients other than the AudioService's phone
// state listener
if (!IN_VOICE_COMM_FOCUS_ID.equals(clientId) && ((cb == null) || !cb.pingBinder())) {
Log.i(TAG, " AudioFocus DOA client for requestAudioFocus(), exiting");
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
@ -2119,14 +2139,17 @@ public class AudioService extends IAudioService.Stub {
}//synchronized(mFocusStack)
// handle the potential premature death of the new holder of the focus
// (premature death == death before abandoning focus)
// Register for client death notification
AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
try {
cb.linkToDeath(afdh, 0);
} catch (RemoteException e) {
// client has already died!
Log.w(TAG, " AudioFocus requestAudioFocus() could not link to "+cb+" binder death");
// (premature death == death before abandoning focus) for a client which is not the
// AudioService's phone state listener
if (!IN_VOICE_COMM_FOCUS_ID.equals(clientId)) {
// Register for client death notification
AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
try {
cb.linkToDeath(afdh, 0);
} catch (RemoteException e) {
// client has already died!
Log.w(TAG, "AudioFocus requestAudioFocus() could not link to "+cb+" binder death");
}
}
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
@ -2212,7 +2235,7 @@ public class AudioService extends IAudioService.Stub {
* Display in the log the current entries in the remote control focus stack
*/
private void dumpRCStack(PrintWriter pw) {
pw.println("Remote Control stack entries:");
pw.println("\nRemote Control stack entries:");
synchronized(mRCStack) {
Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
while(stackIterator.hasNext()) {