Skip to content

Commit 9f103de

Browse files
author
Eric Laurent
committed
Fix issue 4673378: switching from VoIP to GSM call
The problem is that any app can change the audio mode and override a mode previously set by another app. If two apps (gTalk and Phone) compete for audio mode ownership, there is no mechanism to maintain coherency in the actual audio mode selected. Added a mechanism in AudioService to manage an audio mode request stack. Any app requesting a mode different from NORMAL enters at the top of the stack and the requested mode is applied. When an app sets mode back to NORMAL, it exits the stack and the new mode corresponding to the request at the top of the stack (if any) is applied. Change-Id: I68d1755d0922f680df4a19bfc5ab924f5a5d8ccd
1 parent 6a39c02 commit 9f103de

File tree

1 file changed

+108
-80
lines changed

1 file changed

+108
-80
lines changed

media/java/android/media/AudioService.java

Lines changed: 108 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -343,9 +343,8 @@ public AudioService(Context context) {
343343
readPersistedSettings();
344344
mSettingsObserver = new SettingsObserver();
345345
createStreamStates();
346-
// Call setMode() to initialize mSetModeDeathHandlers
347-
mMode = AudioSystem.MODE_INVALID;
348-
setMode(AudioSystem.MODE_NORMAL, null);
346+
347+
mMode = AudioSystem.MODE_NORMAL;
349348
mMediaServerOk = true;
350349

351350
// Call setRingerModeInt() to apply correct mute
@@ -768,37 +767,27 @@ private class SetModeDeathHandler implements IBinder.DeathRecipient {
768767
private int mPid;
769768
private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client
770769

771-
SetModeDeathHandler(IBinder cb) {
770+
SetModeDeathHandler(IBinder cb, int pid) {
772771
mCb = cb;
773-
mPid = Binder.getCallingPid();
772+
mPid = pid;
774773
}
775774

776775
public void binderDied() {
776+
IBinder newModeOwner = null;
777777
synchronized(mSetModeDeathHandlers) {
778778
Log.w(TAG, "setMode() client died");
779779
int index = mSetModeDeathHandlers.indexOf(this);
780780
if (index < 0) {
781781
Log.w(TAG, "unregistered setMode() client died");
782782
} else {
783-
mSetModeDeathHandlers.remove(this);
784-
// If dead client was a the top of client list,
785-
// apply next mode in the stack
786-
if (index == 0) {
787-
// mSetModeDeathHandlers is never empty as the initial entry
788-
// created when AudioService starts is never removed
789-
SetModeDeathHandler hdlr = mSetModeDeathHandlers.get(0);
790-
int mode = hdlr.getMode();
791-
if (AudioService.this.mMode != mode) {
792-
if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) {
793-
AudioService.this.mMode = mode;
794-
if (mode != AudioSystem.MODE_NORMAL) {
795-
disconnectBluetoothSco(mCb);
796-
}
797-
}
798-
}
799-
}
783+
newModeOwner = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid);
800784
}
801785
}
786+
// when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
787+
// SCO connections not started by the application changing the mode
788+
if (newModeOwner != null) {
789+
disconnectBluetoothSco(newModeOwner);
790+
}
802791
}
803792

804793
public int getPid() {
@@ -828,60 +817,97 @@ public void setMode(int mode, IBinder cb) {
828817
return;
829818
}
830819

831-
synchronized (mSettingsLock) {
820+
IBinder newModeOwner = null;
821+
synchronized(mSetModeDeathHandlers) {
832822
if (mode == AudioSystem.MODE_CURRENT) {
833823
mode = mMode;
834824
}
835-
if (mode != mMode) {
825+
newModeOwner = setModeInt(mode, cb, Binder.getCallingPid());
826+
}
827+
// when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
828+
// SCO connections not started by the application changing the mode
829+
if (newModeOwner != null) {
830+
disconnectBluetoothSco(newModeOwner);
831+
}
832+
}
836833

837-
// automatically handle audio focus for mode changes
838-
handleFocusForCalls(mMode, mode, cb);
834+
// must be called synchronized on mSetModeDeathHandlers
835+
// setModeInt() returns a non null IBInder if the audio mode was successfully set to
836+
// any mode other than NORMAL.
837+
IBinder setModeInt(int mode, IBinder cb, int pid) {
838+
IBinder newModeOwner = null;
839+
if (cb == null) {
840+
Log.e(TAG, "setModeInt() called with null binder");
841+
return newModeOwner;
842+
}
839843

840-
if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) {
841-
mMode = mode;
844+
SetModeDeathHandler hdlr = null;
845+
Iterator iter = mSetModeDeathHandlers.iterator();
846+
while (iter.hasNext()) {
847+
SetModeDeathHandler h = (SetModeDeathHandler)iter.next();
848+
if (h.getPid() == pid) {
849+
hdlr = h;
850+
// Remove from client list so that it is re-inserted at top of list
851+
iter.remove();
852+
hdlr.getBinder().unlinkToDeath(hdlr, 0);
853+
break;
854+
}
855+
}
856+
int status = AudioSystem.AUDIO_STATUS_OK;
857+
do {
858+
if (mode == AudioSystem.MODE_NORMAL) {
859+
// get new mode from client at top the list if any
860+
if (!mSetModeDeathHandlers.isEmpty()) {
861+
hdlr = mSetModeDeathHandlers.get(0);
862+
cb = hdlr.getBinder();
863+
mode = hdlr.getMode();
864+
}
865+
} else {
866+
if (hdlr == null) {
867+
hdlr = new SetModeDeathHandler(cb, pid);
868+
}
869+
// Register for client death notification
870+
try {
871+
cb.linkToDeath(hdlr, 0);
872+
} catch (RemoteException e) {
873+
// Client has died!
874+
Log.w(TAG, "setMode() could not link to "+cb+" binder death");
875+
}
842876

843-
synchronized(mSetModeDeathHandlers) {
844-
SetModeDeathHandler hdlr = null;
845-
Iterator iter = mSetModeDeathHandlers.iterator();
846-
while (iter.hasNext()) {
847-
SetModeDeathHandler h = (SetModeDeathHandler)iter.next();
848-
if (h.getBinder() == cb) {
849-
hdlr = h;
850-
// Remove from client list so that it is re-inserted at top of list
851-
iter.remove();
852-
break;
853-
}
854-
}
855-
if (hdlr == null) {
856-
hdlr = new SetModeDeathHandler(cb);
857-
// cb is null when setMode() is called by AudioService constructor
858-
if (cb != null) {
859-
// Register for client death notification
860-
try {
861-
cb.linkToDeath(hdlr, 0);
862-
} catch (RemoteException e) {
863-
// Client has died!
864-
Log.w(TAG, "setMode() could not link to "+cb+" binder death");
865-
}
866-
}
867-
}
868-
// Last client to call setMode() is always at top of client list
869-
// as required by SetModeDeathHandler.binderDied()
870-
mSetModeDeathHandlers.add(0, hdlr);
871-
hdlr.setMode(mode);
872-
}
877+
// Last client to call setMode() is always at top of client list
878+
// as required by SetModeDeathHandler.binderDied()
879+
mSetModeDeathHandlers.add(0, hdlr);
880+
hdlr.setMode(mode);
881+
}
873882

874-
// when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
875-
// SCO connections not started by the application changing the mode
876-
if (mode != AudioSystem.MODE_NORMAL) {
877-
disconnectBluetoothSco(cb);
883+
if (mode != mMode) {
884+
status = AudioSystem.setPhoneState(mode);
885+
if (status == AudioSystem.AUDIO_STATUS_OK) {
886+
// automatically handle audio focus for mode changes
887+
handleFocusForCalls(mMode, mode, cb);
888+
mMode = mode;
889+
} else {
890+
if (hdlr != null) {
891+
mSetModeDeathHandlers.remove(hdlr);
892+
cb.unlinkToDeath(hdlr, 0);
878893
}
894+
// force reading new top of mSetModeDeathHandlers stack
895+
mode = AudioSystem.MODE_NORMAL;
879896
}
897+
} else {
898+
status = AudioSystem.AUDIO_STATUS_OK;
899+
}
900+
} while (status != AudioSystem.AUDIO_STATUS_OK && !mSetModeDeathHandlers.isEmpty());
901+
902+
if (status == AudioSystem.AUDIO_STATUS_OK) {
903+
if (mode != AudioSystem.MODE_NORMAL) {
904+
newModeOwner = cb;
880905
}
881906
int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
882907
int index = mStreamStates[STREAM_VOLUME_ALIAS[streamType]].mIndex;
883908
setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, true, false);
884909
}
910+
return newModeOwner;
885911
}
886912

887913
/** pre-condition: oldMode != newMode */
@@ -1345,28 +1371,30 @@ private void requestScoState(int state) {
13451371
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
13461372
// Accept SCO audio activation only in NORMAL audio mode or if the mode is
13471373
// currently controlled by the same client process.
1348-
if ((AudioService.this.mMode == AudioSystem.MODE_NORMAL ||
1349-
mSetModeDeathHandlers.get(0).getPid() == mCreatorPid) &&
1350-
(mScoAudioState == SCO_STATE_INACTIVE ||
1351-
mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
1352-
if (mScoAudioState == SCO_STATE_INACTIVE) {
1353-
if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
1354-
if (mBluetoothHeadset.startScoUsingVirtualVoiceCall(
1355-
mBluetoothHeadsetDevice)) {
1356-
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
1357-
} else {
1358-
broadcastScoConnectionState(
1359-
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1374+
synchronized(mSetModeDeathHandlers) {
1375+
if ((mSetModeDeathHandlers.isEmpty() ||
1376+
mSetModeDeathHandlers.get(0).getPid() == mCreatorPid) &&
1377+
(mScoAudioState == SCO_STATE_INACTIVE ||
1378+
mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
1379+
if (mScoAudioState == SCO_STATE_INACTIVE) {
1380+
if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
1381+
if (mBluetoothHeadset.startScoUsingVirtualVoiceCall(
1382+
mBluetoothHeadsetDevice)) {
1383+
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
1384+
} else {
1385+
broadcastScoConnectionState(
1386+
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1387+
}
1388+
} else if (getBluetoothHeadset()) {
1389+
mScoAudioState = SCO_STATE_ACTIVATE_REQ;
13601390
}
1361-
} else if (getBluetoothHeadset()) {
1362-
mScoAudioState = SCO_STATE_ACTIVATE_REQ;
1391+
} else {
1392+
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
1393+
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
13631394
}
13641395
} else {
1365-
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
1366-
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
1396+
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
13671397
}
1368-
} else {
1369-
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
13701398
}
13711399
} else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED &&
13721400
(mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||

0 commit comments

Comments
 (0)