Merge "Bluetooth: Fix HFP SCO logic and documentation" into pi-dev
am: 1ae21127da
Change-Id: I9b4fcac73a283aac1794e13cce464728612fd0dc
This commit is contained in:
committed by
android-build-merger
commit
e3e55b865b
@@ -571,8 +571,8 @@ Landroid/bluetooth/BluetoothHeadset;->connectAudio()Z
|
||||
Landroid/bluetooth/BluetoothHeadset;->disconnectAudio()Z
|
||||
Landroid/bluetooth/BluetoothHeadset;->getActiveDevice()Landroid/bluetooth/BluetoothDevice;
|
||||
Landroid/bluetooth/BluetoothHeadset;->setActiveDevice(Landroid/bluetooth/BluetoothDevice;)Z
|
||||
Landroid/bluetooth/BluetoothHeadset;->startScoUsingVirtualVoiceCall(Landroid/bluetooth/BluetoothDevice;)Z
|
||||
Landroid/bluetooth/BluetoothHeadset;->stopScoUsingVirtualVoiceCall(Landroid/bluetooth/BluetoothDevice;)Z
|
||||
Landroid/bluetooth/BluetoothHeadset;->startScoUsingVirtualVoiceCall()Z
|
||||
Landroid/bluetooth/BluetoothHeadset;->stopScoUsingVirtualVoiceCall()Z
|
||||
Landroid/bluetooth/BluetoothHearingAid;->ACTION_ACTIVE_DEVICE_CHANGED:Ljava/lang/String;
|
||||
Landroid/bluetooth/BluetoothHearingAid;->getActiveDevices()Ljava/util/List;
|
||||
Landroid/bluetooth/BluetoothHearingAid;->setActiveDevice(Landroid/bluetooth/BluetoothDevice;)Z
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.bluetooth;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.annotation.SdkConstant;
|
||||
@@ -633,8 +634,9 @@ public final class BluetoothHeadset implements BluetoothProfile {
|
||||
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
|
||||
*
|
||||
* @param device Bluetooth headset
|
||||
* @return false if there is no headset connected of if the connected headset doesn't support
|
||||
* voice recognition or on error, true otherwise
|
||||
* @return false if there is no headset connected, or the connected headset doesn't support
|
||||
* voice recognition, or voice recognition is already started, or audio channel is occupied,
|
||||
* or on error, true otherwise
|
||||
*/
|
||||
public boolean startVoiceRecognition(BluetoothDevice device) {
|
||||
if (DBG) log("startVoiceRecognition()");
|
||||
@@ -654,10 +656,15 @@ public final class BluetoothHeadset implements BluetoothProfile {
|
||||
* Stop Bluetooth Voice Recognition mode, and shut down the
|
||||
* Bluetooth audio path.
|
||||
*
|
||||
* <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
|
||||
* If this function returns true, this intent will be broadcasted with
|
||||
* {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}.
|
||||
*
|
||||
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
|
||||
*
|
||||
* @param device Bluetooth headset
|
||||
* @return false if there is no headset connected or on error, true otherwise
|
||||
* @return false if there is no headset connected, or voice recognition has not started,
|
||||
* or voice recognition has ended on this headset, or on error, true otherwise
|
||||
*/
|
||||
public boolean stopVoiceRecognition(BluetoothDevice device) {
|
||||
if (DBG) log("stopVoiceRecognition()");
|
||||
@@ -798,11 +805,12 @@ public final class BluetoothHeadset implements BluetoothProfile {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Bluetooth SCO audio is connected.
|
||||
* Check if at least one headset's SCO audio is connected or connecting
|
||||
*
|
||||
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
|
||||
*
|
||||
* @return true if SCO is connected, false otherwise or on error
|
||||
* @return true if at least one device's SCO audio is connected or connecting, false otherwise
|
||||
* or on error
|
||||
* @hide
|
||||
*/
|
||||
public boolean isAudioOn() {
|
||||
@@ -821,11 +829,21 @@ public final class BluetoothHeadset implements BluetoothProfile {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates a connection of headset audio.
|
||||
* It setup SCO channel with remote connected headset device.
|
||||
* Initiates a connection of headset audio to the current active device
|
||||
*
|
||||
* @return true if successful false if there was some error such as there is no connected
|
||||
* headset
|
||||
* <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
|
||||
* If this function returns true, this intent will be broadcasted with
|
||||
* {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}.
|
||||
*
|
||||
* <p> {@link #EXTRA_STATE} will transition from
|
||||
* {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when
|
||||
* audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED}
|
||||
* in case of failure to establish the audio connection.
|
||||
*
|
||||
* Note that this intent will not be sent if {@link BluetoothHeadset#isAudioOn()} is true
|
||||
* before calling this method
|
||||
*
|
||||
* @return false if there was some error such as there is no active headset
|
||||
* @hide
|
||||
*/
|
||||
public boolean connectAudio() {
|
||||
@@ -844,11 +862,14 @@ public final class BluetoothHeadset implements BluetoothProfile {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates a disconnection of headset audio.
|
||||
* It tears down the SCO channel from remote headset device.
|
||||
* Initiates a disconnection of HFP SCO audio.
|
||||
* Tear down voice recognition or virtual voice call if any.
|
||||
*
|
||||
* @return true if successful false if there was some error such as there is no connected SCO
|
||||
* channel
|
||||
* <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
|
||||
* If this function returns true, this intent will be broadcasted with
|
||||
* {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}.
|
||||
*
|
||||
* @return false if audio is not connected, or on error, true otherwise
|
||||
* @hide
|
||||
*/
|
||||
public boolean disconnectAudio() {
|
||||
@@ -867,22 +888,33 @@ public final class BluetoothHeadset implements BluetoothProfile {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates a SCO channel connection with the headset (if connected).
|
||||
* Also initiates a virtual voice call for Handsfree devices as many devices
|
||||
* do not accept SCO audio without a call.
|
||||
* This API allows the handsfree device to be used for routing non-cellular
|
||||
* call audio.
|
||||
* Initiates a SCO channel connection as a virtual voice call to the current active device
|
||||
* Active handsfree device will be notified of incoming call and connected call.
|
||||
*
|
||||
* @param device Remote Bluetooth Device
|
||||
* @return true if successful, false if there was some error.
|
||||
* <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
|
||||
* If this function returns true, this intent will be broadcasted with
|
||||
* {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_CONNECTING}.
|
||||
*
|
||||
* <p> {@link #EXTRA_STATE} will transition from
|
||||
* {@link #STATE_AUDIO_CONNECTING} to {@link #STATE_AUDIO_CONNECTED} when
|
||||
* audio connection is established and to {@link #STATE_AUDIO_DISCONNECTED}
|
||||
* in case of failure to establish the audio connection.
|
||||
*
|
||||
* @return true if successful, false if one of the following case applies
|
||||
* - SCO audio is not idle (connecting or connected)
|
||||
* - virtual call has already started
|
||||
* - there is no active device
|
||||
* - a Telecom managed call is going on
|
||||
* - binder is dead or Bluetooth is disabled or other error
|
||||
* @hide
|
||||
*/
|
||||
public boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) {
|
||||
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
|
||||
public boolean startScoUsingVirtualVoiceCall() {
|
||||
if (DBG) log("startScoUsingVirtualVoiceCall()");
|
||||
final IBluetoothHeadset service = mService;
|
||||
if (service != null && isEnabled() && isValidDevice(device)) {
|
||||
if (service != null && isEnabled()) {
|
||||
try {
|
||||
return service.startScoUsingVirtualVoiceCall(device);
|
||||
return service.startScoUsingVirtualVoiceCall();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, e.toString());
|
||||
}
|
||||
@@ -894,19 +926,24 @@ public final class BluetoothHeadset implements BluetoothProfile {
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminates an ongoing SCO connection and the associated virtual
|
||||
* call.
|
||||
* Terminates an ongoing SCO connection and the associated virtual call.
|
||||
*
|
||||
* @param device Remote Bluetooth Device
|
||||
* @return true if successful, false if there was some error.
|
||||
* <p> Users can listen to {@link #ACTION_AUDIO_STATE_CHANGED}.
|
||||
* If this function returns true, this intent will be broadcasted with
|
||||
* {@link #EXTRA_STATE} set to {@link #STATE_AUDIO_DISCONNECTED}.
|
||||
*
|
||||
* @return true if successful, false if one of the following case applies
|
||||
* - virtual voice call is not started or has ended
|
||||
* - binder is dead or Bluetooth is disabled or other error
|
||||
* @hide
|
||||
*/
|
||||
public boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) {
|
||||
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
|
||||
public boolean stopScoUsingVirtualVoiceCall() {
|
||||
if (DBG) log("stopScoUsingVirtualVoiceCall()");
|
||||
final IBluetoothHeadset service = mService;
|
||||
if (service != null && isEnabled() && isValidDevice(device)) {
|
||||
if (service != null && isEnabled()) {
|
||||
try {
|
||||
return service.stopScoUsingVirtualVoiceCall(device);
|
||||
return service.stopScoUsingVirtualVoiceCall();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, e.toString());
|
||||
}
|
||||
|
||||
@@ -4558,8 +4558,7 @@ public class AudioManager {
|
||||
/**
|
||||
* The list of {@link AudioDeviceCallback} objects to receive add/remove notifications.
|
||||
*/
|
||||
private ArrayMap<AudioDeviceCallback, NativeEventHandlerDelegate>
|
||||
mDeviceCallbacks =
|
||||
private final ArrayMap<AudioDeviceCallback, NativeEventHandlerDelegate> mDeviceCallbacks =
|
||||
new ArrayMap<AudioDeviceCallback, NativeEventHandlerDelegate>();
|
||||
|
||||
/**
|
||||
@@ -4859,22 +4858,21 @@ public class AudioManager {
|
||||
calcListDeltas(mPreviousPorts, current_ports, GET_DEVICES_ALL);
|
||||
AudioDeviceInfo[] removed_devices =
|
||||
calcListDeltas(current_ports, mPreviousPorts, GET_DEVICES_ALL);
|
||||
|
||||
if (added_devices.length != 0 || removed_devices.length != 0) {
|
||||
synchronized (mDeviceCallbacks) {
|
||||
for (int i = 0; i < mDeviceCallbacks.size(); i++) {
|
||||
handler = mDeviceCallbacks.valueAt(i).getHandler();
|
||||
if (handler != null) {
|
||||
if (added_devices.length != 0) {
|
||||
handler.sendMessage(Message.obtain(handler,
|
||||
MSG_DEVICES_DEVICES_ADDED,
|
||||
added_devices));
|
||||
}
|
||||
if (removed_devices.length != 0) {
|
||||
handler.sendMessage(Message.obtain(handler,
|
||||
MSG_DEVICES_DEVICES_REMOVED,
|
||||
removed_devices));
|
||||
}
|
||||
if (added_devices.length != 0) {
|
||||
handler.sendMessage(Message.obtain(handler,
|
||||
MSG_DEVICES_DEVICES_ADDED,
|
||||
added_devices));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -525,6 +525,8 @@ public class AudioService extends IAudioService.Stub
|
||||
private static final int SCO_STATE_ACTIVE_INTERNAL = 3;
|
||||
// SCO audio deactivation request waiting for headset service to connect
|
||||
private static final int SCO_STATE_DEACTIVATE_REQ = 5;
|
||||
// SCO audio deactivation in progress, waiting for Bluetooth audio intent
|
||||
private static final int SCO_STATE_DEACTIVATING = 6;
|
||||
|
||||
// SCO audio state is active due to an action in BT handsfree (either voice recognition or
|
||||
// in call audio)
|
||||
@@ -2714,9 +2716,13 @@ public class AudioService extends IAudioService.Stub
|
||||
}
|
||||
|
||||
public void binderDied() {
|
||||
int oldModeOwnerPid = 0;
|
||||
int newModeOwnerPid = 0;
|
||||
synchronized(mSetModeDeathHandlers) {
|
||||
Log.w(TAG, "setMode() client died");
|
||||
if (!mSetModeDeathHandlers.isEmpty()) {
|
||||
oldModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
|
||||
}
|
||||
int index = mSetModeDeathHandlers.indexOf(this);
|
||||
if (index < 0) {
|
||||
Log.w(TAG, "unregistered setMode() client died");
|
||||
@@ -2725,8 +2731,8 @@ public class AudioService extends IAudioService.Stub
|
||||
}
|
||||
}
|
||||
// when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
|
||||
// SCO connections not started by the application changing the mode
|
||||
if (newModeOwnerPid != 0) {
|
||||
// SCO connections not started by the application changing the mode when pid changes
|
||||
if ((newModeOwnerPid != oldModeOwnerPid) && (newModeOwnerPid != 0)) {
|
||||
final long ident = Binder.clearCallingIdentity();
|
||||
disconnectBluetoothSco(newModeOwnerPid);
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
@@ -2770,17 +2776,21 @@ public class AudioService extends IAudioService.Stub
|
||||
return;
|
||||
}
|
||||
|
||||
int oldModeOwnerPid = 0;
|
||||
int newModeOwnerPid = 0;
|
||||
synchronized(mSetModeDeathHandlers) {
|
||||
if (!mSetModeDeathHandlers.isEmpty()) {
|
||||
oldModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
|
||||
}
|
||||
if (mode == AudioSystem.MODE_CURRENT) {
|
||||
mode = mMode;
|
||||
}
|
||||
newModeOwnerPid = setModeInt(mode, cb, Binder.getCallingPid(), callingPackage);
|
||||
}
|
||||
// when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
|
||||
// SCO connections not started by the application changing the mode
|
||||
if (newModeOwnerPid != 0) {
|
||||
disconnectBluetoothSco(newModeOwnerPid);
|
||||
// SCO connections not started by the application changing the mode when pid changes
|
||||
if ((newModeOwnerPid != oldModeOwnerPid) && (newModeOwnerPid != 0)) {
|
||||
disconnectBluetoothSco(newModeOwnerPid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3196,28 +3206,17 @@ public class AudioService extends IAudioService.Stub
|
||||
}
|
||||
|
||||
public void setBluetoothScoOnInt(boolean on, String eventSource) {
|
||||
if (DEBUG_DEVICES) {
|
||||
Log.d(TAG, "setBluetoothScoOnInt: " + on + " " + eventSource);
|
||||
}
|
||||
Log.i(TAG, "setBluetoothScoOnInt: " + on + " " + eventSource);
|
||||
if (on) {
|
||||
// do not accept SCO ON if SCO audio is not connected
|
||||
synchronized (mScoClients) {
|
||||
if (mBluetoothHeadset != null) {
|
||||
if (mBluetoothHeadsetDevice == null) {
|
||||
BluetoothDevice activeDevice = mBluetoothHeadset.getActiveDevice();
|
||||
if (activeDevice != null) {
|
||||
// setBtScoActiveDevice() might trigger resetBluetoothSco() which
|
||||
// will call setBluetoothScoOnInt(false, "resetBluetoothSco")
|
||||
setBtScoActiveDevice(activeDevice);
|
||||
}
|
||||
}
|
||||
if (mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
|
||||
!= BluetoothHeadset.STATE_AUDIO_CONNECTED) {
|
||||
mForcedUseForCommExt = AudioSystem.FORCE_BT_SCO;
|
||||
Log.w(TAG, "setBluetoothScoOnInt(true) failed because "
|
||||
+ mBluetoothHeadsetDevice + " is not in audio connected mode");
|
||||
return;
|
||||
}
|
||||
if ((mBluetoothHeadset != null)
|
||||
&& (mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
|
||||
!= BluetoothHeadset.STATE_AUDIO_CONNECTED)) {
|
||||
mForcedUseForCommExt = AudioSystem.FORCE_BT_SCO;
|
||||
Log.w(TAG, "setBluetoothScoOnInt(true) failed because "
|
||||
+ mBluetoothHeadsetDevice + " is not in audio connected mode");
|
||||
return;
|
||||
}
|
||||
}
|
||||
mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
|
||||
@@ -3397,9 +3396,8 @@ public class AudioService extends IAudioService.Stub
|
||||
public int totalCount() {
|
||||
synchronized(mScoClients) {
|
||||
int count = 0;
|
||||
int size = mScoClients.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
count += mScoClients.get(i).getCount();
|
||||
for (ScoClient mScoClient : mScoClients) {
|
||||
count += mScoClient.getCount();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
@@ -3407,128 +3405,161 @@ public class AudioService extends IAudioService.Stub
|
||||
|
||||
private void requestScoState(int state, int scoAudioMode) {
|
||||
checkScoAudioState();
|
||||
if (totalCount() == 0) {
|
||||
if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
|
||||
// Make sure that the state transitions to CONNECTING even if we cannot initiate
|
||||
// the connection.
|
||||
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
|
||||
// Accept SCO audio activation only in NORMAL audio mode or if the mode is
|
||||
// currently controlled by the same client process.
|
||||
synchronized(mSetModeDeathHandlers) {
|
||||
if ((mSetModeDeathHandlers.isEmpty() ||
|
||||
mSetModeDeathHandlers.get(0).getPid() == mCreatorPid) &&
|
||||
(mScoAudioState == SCO_STATE_INACTIVE ||
|
||||
mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
|
||||
if (mScoAudioState == SCO_STATE_INACTIVE) {
|
||||
mScoAudioMode = scoAudioMode;
|
||||
if (scoAudioMode == SCO_MODE_UNDEFINED) {
|
||||
if (mBluetoothHeadsetDevice != null) {
|
||||
mScoAudioMode = new Integer(Settings.Global.getInt(
|
||||
mContentResolver,
|
||||
"bluetooth_sco_channel_"+
|
||||
mBluetoothHeadsetDevice.getAddress(),
|
||||
SCO_MODE_VIRTUAL_CALL));
|
||||
if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) {
|
||||
mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
|
||||
}
|
||||
} else {
|
||||
mScoAudioMode = SCO_MODE_RAW;
|
||||
}
|
||||
}
|
||||
if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
|
||||
boolean status = false;
|
||||
if (mScoAudioMode == SCO_MODE_RAW) {
|
||||
status = mBluetoothHeadset.connectAudio();
|
||||
} else if (mScoAudioMode == SCO_MODE_VIRTUAL_CALL) {
|
||||
status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
|
||||
mBluetoothHeadsetDevice);
|
||||
} else if (mScoAudioMode == SCO_MODE_VR) {
|
||||
status = mBluetoothHeadset.startVoiceRecognition(
|
||||
mBluetoothHeadsetDevice);
|
||||
}
|
||||
|
||||
if (status) {
|
||||
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
|
||||
} else {
|
||||
broadcastScoConnectionState(
|
||||
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
||||
}
|
||||
} else if (getBluetoothHeadset()) {
|
||||
mScoAudioState = SCO_STATE_ACTIVATE_REQ;
|
||||
}
|
||||
} else {
|
||||
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
|
||||
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
|
||||
}
|
||||
} else {
|
||||
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
||||
}
|
||||
int clientCount = totalCount();
|
||||
if (clientCount != 0) {
|
||||
Log.i(TAG, "requestScoState: state=" + state + ", scoAudioMode=" + scoAudioMode
|
||||
+ ", clientCount=" + clientCount);
|
||||
return;
|
||||
}
|
||||
if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
|
||||
// Make sure that the state transitions to CONNECTING even if we cannot initiate
|
||||
// the connection.
|
||||
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
|
||||
// Accept SCO audio activation only in NORMAL audio mode or if the mode is
|
||||
// currently controlled by the same client process.
|
||||
synchronized(mSetModeDeathHandlers) {
|
||||
int modeOwnerPid = mSetModeDeathHandlers.isEmpty()
|
||||
? 0 : mSetModeDeathHandlers.get(0).getPid();
|
||||
if (modeOwnerPid != 0 && (modeOwnerPid != mCreatorPid)) {
|
||||
Log.w(TAG, "requestScoState: audio mode is not NORMAL and modeOwnerPid "
|
||||
+ modeOwnerPid + " != creatorPid " + mCreatorPid);
|
||||
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
||||
return;
|
||||
}
|
||||
} else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED &&
|
||||
(mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
|
||||
mScoAudioState == SCO_STATE_ACTIVATE_REQ)) {
|
||||
if (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) {
|
||||
if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
|
||||
boolean status = false;
|
||||
if (mScoAudioMode == SCO_MODE_RAW) {
|
||||
status = mBluetoothHeadset.disconnectAudio();
|
||||
} else if (mScoAudioMode == SCO_MODE_VIRTUAL_CALL) {
|
||||
status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
|
||||
mBluetoothHeadsetDevice);
|
||||
} else if (mScoAudioMode == SCO_MODE_VR) {
|
||||
status = mBluetoothHeadset.stopVoiceRecognition(
|
||||
mBluetoothHeadsetDevice);
|
||||
switch (mScoAudioState) {
|
||||
case SCO_STATE_INACTIVE:
|
||||
mScoAudioMode = scoAudioMode;
|
||||
if (scoAudioMode == SCO_MODE_UNDEFINED) {
|
||||
mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
|
||||
if (mBluetoothHeadsetDevice != null) {
|
||||
mScoAudioMode = Settings.Global.getInt(mContentResolver,
|
||||
"bluetooth_sco_channel_"
|
||||
+ mBluetoothHeadsetDevice.getAddress(),
|
||||
SCO_MODE_VIRTUAL_CALL);
|
||||
if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) {
|
||||
mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mBluetoothHeadset == null) {
|
||||
if (getBluetoothHeadset()) {
|
||||
mScoAudioState = SCO_STATE_ACTIVATE_REQ;
|
||||
} else {
|
||||
Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
|
||||
+ " connection, mScoAudioMode=" + mScoAudioMode);
|
||||
broadcastScoConnectionState(
|
||||
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (mBluetoothHeadsetDevice == null) {
|
||||
Log.w(TAG, "requestScoState: no active device while connecting,"
|
||||
+ " mScoAudioMode=" + mScoAudioMode);
|
||||
broadcastScoConnectionState(
|
||||
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
||||
break;
|
||||
}
|
||||
if (connectBluetoothScoAudioHelper(mBluetoothHeadset,
|
||||
mBluetoothHeadsetDevice, mScoAudioMode)) {
|
||||
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
|
||||
} else {
|
||||
Log.w(TAG, "requestScoState: connect to " + mBluetoothHeadsetDevice
|
||||
+ " failed, mScoAudioMode=" + mScoAudioMode);
|
||||
broadcastScoConnectionState(
|
||||
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
||||
}
|
||||
break;
|
||||
case SCO_STATE_DEACTIVATING:
|
||||
mScoAudioState = SCO_STATE_ACTIVATE_REQ;
|
||||
break;
|
||||
case SCO_STATE_DEACTIVATE_REQ:
|
||||
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
|
||||
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
|
||||
break;
|
||||
default:
|
||||
Log.w(TAG, "requestScoState: failed to connect in state "
|
||||
+ mScoAudioState + ", scoAudioMode=" + scoAudioMode);
|
||||
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
||||
break;
|
||||
|
||||
if (!status) {
|
||||
}
|
||||
}
|
||||
} else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
|
||||
switch (mScoAudioState) {
|
||||
case SCO_STATE_ACTIVE_INTERNAL:
|
||||
if (mBluetoothHeadset == null) {
|
||||
if (getBluetoothHeadset()) {
|
||||
mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
|
||||
} else {
|
||||
Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
|
||||
+ " disconnection, mScoAudioMode=" + mScoAudioMode);
|
||||
mScoAudioState = SCO_STATE_INACTIVE;
|
||||
broadcastScoConnectionState(
|
||||
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
||||
}
|
||||
} else if (getBluetoothHeadset()) {
|
||||
mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (mBluetoothHeadsetDevice == null) {
|
||||
mScoAudioState = SCO_STATE_INACTIVE;
|
||||
broadcastScoConnectionState(
|
||||
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
||||
break;
|
||||
}
|
||||
if (disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
|
||||
mBluetoothHeadsetDevice, mScoAudioMode)) {
|
||||
mScoAudioState = SCO_STATE_DEACTIVATING;
|
||||
} else {
|
||||
mScoAudioState = SCO_STATE_INACTIVE;
|
||||
broadcastScoConnectionState(
|
||||
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
||||
}
|
||||
break;
|
||||
case SCO_STATE_ACTIVATE_REQ:
|
||||
mScoAudioState = SCO_STATE_INACTIVE;
|
||||
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Log.w(TAG, "requestScoState: failed to disconnect in state "
|
||||
+ mScoAudioState + ", scoAudioMode=" + scoAudioMode);
|
||||
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkScoAudioState() {
|
||||
if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null &&
|
||||
mScoAudioState == SCO_STATE_INACTIVE &&
|
||||
mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
|
||||
!= BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
|
||||
mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
|
||||
synchronized (mScoClients) {
|
||||
if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null &&
|
||||
mScoAudioState == SCO_STATE_INACTIVE &&
|
||||
mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
|
||||
!= BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
|
||||
mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private ScoClient getScoClient(IBinder cb, boolean create) {
|
||||
synchronized(mScoClients) {
|
||||
ScoClient client = null;
|
||||
int size = mScoClients.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
client = mScoClients.get(i);
|
||||
if (client.getBinder() == cb)
|
||||
return client;
|
||||
for (ScoClient existingClient : mScoClients) {
|
||||
if (existingClient.getBinder() == cb) {
|
||||
return existingClient;
|
||||
}
|
||||
}
|
||||
if (create) {
|
||||
client = new ScoClient(cb);
|
||||
mScoClients.add(client);
|
||||
ScoClient newClient = new ScoClient(cb);
|
||||
mScoClients.add(newClient);
|
||||
return newClient;
|
||||
}
|
||||
return client;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void clearAllScoClients(int exceptPid, boolean stopSco) {
|
||||
synchronized(mScoClients) {
|
||||
ScoClient savedClient = null;
|
||||
int size = mScoClients.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
ScoClient cl = mScoClients.get(i);
|
||||
for (ScoClient cl : mScoClients) {
|
||||
if (cl.getPid() != exceptPid) {
|
||||
cl.clearCount(stopSco);
|
||||
} else {
|
||||
@@ -3565,10 +3596,17 @@ public class AudioService extends IAudioService.Stub
|
||||
mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
|
||||
if (mBluetoothHeadsetDevice != null) {
|
||||
if (mBluetoothHeadset != null) {
|
||||
if (!mBluetoothHeadset.stopVoiceRecognition(
|
||||
mBluetoothHeadsetDevice)) {
|
||||
sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED,
|
||||
SENDMSG_REPLACE, 0, 0, null, 0);
|
||||
boolean status = disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
|
||||
mBluetoothHeadsetDevice, SCO_MODE_RAW)
|
||||
|| disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
|
||||
mBluetoothHeadsetDevice, SCO_MODE_VIRTUAL_CALL)
|
||||
|| disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
|
||||
mBluetoothHeadsetDevice, SCO_MODE_VR);
|
||||
if (status) {
|
||||
mScoAudioState = SCO_STATE_DEACTIVATING;
|
||||
} else {
|
||||
clearAllScoClients(exceptPid, false);
|
||||
mScoAudioState = SCO_STATE_INACTIVE;
|
||||
}
|
||||
} else if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL &&
|
||||
getBluetoothHeadset()) {
|
||||
@@ -3581,6 +3619,34 @@ public class AudioService extends IAudioService.Stub
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean disconnectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset,
|
||||
BluetoothDevice device, int scoAudioMode) {
|
||||
switch (scoAudioMode) {
|
||||
case SCO_MODE_RAW:
|
||||
return bluetoothHeadset.disconnectAudio();
|
||||
case SCO_MODE_VIRTUAL_CALL:
|
||||
return bluetoothHeadset.stopScoUsingVirtualVoiceCall();
|
||||
case SCO_MODE_VR:
|
||||
return bluetoothHeadset.stopVoiceRecognition(device);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean connectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset,
|
||||
BluetoothDevice device, int scoAudioMode) {
|
||||
switch (scoAudioMode) {
|
||||
case SCO_MODE_RAW:
|
||||
return bluetoothHeadset.connectAudio();
|
||||
case SCO_MODE_VIRTUAL_CALL:
|
||||
return bluetoothHeadset.startScoUsingVirtualVoiceCall();
|
||||
case SCO_MODE_VR:
|
||||
return bluetoothHeadset.startVoiceRecognition(device);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void resetBluetoothSco() {
|
||||
synchronized(mScoClients) {
|
||||
clearAllScoClients(0, false);
|
||||
@@ -3638,11 +3704,9 @@ public class AudioService extends IAudioService.Stub
|
||||
return result;
|
||||
}
|
||||
|
||||
void setBtScoActiveDevice(BluetoothDevice btDevice) {
|
||||
if (DEBUG_DEVICES) {
|
||||
Log.d(TAG, "setBtScoActiveDevice(" + btDevice + ")");
|
||||
}
|
||||
private void setBtScoActiveDevice(BluetoothDevice btDevice) {
|
||||
synchronized (mScoClients) {
|
||||
Log.i(TAG, "setBtScoActiveDevice: " + mBluetoothHeadsetDevice + " -> " + btDevice);
|
||||
final BluetoothDevice previousActiveDevice = mBluetoothHeadsetDevice;
|
||||
if (!Objects.equals(btDevice, previousActiveDevice)) {
|
||||
if (!handleBtScoActiveDeviceChange(previousActiveDevice, false)) {
|
||||
@@ -3722,37 +3786,36 @@ public class AudioService extends IAudioService.Stub
|
||||
boolean status = false;
|
||||
if (mBluetoothHeadsetDevice != null) {
|
||||
switch (mScoAudioState) {
|
||||
case SCO_STATE_ACTIVATE_REQ:
|
||||
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
|
||||
if (mScoAudioMode == SCO_MODE_RAW) {
|
||||
status = mBluetoothHeadset.connectAudio();
|
||||
} else if (mScoAudioMode == SCO_MODE_VIRTUAL_CALL) {
|
||||
status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
|
||||
mBluetoothHeadsetDevice);
|
||||
} else if (mScoAudioMode == SCO_MODE_VR) {
|
||||
status = mBluetoothHeadset.startVoiceRecognition(
|
||||
mBluetoothHeadsetDevice);
|
||||
}
|
||||
break;
|
||||
case SCO_STATE_DEACTIVATE_REQ:
|
||||
if (mScoAudioMode == SCO_MODE_RAW) {
|
||||
status = mBluetoothHeadset.disconnectAudio();
|
||||
} else if (mScoAudioMode == SCO_MODE_VIRTUAL_CALL) {
|
||||
status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
|
||||
mBluetoothHeadsetDevice);
|
||||
} else if (mScoAudioMode == SCO_MODE_VR) {
|
||||
status = mBluetoothHeadset.stopVoiceRecognition(
|
||||
mBluetoothHeadsetDevice);
|
||||
}
|
||||
break;
|
||||
case SCO_STATE_DEACTIVATE_EXT_REQ:
|
||||
status = mBluetoothHeadset.stopVoiceRecognition(
|
||||
mBluetoothHeadsetDevice);
|
||||
case SCO_STATE_ACTIVATE_REQ:
|
||||
status = connectBluetoothScoAudioHelper(mBluetoothHeadset,
|
||||
mBluetoothHeadsetDevice, mScoAudioMode);
|
||||
if (status) {
|
||||
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
|
||||
}
|
||||
break;
|
||||
case SCO_STATE_DEACTIVATE_REQ:
|
||||
status = disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
|
||||
mBluetoothHeadsetDevice, mScoAudioMode);
|
||||
if (status) {
|
||||
mScoAudioState = SCO_STATE_DEACTIVATING;
|
||||
}
|
||||
break;
|
||||
case SCO_STATE_DEACTIVATE_EXT_REQ:
|
||||
status = disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
|
||||
mBluetoothHeadsetDevice, SCO_MODE_RAW) ||
|
||||
disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
|
||||
mBluetoothHeadsetDevice, SCO_MODE_VIRTUAL_CALL) ||
|
||||
disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
|
||||
mBluetoothHeadsetDevice, SCO_MODE_VR);
|
||||
if (status) {
|
||||
mScoAudioState = SCO_STATE_DEACTIVATING;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!status) {
|
||||
sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED,
|
||||
SENDMSG_REPLACE, 0, 0, null, 0);
|
||||
mScoAudioState = SCO_STATE_INACTIVE;
|
||||
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6322,33 +6385,47 @@ public class AudioService extends IAudioService.Stub
|
||||
if (!mScoClients.isEmpty() &&
|
||||
(mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
|
||||
mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
|
||||
mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
|
||||
mScoAudioState == SCO_STATE_DEACTIVATE_REQ ||
|
||||
mScoAudioState == SCO_STATE_DEACTIVATING)) {
|
||||
broadcast = true;
|
||||
}
|
||||
switch (btState) {
|
||||
case BluetoothHeadset.STATE_AUDIO_CONNECTED:
|
||||
scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
|
||||
if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
|
||||
mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
|
||||
mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
|
||||
mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
|
||||
}
|
||||
break;
|
||||
case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
|
||||
scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
|
||||
mScoAudioState = SCO_STATE_INACTIVE;
|
||||
clearAllScoClients(0, false);
|
||||
break;
|
||||
case BluetoothHeadset.STATE_AUDIO_CONNECTING:
|
||||
if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
|
||||
mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
|
||||
mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
|
||||
mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
|
||||
}
|
||||
default:
|
||||
// do not broadcast CONNECTING or invalid state
|
||||
broadcast = false;
|
||||
break;
|
||||
case BluetoothHeadset.STATE_AUDIO_CONNECTED:
|
||||
scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
|
||||
if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
|
||||
mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
|
||||
mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
|
||||
mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
|
||||
}
|
||||
setBluetoothScoOn(true);
|
||||
break;
|
||||
case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
|
||||
setBluetoothScoOn(false);
|
||||
scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
|
||||
// startBluetoothSco called after stopBluetoothSco
|
||||
if (mScoAudioState == SCO_STATE_ACTIVATE_REQ) {
|
||||
if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null
|
||||
&& connectBluetoothScoAudioHelper(mBluetoothHeadset,
|
||||
mBluetoothHeadsetDevice, mScoAudioMode)) {
|
||||
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
|
||||
broadcast = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Tear down SCO if disconnected from external
|
||||
clearAllScoClients(0, mScoAudioState == SCO_STATE_ACTIVE_INTERNAL);
|
||||
mScoAudioState = SCO_STATE_INACTIVE;
|
||||
break;
|
||||
case BluetoothHeadset.STATE_AUDIO_CONNECTING:
|
||||
if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
|
||||
mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
|
||||
mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
|
||||
mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
|
||||
}
|
||||
default:
|
||||
// do not broadcast CONNECTING or invalid state
|
||||
broadcast = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (broadcast) {
|
||||
|
||||
Reference in New Issue
Block a user