Fix interaction between BluetoothEventLoop and BluetoothService.
BluetoothEventLoop primarily handles event notifications from Bluez. It should know innards of Bonding and especially BondState. BondState class and BluetoothService call into each other. When BluetoothEvent loop gets the handle to BondState it leads to deadlocks and ANRs. Change-Id: I785c57f6246c1288350d26d4acb87d879b27a5f9
This commit is contained in:
@@ -52,22 +52,14 @@ class BluetoothEventLoop {
|
||||
private final BluetoothAdapter mAdapter;
|
||||
private final Context mContext;
|
||||
|
||||
private static final int EVENT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 1;
|
||||
private static final int EVENT_RESTART_BLUETOOTH = 2;
|
||||
private static final int EVENT_PAIRING_CONSENT_DELAYED_ACCEPT = 3;
|
||||
private static final int EVENT_AGENT_CANCEL = 4;
|
||||
private static final int EVENT_RESTART_BLUETOOTH = 1;
|
||||
private static final int EVENT_PAIRING_CONSENT_DELAYED_ACCEPT = 2;
|
||||
private static final int EVENT_AGENT_CANCEL = 3;
|
||||
|
||||
private static final int CREATE_DEVICE_ALREADY_EXISTS = 1;
|
||||
private static final int CREATE_DEVICE_SUCCESS = 0;
|
||||
private static final int CREATE_DEVICE_FAILED = -1;
|
||||
|
||||
// The time (in millisecs) to delay the pairing attempt after the first
|
||||
// auto pairing attempt fails. We use an exponential delay with
|
||||
// INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the initial value and
|
||||
// MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the max value.
|
||||
private static final long INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 3000;
|
||||
private static final long MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 12000;
|
||||
|
||||
private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
|
||||
private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
|
||||
|
||||
@@ -76,13 +68,6 @@ class BluetoothEventLoop {
|
||||
public void handleMessage(Message msg) {
|
||||
String address = null;
|
||||
switch (msg.what) {
|
||||
case EVENT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY:
|
||||
address = (String)msg.obj;
|
||||
if (address != null) {
|
||||
mBluetoothService.createBond(address);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case EVENT_RESTART_BLUETOOTH:
|
||||
mBluetoothService.restart();
|
||||
break;
|
||||
@@ -95,8 +80,7 @@ class BluetoothEventLoop {
|
||||
case EVENT_AGENT_CANCEL:
|
||||
// Set the Bond State to BOND_NONE.
|
||||
// We always have only 1 device in BONDING state.
|
||||
String[] devices =
|
||||
mBluetoothService.getBondState().listInState(BluetoothDevice.BOND_BONDING);
|
||||
String[] devices = mBluetoothService.listInState(BluetoothDevice.BOND_BONDING);
|
||||
if (devices.length == 0) {
|
||||
break;
|
||||
} else if (devices.length > 1) {
|
||||
@@ -104,7 +88,7 @@ class BluetoothEventLoop {
|
||||
break;
|
||||
}
|
||||
address = devices[0];
|
||||
mBluetoothService.getBondState().setBondState(address,
|
||||
mBluetoothService.setBondState(address,
|
||||
BluetoothDevice.BOND_NONE,
|
||||
BluetoothDevice.UNBOND_REASON_REMOTE_AUTH_CANCELED);
|
||||
break;
|
||||
@@ -115,7 +99,7 @@ class BluetoothEventLoop {
|
||||
static { classInitNative(); }
|
||||
private static native void classInitNative();
|
||||
|
||||
/* pacakge */ BluetoothEventLoop(Context context, BluetoothAdapter adapter,
|
||||
/* package */ BluetoothEventLoop(Context context, BluetoothAdapter adapter,
|
||||
BluetoothService bluetoothService) {
|
||||
mBluetoothService = bluetoothService;
|
||||
mContext = context;
|
||||
@@ -209,55 +193,7 @@ class BluetoothEventLoop {
|
||||
|
||||
private void onCreatePairedDeviceResult(String address, int result) {
|
||||
address = address.toUpperCase();
|
||||
if (result == BluetoothDevice.BOND_SUCCESS) {
|
||||
mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDED);
|
||||
if (mBluetoothService.getBondState().isAutoPairingAttemptsInProgress(address)) {
|
||||
mBluetoothService.getBondState().clearPinAttempts(address);
|
||||
}
|
||||
} else if (result == BluetoothDevice.UNBOND_REASON_AUTH_FAILED &&
|
||||
mBluetoothService.getBondState().getAttempt(address) == 1) {
|
||||
mBluetoothService.getBondState().addAutoPairingFailure(address);
|
||||
pairingAttempt(address, result);
|
||||
} else if (result == BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN &&
|
||||
mBluetoothService.getBondState().isAutoPairingAttemptsInProgress(address)) {
|
||||
pairingAttempt(address, result);
|
||||
} else {
|
||||
mBluetoothService.getBondState().setBondState(address,
|
||||
BluetoothDevice.BOND_NONE, result);
|
||||
if (mBluetoothService.getBondState().isAutoPairingAttemptsInProgress(address)) {
|
||||
mBluetoothService.getBondState().clearPinAttempts(address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void pairingAttempt(String address, int result) {
|
||||
// This happens when our initial guess of "0000" as the pass key
|
||||
// fails. Try to create the bond again and display the pin dialog
|
||||
// to the user. Use back-off while posting the delayed
|
||||
// message. The initial value is
|
||||
// INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY and the max value is
|
||||
// MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY. If the max value is
|
||||
// reached, display an error to the user.
|
||||
int attempt = mBluetoothService.getBondState().getAttempt(address);
|
||||
if (attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY >
|
||||
MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY) {
|
||||
mBluetoothService.getBondState().clearPinAttempts(address);
|
||||
mBluetoothService.getBondState().setBondState(address,
|
||||
BluetoothDevice.BOND_NONE, result);
|
||||
return;
|
||||
}
|
||||
|
||||
Message message = mHandler.obtainMessage(EVENT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY);
|
||||
message.obj = address;
|
||||
boolean postResult = mHandler.sendMessageDelayed(message,
|
||||
attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY);
|
||||
if (!postResult) {
|
||||
mBluetoothService.getBondState().clearPinAttempts(address);
|
||||
mBluetoothService.getBondState().setBondState(address,
|
||||
BluetoothDevice.BOND_NONE, result);
|
||||
return;
|
||||
}
|
||||
mBluetoothService.getBondState().attempt(address);
|
||||
mBluetoothService.onCreatePairedDeviceResult(address, result);
|
||||
}
|
||||
|
||||
private void onDeviceCreated(String deviceObjectPath) {
|
||||
@@ -275,8 +211,8 @@ class BluetoothEventLoop {
|
||||
private void onDeviceRemoved(String deviceObjectPath) {
|
||||
String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
|
||||
if (address != null) {
|
||||
mBluetoothService.getBondState().setBondState(address.toUpperCase(),
|
||||
BluetoothDevice.BOND_NONE, BluetoothDevice.UNBOND_REASON_REMOVED);
|
||||
mBluetoothService.setBondState(address.toUpperCase(), BluetoothDevice.BOND_NONE,
|
||||
BluetoothDevice.UNBOND_REASON_REMOVED);
|
||||
mBluetoothService.setRemoteDeviceProperty(address, "UUIDs", null);
|
||||
}
|
||||
}
|
||||
@@ -407,13 +343,11 @@ class BluetoothEventLoop {
|
||||
// If locally initiated pairing, we will
|
||||
// not go to BOND_BONDED state until we have received a
|
||||
// successful return value in onCreatePairedDeviceResult
|
||||
if (null == mBluetoothService.getBondState().getPendingOutgoingBonding()) {
|
||||
mBluetoothService.getBondState().setBondState(address,
|
||||
BluetoothDevice.BOND_BONDED);
|
||||
if (null == mBluetoothService.getPendingOutgoingBonding()) {
|
||||
mBluetoothService.setBondState(address, BluetoothDevice.BOND_BONDED);
|
||||
}
|
||||
} else {
|
||||
mBluetoothService.getBondState().setBondState(address,
|
||||
BluetoothDevice.BOND_NONE);
|
||||
mBluetoothService.setBondState(address, BluetoothDevice.BOND_NONE);
|
||||
mBluetoothService.setRemoteDeviceProperty(address, "Trusted", "false");
|
||||
}
|
||||
} else if (name.equals("Trusted")) {
|
||||
@@ -443,8 +377,8 @@ class BluetoothEventLoop {
|
||||
// Also set it only when the state is not already Bonded, we can sometimes
|
||||
// get an authorization request from the remote end if it doesn't have the link key
|
||||
// while we still have it.
|
||||
if (mBluetoothService.getBondState().getBondState(address) != BluetoothDevice.BOND_BONDED)
|
||||
mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDING);
|
||||
if (mBluetoothService.getBondState(address) != BluetoothDevice.BOND_BONDED)
|
||||
mBluetoothService.setBondState(address, BluetoothDevice.BOND_BONDING);
|
||||
return address;
|
||||
}
|
||||
|
||||
@@ -458,7 +392,7 @@ class BluetoothEventLoop {
|
||||
* so we may get this request many times. Also if we respond immediately,
|
||||
* the other end is unable to handle it. Delay sending the message.
|
||||
*/
|
||||
if (mBluetoothService.getBondState().getBondState(address) == BluetoothDevice.BOND_BONDED) {
|
||||
if (mBluetoothService.getBondState(address) == BluetoothDevice.BOND_BONDED) {
|
||||
Message message = mHandler.obtainMessage(EVENT_PAIRING_CONSENT_DELAYED_ACCEPT);
|
||||
message.obj = address;
|
||||
mHandler.sendMessageDelayed(message, 1500);
|
||||
@@ -503,7 +437,7 @@ class BluetoothEventLoop {
|
||||
if (address == null) return;
|
||||
|
||||
String pendingOutgoingAddress =
|
||||
mBluetoothService.getBondState().getPendingOutgoingBonding();
|
||||
mBluetoothService.getPendingOutgoingBonding();
|
||||
if (address.equals(pendingOutgoingAddress)) {
|
||||
// we initiated the bonding
|
||||
|
||||
@@ -524,12 +458,7 @@ class BluetoothEventLoop {
|
||||
case BluetoothClass.Device.AUDIO_VIDEO_PORTABLE_AUDIO:
|
||||
case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
|
||||
case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO:
|
||||
if (!mBluetoothService.getBondState().hasAutoPairingFailed(address) &&
|
||||
!mBluetoothService.getBondState().isAutoPairingBlacklisted(address)) {
|
||||
mBluetoothService.getBondState().attempt(address);
|
||||
mBluetoothService.setPin(address, BluetoothDevice.convertPinToBytes("0000"));
|
||||
return;
|
||||
}
|
||||
if (mBluetoothService.attemptAutoPair(address)) return;
|
||||
}
|
||||
}
|
||||
Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
|
||||
|
||||
@@ -104,6 +104,14 @@ public class BluetoothService extends IBluetooth.Stub {
|
||||
private static final int MESSAGE_FINISH_DISABLE = 2;
|
||||
private static final int MESSAGE_UUID_INTENT = 3;
|
||||
private static final int MESSAGE_DISCOVERABLE_TIMEOUT = 4;
|
||||
private static final int MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 5;
|
||||
|
||||
// The time (in millisecs) to delay the pairing attempt after the first
|
||||
// auto pairing attempt fails. We use an exponential delay with
|
||||
// INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the initial value and
|
||||
// MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY as the max value.
|
||||
private static final long INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 3000;
|
||||
private static final long MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY = 12000;
|
||||
|
||||
// The timeout used to sent the UUIDs Intent
|
||||
// This timeout should be greater than the page timeout
|
||||
@@ -502,6 +510,13 @@ public class BluetoothService extends IBluetooth.Stub {
|
||||
setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE, -1);
|
||||
}
|
||||
break;
|
||||
case MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY:
|
||||
address = (String)msg.obj;
|
||||
if (address != null) {
|
||||
createBond(address);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -591,8 +606,68 @@ public class BluetoothService extends IBluetooth.Stub {
|
||||
Binder.restoreCallingIdentity(origCallerIdentityToken);
|
||||
}
|
||||
|
||||
/* package */ BondState getBondState() {
|
||||
return mBondState;
|
||||
/*package*/ synchronized boolean attemptAutoPair(String address) {
|
||||
if (!mBondState.hasAutoPairingFailed(address) &&
|
||||
!mBondState.isAutoPairingBlacklisted(address)) {
|
||||
mBondState.attempt(address);
|
||||
setPin(address, BluetoothDevice.convertPinToBytes("0000"));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*package*/ synchronized void onCreatePairedDeviceResult(String address, int result) {
|
||||
if (result == BluetoothDevice.BOND_SUCCESS) {
|
||||
setBondState(address, BluetoothDevice.BOND_BONDED);
|
||||
if (mBondState.isAutoPairingAttemptsInProgress(address)) {
|
||||
mBondState.clearPinAttempts(address);
|
||||
}
|
||||
} else if (result == BluetoothDevice.UNBOND_REASON_AUTH_FAILED &&
|
||||
mBondState.getAttempt(address) == 1) {
|
||||
mBondState.addAutoPairingFailure(address);
|
||||
pairingAttempt(address, result);
|
||||
} else if (result == BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN &&
|
||||
mBondState.isAutoPairingAttemptsInProgress(address)) {
|
||||
pairingAttempt(address, result);
|
||||
} else {
|
||||
setBondState(address, BluetoothDevice.BOND_NONE, result);
|
||||
if (mBondState.isAutoPairingAttemptsInProgress(address)) {
|
||||
mBondState.clearPinAttempts(address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*package*/ synchronized String getPendingOutgoingBonding() {
|
||||
return mBondState.getPendingOutgoingBonding();
|
||||
}
|
||||
|
||||
private void pairingAttempt(String address, int result) {
|
||||
// This happens when our initial guess of "0000" as the pass key
|
||||
// fails. Try to create the bond again and display the pin dialog
|
||||
// to the user. Use back-off while posting the delayed
|
||||
// message. The initial value is
|
||||
// INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY and the max value is
|
||||
// MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY. If the max value is
|
||||
// reached, display an error to the user.
|
||||
int attempt = mBondState.getAttempt(address);
|
||||
if (attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY >
|
||||
MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY) {
|
||||
mBondState.clearPinAttempts(address);
|
||||
setBondState(address, BluetoothDevice.BOND_NONE, result);
|
||||
return;
|
||||
}
|
||||
|
||||
Message message = mHandler.obtainMessage(MESSAGE_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY);
|
||||
message.obj = address;
|
||||
boolean postResult = mHandler.sendMessageDelayed(message,
|
||||
attempt * INIT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY);
|
||||
if (!postResult) {
|
||||
mBondState.clearPinAttempts(address);
|
||||
setBondState(address,
|
||||
BluetoothDevice.BOND_NONE, result);
|
||||
return;
|
||||
}
|
||||
mBondState.attempt(address);
|
||||
}
|
||||
|
||||
/** local cache of bonding state.
|
||||
@@ -1259,6 +1334,10 @@ public class BluetoothService extends IBluetooth.Stub {
|
||||
return mBondState.listInState(BluetoothDevice.BOND_BONDED);
|
||||
}
|
||||
|
||||
/*package*/ synchronized String[] listInState(int state) {
|
||||
return mBondState.listInState(state);
|
||||
}
|
||||
|
||||
public synchronized int getBondState(String address) {
|
||||
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
|
||||
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
|
||||
@@ -1267,6 +1346,15 @@ public class BluetoothService extends IBluetooth.Stub {
|
||||
return mBondState.getBondState(address.toUpperCase());
|
||||
}
|
||||
|
||||
/*package*/ synchronized boolean setBondState(String address, int state) {
|
||||
return setBondState(address, state, 0);
|
||||
}
|
||||
|
||||
/*package*/ synchronized boolean setBondState(String address, int state, int reason) {
|
||||
mBondState.setBondState(address.toUpperCase(), state);
|
||||
return true;
|
||||
}
|
||||
|
||||
public synchronized boolean isBluetoothDock(String address) {
|
||||
SharedPreferences sp = mContext.getSharedPreferences(SHARED_PREFERENCES_NAME,
|
||||
mContext.MODE_PRIVATE);
|
||||
|
||||
Reference in New Issue
Block a user