Loading media/java/android/media/AudioService.java +132 −54 Original line number Diff line number Diff line Loading @@ -264,6 +264,17 @@ public class AudioService extends IAudioService.Stub { // Bluetooth headset device private BluetoothDevice mBluetoothHeadsetDevice; // Indicate if SCO audio connection is currently active and if the initiator is // audio service (internal) or bluetooth headset (external) private int mScoAudioState; // SCO audio state is not active private static final int SCO_STATE_INACTIVE = 0; // SCO audio state is active or starting due to a local request to start a virtual call private static final int SCO_STATE_ACTIVE_INTERNAL = 1; // SCO audio state is active due to an action in BT handsfree (either voice recognition or // in call audio) private static final int SCO_STATE_ACTIVE_EXTERNAL = 2; /////////////////////////////////////////////////////////////////////////// // Construction /////////////////////////////////////////////////////////////////////////// Loading Loading @@ -315,6 +326,7 @@ public class AudioService extends IAudioService.Stub { intentFilter.addAction(Intent.ACTION_USB_ANLG_HEADSET_PLUG); intentFilter.addAction(Intent.ACTION_USB_DGTL_HEADSET_PLUG); intentFilter.addAction(Intent.ACTION_HDMI_AUDIO_PLUG); intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED); context.registerReceiver(mReceiver, intentFilter); // Register for media button intent broadcasts. Loading Loading @@ -692,6 +704,19 @@ public class AudioService extends IAudioService.Stub { if (AudioService.this.mMode != mode) { if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) { AudioService.this.mMode = mode; if (mode != AudioSystem.MODE_NORMAL) { synchronized(mScoClients) { checkScoAudioState(); if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) { mBluetoothHeadset.stopVoiceRecognition( mBluetoothHeadsetDevice); mBluetoothHeadset.stopVirtualVoiceCall( mBluetoothHeadsetDevice); } else { clearAllScoClients(mCb, true); } } } } } } Loading Loading @@ -761,8 +786,18 @@ public class AudioService extends IAudioService.Stub { hdlr.setMode(mode); } // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all // SCO connections not started by the application changing the mode if (mode != AudioSystem.MODE_NORMAL) { clearAllScoClients(); synchronized(mScoClients) { checkScoAudioState(); if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) { mBluetoothHeadset.stopVoiceRecognition(mBluetoothHeadsetDevice); mBluetoothHeadset.stopVirtualVoiceCall(mBluetoothHeadsetDevice); } else { clearAllScoClients(cb, true); } } } } } Loading Loading @@ -1077,18 +1112,39 @@ public class AudioService extends IAudioService.Stub { } private void requestScoState(int state) { if (mBluetoothHeadset == null) { return; } checkScoAudioState(); if (totalCount() == 0 && mBluetoothHeadsetDevice != null && AudioService.this.mMode == AudioSystem.MODE_NORMAL) { if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) { mBluetoothHeadset.startVoiceRecognition(mBluetoothHeadsetDevice); } else { mBluetoothHeadset.stopVoiceRecognition(mBluetoothHeadsetDevice); mBluetoothHeadsetDevice != null) { // Accept SCO audio activation only in NORMAL audio mode or if the mode is // currently controlled by the same client. if ((AudioService.this.mMode == AudioSystem.MODE_NORMAL || mSetModeDeathHandlers.get(0).getBinder() == mCb) && state == BluetoothHeadset.STATE_AUDIO_CONNECTED && mScoAudioState == SCO_STATE_INACTIVE) { mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; mBluetoothHeadset.startVirtualVoiceCall(mBluetoothHeadsetDevice); } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED && mScoAudioState == SCO_STATE_ACTIVE_INTERNAL){ mBluetoothHeadset.stopVirtualVoiceCall(mBluetoothHeadsetDevice); } } } } private void checkScoAudioState() { if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null && mScoAudioState != SCO_STATE_ACTIVE_INTERNAL && mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice) != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; } } public ScoClient getScoClient(IBinder cb) { synchronized(mScoClients) { ScoClient client; Loading @@ -1104,11 +1160,12 @@ public class AudioService extends IAudioService.Stub { } } public void clearAllScoClients() { public void clearAllScoClients(IBinder exceptBinder, boolean stopSco) { synchronized(mScoClients) { int size = mScoClients.size(); for (int i = 0; i < size; i++) { mScoClients.get(i).clearCount(false); if (mScoClients.get(i).getBinder() != exceptBinder) mScoClients.get(i).clearCount(stopSco); } } } Loading @@ -1116,6 +1173,7 @@ public class AudioService extends IAudioService.Stub { private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener = new BluetoothProfile.ServiceListener() { public void onServiceConnected(int profile, BluetoothProfile proxy) { synchronized (mScoClients) { mBluetoothHeadset = (BluetoothHeadset) proxy; List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices(); if (deviceList.size() > 0) { Loading @@ -1124,16 +1182,20 @@ public class AudioService extends IAudioService.Stub { mBluetoothHeadsetDevice = null; } } } public void onServiceDisconnected(int profile) { synchronized (mScoClients) { if (mBluetoothHeadset != null) { List<BluetoothDevice> devices = mBluetoothHeadset.getConnectedDevices(); if (devices.size() == 0) { mBluetoothHeadsetDevice = null; clearAllScoClients(); clearAllScoClients(null, false); mScoAudioState = SCO_STATE_INACTIVE; } mBluetoothHeadset = null; } } } }; /////////////////////////////////////////////////////////////////////////// Loading Loading @@ -1892,13 +1954,15 @@ public class AudioService extends IAudioService.Stub { boolean isConnected = (mConnectedDevices.containsKey(device) && mConnectedDevices.get(device).equals(address)); synchronized (mScoClients) { if (isConnected && state != BluetoothProfile.STATE_CONNECTED) { AudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_UNAVAILABLE, address); mConnectedDevices.remove(device); mBluetoothHeadsetDevice = null; clearAllScoClients(); clearAllScoClients(null, false); mScoAudioState = SCO_STATE_INACTIVE; } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) { AudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_AVAILABLE, Loading @@ -1906,6 +1970,7 @@ public class AudioService extends IAudioService.Stub { mConnectedDevices.put(new Integer(device), address); mBluetoothHeadsetDevice = btDevice; } } } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) { int state = intent.getIntExtra("state", 0); int microphone = intent.getIntExtra("microphone", 0); Loading Loading @@ -1965,31 +2030,44 @@ public class AudioService extends IAudioService.Stub { mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET), ""); } } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) { int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); boolean broadcast = false; int audioState = AudioManager.SCO_AUDIO_STATE_ERROR; synchronized (mScoClients) { if (!mScoClients.isEmpty()) { switch (state) { int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); if (!mScoClients.isEmpty() && mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) { broadcast = true; } switch (btState) { case BluetoothHeadset.STATE_AUDIO_CONNECTED: state = AudioManager.SCO_AUDIO_STATE_CONNECTED; audioState = AudioManager.SCO_AUDIO_STATE_CONNECTED; if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL) { mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; } break; case BluetoothHeadset.STATE_AUDIO_DISCONNECTED: state = AudioManager.SCO_AUDIO_STATE_DISCONNECTED; audioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED; mScoAudioState = SCO_STATE_INACTIVE; break; case BluetoothHeadset.STATE_AUDIO_CONNECTING: // Todo(): Handle this, ignore for now as a public // API will break. break; if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL) { mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; } default: state = AudioManager.SCO_AUDIO_STATE_ERROR; // do not broadcast CONNECTING or invalid state broadcast = false; break; } if (state != AudioManager.SCO_AUDIO_STATE_ERROR) { } if (broadcast) { Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED); newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state); newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, audioState); mContext.sendStickyBroadcast(newIntent); } } } } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) { Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED); newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, AudioManager.SCO_AUDIO_STATE_DISCONNECTED); mContext.sendStickyBroadcast(newIntent); } } } Loading Loading
media/java/android/media/AudioService.java +132 −54 Original line number Diff line number Diff line Loading @@ -264,6 +264,17 @@ public class AudioService extends IAudioService.Stub { // Bluetooth headset device private BluetoothDevice mBluetoothHeadsetDevice; // Indicate if SCO audio connection is currently active and if the initiator is // audio service (internal) or bluetooth headset (external) private int mScoAudioState; // SCO audio state is not active private static final int SCO_STATE_INACTIVE = 0; // SCO audio state is active or starting due to a local request to start a virtual call private static final int SCO_STATE_ACTIVE_INTERNAL = 1; // SCO audio state is active due to an action in BT handsfree (either voice recognition or // in call audio) private static final int SCO_STATE_ACTIVE_EXTERNAL = 2; /////////////////////////////////////////////////////////////////////////// // Construction /////////////////////////////////////////////////////////////////////////// Loading Loading @@ -315,6 +326,7 @@ public class AudioService extends IAudioService.Stub { intentFilter.addAction(Intent.ACTION_USB_ANLG_HEADSET_PLUG); intentFilter.addAction(Intent.ACTION_USB_DGTL_HEADSET_PLUG); intentFilter.addAction(Intent.ACTION_HDMI_AUDIO_PLUG); intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED); context.registerReceiver(mReceiver, intentFilter); // Register for media button intent broadcasts. Loading Loading @@ -692,6 +704,19 @@ public class AudioService extends IAudioService.Stub { if (AudioService.this.mMode != mode) { if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) { AudioService.this.mMode = mode; if (mode != AudioSystem.MODE_NORMAL) { synchronized(mScoClients) { checkScoAudioState(); if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) { mBluetoothHeadset.stopVoiceRecognition( mBluetoothHeadsetDevice); mBluetoothHeadset.stopVirtualVoiceCall( mBluetoothHeadsetDevice); } else { clearAllScoClients(mCb, true); } } } } } } Loading Loading @@ -761,8 +786,18 @@ public class AudioService extends IAudioService.Stub { hdlr.setMode(mode); } // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all // SCO connections not started by the application changing the mode if (mode != AudioSystem.MODE_NORMAL) { clearAllScoClients(); synchronized(mScoClients) { checkScoAudioState(); if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) { mBluetoothHeadset.stopVoiceRecognition(mBluetoothHeadsetDevice); mBluetoothHeadset.stopVirtualVoiceCall(mBluetoothHeadsetDevice); } else { clearAllScoClients(cb, true); } } } } } Loading Loading @@ -1077,18 +1112,39 @@ public class AudioService extends IAudioService.Stub { } private void requestScoState(int state) { if (mBluetoothHeadset == null) { return; } checkScoAudioState(); if (totalCount() == 0 && mBluetoothHeadsetDevice != null && AudioService.this.mMode == AudioSystem.MODE_NORMAL) { if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) { mBluetoothHeadset.startVoiceRecognition(mBluetoothHeadsetDevice); } else { mBluetoothHeadset.stopVoiceRecognition(mBluetoothHeadsetDevice); mBluetoothHeadsetDevice != null) { // Accept SCO audio activation only in NORMAL audio mode or if the mode is // currently controlled by the same client. if ((AudioService.this.mMode == AudioSystem.MODE_NORMAL || mSetModeDeathHandlers.get(0).getBinder() == mCb) && state == BluetoothHeadset.STATE_AUDIO_CONNECTED && mScoAudioState == SCO_STATE_INACTIVE) { mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; mBluetoothHeadset.startVirtualVoiceCall(mBluetoothHeadsetDevice); } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED && mScoAudioState == SCO_STATE_ACTIVE_INTERNAL){ mBluetoothHeadset.stopVirtualVoiceCall(mBluetoothHeadsetDevice); } } } } private void checkScoAudioState() { if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null && mScoAudioState != SCO_STATE_ACTIVE_INTERNAL && mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice) != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; } } public ScoClient getScoClient(IBinder cb) { synchronized(mScoClients) { ScoClient client; Loading @@ -1104,11 +1160,12 @@ public class AudioService extends IAudioService.Stub { } } public void clearAllScoClients() { public void clearAllScoClients(IBinder exceptBinder, boolean stopSco) { synchronized(mScoClients) { int size = mScoClients.size(); for (int i = 0; i < size; i++) { mScoClients.get(i).clearCount(false); if (mScoClients.get(i).getBinder() != exceptBinder) mScoClients.get(i).clearCount(stopSco); } } } Loading @@ -1116,6 +1173,7 @@ public class AudioService extends IAudioService.Stub { private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener = new BluetoothProfile.ServiceListener() { public void onServiceConnected(int profile, BluetoothProfile proxy) { synchronized (mScoClients) { mBluetoothHeadset = (BluetoothHeadset) proxy; List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices(); if (deviceList.size() > 0) { Loading @@ -1124,16 +1182,20 @@ public class AudioService extends IAudioService.Stub { mBluetoothHeadsetDevice = null; } } } public void onServiceDisconnected(int profile) { synchronized (mScoClients) { if (mBluetoothHeadset != null) { List<BluetoothDevice> devices = mBluetoothHeadset.getConnectedDevices(); if (devices.size() == 0) { mBluetoothHeadsetDevice = null; clearAllScoClients(); clearAllScoClients(null, false); mScoAudioState = SCO_STATE_INACTIVE; } mBluetoothHeadset = null; } } } }; /////////////////////////////////////////////////////////////////////////// Loading Loading @@ -1892,13 +1954,15 @@ public class AudioService extends IAudioService.Stub { boolean isConnected = (mConnectedDevices.containsKey(device) && mConnectedDevices.get(device).equals(address)); synchronized (mScoClients) { if (isConnected && state != BluetoothProfile.STATE_CONNECTED) { AudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_UNAVAILABLE, address); mConnectedDevices.remove(device); mBluetoothHeadsetDevice = null; clearAllScoClients(); clearAllScoClients(null, false); mScoAudioState = SCO_STATE_INACTIVE; } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) { AudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_AVAILABLE, Loading @@ -1906,6 +1970,7 @@ public class AudioService extends IAudioService.Stub { mConnectedDevices.put(new Integer(device), address); mBluetoothHeadsetDevice = btDevice; } } } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) { int state = intent.getIntExtra("state", 0); int microphone = intent.getIntExtra("microphone", 0); Loading Loading @@ -1965,31 +2030,44 @@ public class AudioService extends IAudioService.Stub { mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET), ""); } } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) { int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); boolean broadcast = false; int audioState = AudioManager.SCO_AUDIO_STATE_ERROR; synchronized (mScoClients) { if (!mScoClients.isEmpty()) { switch (state) { int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); if (!mScoClients.isEmpty() && mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) { broadcast = true; } switch (btState) { case BluetoothHeadset.STATE_AUDIO_CONNECTED: state = AudioManager.SCO_AUDIO_STATE_CONNECTED; audioState = AudioManager.SCO_AUDIO_STATE_CONNECTED; if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL) { mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; } break; case BluetoothHeadset.STATE_AUDIO_DISCONNECTED: state = AudioManager.SCO_AUDIO_STATE_DISCONNECTED; audioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED; mScoAudioState = SCO_STATE_INACTIVE; break; case BluetoothHeadset.STATE_AUDIO_CONNECTING: // Todo(): Handle this, ignore for now as a public // API will break. break; if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL) { mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; } default: state = AudioManager.SCO_AUDIO_STATE_ERROR; // do not broadcast CONNECTING or invalid state broadcast = false; break; } if (state != AudioManager.SCO_AUDIO_STATE_ERROR) { } if (broadcast) { Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED); newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state); newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, audioState); mContext.sendStickyBroadcast(newIntent); } } } } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) { Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED); newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, AudioManager.SCO_AUDIO_STATE_DISCONNECTED); mContext.sendStickyBroadcast(newIntent); } } } Loading