summaryrefslogtreecommitdiffstats
path: root/core/java/android/server/BluetoothEventLoop.java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android/server/BluetoothEventLoop.java')
-rw-r--r--core/java/android/server/BluetoothEventLoop.java513
1 files changed, 383 insertions, 130 deletions
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index 8cc229b..c0b9a68 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -17,16 +17,15 @@
package android.server;
import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothError;
-import android.bluetooth.BluetoothIntent;
-import android.bluetooth.IBluetoothDeviceCallback;
+import android.bluetooth.BluetoothUuid;
+import android.os.ParcelUuid;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
-import android.os.RemoteException;
import android.util.Log;
import java.util.HashMap;
@@ -46,13 +45,20 @@ class BluetoothEventLoop {
private Thread mThread;
private boolean mStarted;
private boolean mInterrupted;
+
private final HashMap<String, Integer> mPasskeyAgentRequestData;
- private final HashMap<String, IBluetoothDeviceCallback> mGetRemoteServiceChannelCallbacks;
- private final BluetoothDeviceService mBluetoothService;
+ private final BluetoothService mBluetoothService;
+ 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 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
@@ -67,9 +73,10 @@ class BluetoothEventLoop {
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
+ String address = null;
switch (msg.what) {
case EVENT_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY:
- String address = (String)msg.obj;
+ address = (String)msg.obj;
if (address != null) {
mBluetoothService.createBond(address);
return;
@@ -78,6 +85,28 @@ class BluetoothEventLoop {
case EVENT_RESTART_BLUETOOTH:
mBluetoothService.restart();
break;
+ case EVENT_PAIRING_CONSENT_DELAYED_ACCEPT:
+ address = (String)msg.obj;
+ if (address != null) {
+ mBluetoothService.setPairingConfirmation(address, true);
+ }
+ break;
+ 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);
+ if (devices.length == 0) {
+ break;
+ } else if (devices.length > 1) {
+ Log.e(TAG, " There is more than one device in the Bonding State");
+ break;
+ }
+ address = devices[0];
+ mBluetoothService.getBondState().setBondState(address,
+ BluetoothDevice.BOND_NONE,
+ BluetoothDevice.UNBOND_REASON_REMOTE_AUTH_CANCELED);
+ break;
}
}
};
@@ -85,14 +114,14 @@ class BluetoothEventLoop {
static { classInitNative(); }
private static native void classInitNative();
- /* pacakge */ BluetoothEventLoop(Context context, BluetoothDeviceService bluetoothService) {
+ /* pacakge */ BluetoothEventLoop(Context context, BluetoothAdapter adapter,
+ BluetoothService bluetoothService) {
mBluetoothService = bluetoothService;
mContext = context;
mPasskeyAgentRequestData = new HashMap();
- mGetRemoteServiceChannelCallbacks = new HashMap();
+ mAdapter = adapter;
initializeNativeDataNative();
}
- private native void initializeNativeDataNative();
protected void finalize() throws Throwable {
try {
@@ -101,20 +130,11 @@ class BluetoothEventLoop {
super.finalize();
}
}
- private native void cleanupNativeDataNative();
-
- /* pacakge */ HashMap<String, IBluetoothDeviceCallback> getRemoteServiceChannelCallbacks() {
- return mGetRemoteServiceChannelCallbacks;
- }
- /* pacakge */ HashMap<String, Integer> getPasskeyAgentRequestData() {
+ /* package */ HashMap<String, Integer> getPasskeyAgentRequestData() {
return mPasskeyAgentRequestData;
}
- private native void startEventLoopNative();
- private native void stopEventLoopNative();
- private native boolean isEventLoopRunningNative();
-
/* package */ void start() {
if (!isEventLoopRunningNative()) {
@@ -134,81 +154,61 @@ class BluetoothEventLoop {
return isEventLoopRunningNative();
}
- /*package*/ void onModeChanged(String bluezMode) {
- int mode = BluetoothDeviceService.bluezStringToScanMode(bluezMode);
- if (mode >= 0) {
- Intent intent = new Intent(BluetoothIntent.SCAN_MODE_CHANGED_ACTION);
- intent.putExtra(BluetoothIntent.SCAN_MODE, mode);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ private void addDevice(String address, String[] properties) {
+ mBluetoothService.addRemoteDeviceProperties(address, properties);
+ String rssi = mBluetoothService.getRemoteDeviceProperty(address, "RSSI");
+ String classValue = mBluetoothService.getRemoteDeviceProperty(address, "Class");
+ String name = mBluetoothService.getRemoteDeviceProperty(address, "Name");
+ short rssiValue;
+ // For incoming connections, we don't get the RSSI value. Use a default of MIN_VALUE.
+ // If we accept the pairing, we will automatically show it at the top of the list.
+ if (rssi != null) {
+ rssiValue = (short)Integer.valueOf(rssi).intValue();
+ } else {
+ rssiValue = Short.MIN_VALUE;
+ }
+ if (classValue != null) {
+ Intent intent = new Intent(BluetoothDevice.ACTION_FOUND);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
+ intent.putExtra(BluetoothDevice.EXTRA_CLASS,
+ new BluetoothClass(Integer.valueOf(classValue)));
+ intent.putExtra(BluetoothDevice.EXTRA_RSSI, rssiValue);
+ intent.putExtra(BluetoothDevice.EXTRA_NAME, name);
+
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+ } else {
+ log ("ClassValue: " + classValue + " for remote device: " + address + " is null");
}
}
- private void onDiscoveryStarted() {
- mBluetoothService.setIsDiscovering(true);
- Intent intent = new Intent(BluetoothIntent.DISCOVERY_STARTED_ACTION);
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- }
- private void onDiscoveryCompleted() {
- mBluetoothService.setIsDiscovering(false);
- Intent intent = new Intent(BluetoothIntent.DISCOVERY_COMPLETED_ACTION);
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+ private void onDeviceFound(String address, String[] properties) {
+ if (properties == null) {
+ Log.e(TAG, "ERROR: Remote device properties are null");
+ return;
+ }
+ addDevice(address, properties);
}
- private void onRemoteDeviceFound(String address, int deviceClass, short rssi) {
- Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_FOUND_ACTION);
- intent.putExtra(BluetoothIntent.ADDRESS, address);
- intent.putExtra(BluetoothIntent.CLASS, deviceClass);
- intent.putExtra(BluetoothIntent.RSSI, rssi);
+ private void onDeviceDisappeared(String address) {
+ Intent intent = new Intent(BluetoothDevice.ACTION_DISAPPEARED);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
}
- private void onRemoteDeviceDisappeared(String address) {
- Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISAPPEARED_ACTION);
- intent.putExtra(BluetoothIntent.ADDRESS, address);
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- }
- private void onRemoteClassUpdated(String address, int deviceClass) {
- Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_CLASS_UPDATED_ACTION);
- intent.putExtra(BluetoothIntent.ADDRESS, address);
- intent.putExtra(BluetoothIntent.CLASS, deviceClass);
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- }
- private void onRemoteDeviceConnected(String address) {
- Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_CONNECTED_ACTION);
- intent.putExtra(BluetoothIntent.ADDRESS, address);
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- }
- private void onRemoteDeviceDisconnectRequested(String address) {
- Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISCONNECT_REQUESTED_ACTION);
- intent.putExtra(BluetoothIntent.ADDRESS, address);
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- }
- private void onRemoteDeviceDisconnected(String address) {
- Intent intent = new Intent(BluetoothIntent.REMOTE_DEVICE_DISCONNECTED_ACTION);
- intent.putExtra(BluetoothIntent.ADDRESS, address);
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- }
- private void onRemoteNameUpdated(String address, String name) {
- Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_UPDATED_ACTION);
- intent.putExtra(BluetoothIntent.ADDRESS, address);
- intent.putExtra(BluetoothIntent.NAME, name);
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- }
- private void onRemoteNameFailed(String address) {
- Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_FAILED_ACTION);
- intent.putExtra(BluetoothIntent.ADDRESS, address);
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
- }
- private void onRemoteNameChanged(String address, String name) {
- Intent intent = new Intent(BluetoothIntent.REMOTE_NAME_UPDATED_ACTION);
- intent.putExtra(BluetoothIntent.ADDRESS, address);
- intent.putExtra(BluetoothIntent.NAME, name);
+
+ private void onDeviceDisconnectRequested(String deviceObjectPath) {
+ String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
+ if (address == null) {
+ Log.e(TAG, "onDeviceDisconnectRequested: Address of the remote device in null");
+ return;
+ }
+ Intent intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECT_REQUESTED);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
mContext.sendBroadcast(intent, BLUETOOTH_PERM);
}
- private void onCreateBondingResult(String address, int result) {
+ private void onCreatePairedDeviceResult(String address, int result) {
address = address.toUpperCase();
- if (result == BluetoothError.SUCCESS) {
+ if (result == BluetoothDevice.BOND_SUCCESS) {
mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDED);
if (mBluetoothService.getBondState().isAutoPairingAttemptsInProgress(address)) {
mBluetoothService.getBondState().clearPinAttempts(address);
@@ -222,7 +222,7 @@ class BluetoothEventLoop {
pairingAttempt(address, result);
} else {
mBluetoothService.getBondState().setBondState(address,
- BluetoothDevice.BOND_NOT_BONDED, result);
+ BluetoothDevice.BOND_NONE, result);
if (mBluetoothService.getBondState().isAutoPairingAttemptsInProgress(address)) {
mBluetoothService.getBondState().clearPinAttempts(address);
}
@@ -242,7 +242,7 @@ class BluetoothEventLoop {
MAX_AUTO_PAIRING_FAILURE_ATTEMPT_DELAY) {
mBluetoothService.getBondState().clearPinAttempts(address);
mBluetoothService.getBondState().setBondState(address,
- BluetoothDevice.BOND_NOT_BONDED, result);
+ BluetoothDevice.BOND_NONE, result);
return;
}
@@ -253,45 +253,249 @@ class BluetoothEventLoop {
if (!postResult) {
mBluetoothService.getBondState().clearPinAttempts(address);
mBluetoothService.getBondState().setBondState(address,
- BluetoothDevice.BOND_NOT_BONDED, result);
+ BluetoothDevice.BOND_NONE, result);
return;
}
mBluetoothService.getBondState().attempt(address);
}
- private void onBondingCreated(String address) {
- mBluetoothService.getBondState().setBondState(address.toUpperCase(),
- BluetoothDevice.BOND_BONDED);
+ private void onDeviceCreated(String deviceObjectPath) {
+ String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
+ if (!mBluetoothService.isRemoteDeviceInCache(address)) {
+ // Incoming connection, we haven't seen this device, add to cache.
+ String[] properties = mBluetoothService.getRemoteDeviceProperties(address);
+ if (properties != null) {
+ addDevice(address, properties);
+ }
+ }
+ return;
}
- private void onBondingRemoved(String address) {
- mBluetoothService.getBondState().setBondState(address.toUpperCase(),
- BluetoothDevice.BOND_NOT_BONDED, BluetoothDevice.UNBOND_REASON_REMOVED);
+ 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);
}
- private void onNameChanged(String name) {
- Intent intent = new Intent(BluetoothIntent.NAME_CHANGED_ACTION);
- intent.putExtra(BluetoothIntent.NAME, name);
- mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+ /*package*/ void onPropertyChanged(String[] propValues) {
+ if (mBluetoothService.isAdapterPropertiesEmpty()) {
+ // We have got a property change before
+ // we filled up our cache.
+ mBluetoothService.getAllProperties();
+ }
+ String name = propValues[0];
+ if (name.equals("Name")) {
+ Intent intent = new Intent(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
+ intent.putExtra(BluetoothAdapter.EXTRA_LOCAL_NAME, propValues[1]);
+ mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+ mBluetoothService.setProperty(name, propValues[1]);
+ } else if (name.equals("Pairable") || name.equals("Discoverable")) {
+ String pairable = name.equals("Pairable") ? propValues[1] :
+ mBluetoothService.getProperty("Pairable");
+ String discoverable = name.equals("Discoverable") ? propValues[1] :
+ mBluetoothService.getProperty("Discoverable");
+
+ // This shouldn't happen, unless Adapter Properties are null.
+ if (pairable == null || discoverable == null)
+ return;
+
+ int mode = BluetoothService.bluezStringToScanMode(
+ pairable.equals("true"),
+ discoverable.equals("true"));
+ if (mode >= 0) {
+ Intent intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
+ intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, mode);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+ }
+ mBluetoothService.setProperty(name, propValues[1]);
+ } else if (name.equals("Discovering")) {
+ Intent intent;
+ if (propValues[1].equals("true")) {
+ mBluetoothService.setIsDiscovering(true);
+ intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
+ } else {
+ // Stop the discovery.
+ mBluetoothService.cancelDiscovery();
+ mBluetoothService.setIsDiscovering(false);
+ intent = new Intent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
+ }
+ mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+ mBluetoothService.setProperty(name, propValues[1]);
+ } else if (name.equals("Devices")) {
+ String value = null;
+ int len = Integer.valueOf(propValues[1]);
+ if (len > 0) {
+ StringBuilder str = new StringBuilder();
+ for (int i = 2; i < propValues.length; i++) {
+ str.append(propValues[i]);
+ str.append(",");
+ }
+ value = str.toString();
+ }
+ mBluetoothService.setProperty(name, value);
+ } else if (name.equals("Powered")) {
+ // bluetoothd has restarted, re-read all our properties.
+ // Note: bluez only sends this property change when it restarts.
+ if (propValues[1].equals("true"))
+ onRestartRequired();
+ }
}
- private void onPasskeyAgentRequest(String address, int nativeData) {
+ private void onDevicePropertyChanged(String deviceObjectPath, String[] propValues) {
+ String name = propValues[0];
+ String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
+ if (address == null) {
+ Log.e(TAG, "onDevicePropertyChanged: Address of the remote device in null");
+ return;
+ }
+ if (DBG) {
+ log("Device property changed:" + address + "property:" + name);
+ }
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ if (name.equals("Name")) {
+ Intent intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+ intent.putExtra(BluetoothDevice.EXTRA_NAME, propValues[1]);
+ mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+ mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
+ } else if (name.equals("Class")) {
+ Intent intent = new Intent(BluetoothDevice.ACTION_CLASS_CHANGED);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+ intent.putExtra(BluetoothDevice.EXTRA_CLASS,
+ new BluetoothClass(Integer.valueOf(propValues[1])));
+ mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+ mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
+ } else if (name.equals("Connected")) {
+ Intent intent = null;
+ if (propValues[1].equals("true")) {
+ intent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED);
+ } else {
+ intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+ }
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+ mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+ mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
+ } else if (name.equals("UUIDs")) {
+ String uuid = null;
+ int len = Integer.valueOf(propValues[1]);
+ if (len > 0) {
+ StringBuilder str = new StringBuilder();
+ for (int i = 2; i < propValues.length; i++) {
+ str.append(propValues[i]);
+ str.append(",");
+ }
+ uuid = str.toString();
+ }
+ mBluetoothService.setRemoteDeviceProperty(address, name, uuid);
+
+ // UUIDs have changed, query remote service channel and update cache.
+ mBluetoothService.updateDeviceServiceChannelCache(address);
+
+ mBluetoothService.sendUuidIntent(address);
+ } else if (name.equals("Paired")) {
+ if (propValues[1].equals("true")) {
+ mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDED);
+ } else {
+ mBluetoothService.getBondState().setBondState(address,
+ BluetoothDevice.BOND_NONE);
+ mBluetoothService.setRemoteDeviceProperty(address, "Trusted", "false");
+ }
+ } else if (name.equals("Trusted")) {
+ if (DBG)
+ log("set trust state succeded, value is " + propValues[1]);
+ mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
+ }
+ }
+
+ private String checkPairingRequestAndGetAddress(String objectPath, int nativeData) {
+ String address = mBluetoothService.getAddressFromObjectPath(objectPath);
+ if (address == null) {
+ Log.e(TAG, "Unable to get device address in checkPairingRequestAndGetAddress, " +
+ "returning null");
+ return null;
+ }
address = address.toUpperCase();
mPasskeyAgentRequestData.put(address, new Integer(nativeData));
- if (mBluetoothService.getBluetoothState() == BluetoothDevice.BLUETOOTH_STATE_TURNING_OFF) {
+ if (mBluetoothService.getBluetoothState() == BluetoothAdapter.STATE_TURNING_OFF) {
// shutdown path
- mBluetoothService.cancelPin(address);
+ mBluetoothService.cancelPairingUserInput(address);
+ return null;
+ }
+ // Set state to BONDING. For incoming connections it will be set here.
+ // For outgoing connections, it gets set when we call createBond.
+ // 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);
+ return address;
+ }
+
+ private void onRequestPairingConsent(String objectPath, int nativeData) {
+ String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
+ if (address == null) return;
+
+ /* The link key will not be stored if the incoming request has MITM
+ * protection switched on. Unfortunately, some devices have MITM
+ * switched on even though their capabilities are NoInputNoOutput,
+ * 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) {
+ Message message = mHandler.obtainMessage(EVENT_PAIRING_CONSENT_DELAYED_ACCEPT);
+ message.obj = address;
+ mHandler.sendMessageDelayed(message, 1500);
return;
}
- if (mBluetoothService.getBondState().getBondState(address) ==
- BluetoothDevice.BOND_BONDING) {
+ Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
+ intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
+ BluetoothDevice.PAIRING_VARIANT_CONSENT);
+ mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
+ return;
+ }
+
+ private void onRequestPasskeyConfirmation(String objectPath, int passkey, int nativeData) {
+ String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
+ if (address == null) return;
+
+ Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
+ intent.putExtra(BluetoothDevice.EXTRA_PASSKEY, passkey);
+ intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
+ BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION);
+ mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
+ return;
+ }
+
+ private void onRequestPasskey(String objectPath, int nativeData) {
+ String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
+ if (address == null) return;
+
+ Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
+ intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
+ BluetoothDevice.PAIRING_VARIANT_PASSKEY);
+ mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
+ return;
+ }
+
+ private void onRequestPinCode(String objectPath, int nativeData) {
+ String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
+ if (address == null) return;
+
+ String pendingOutgoingAddress =
+ mBluetoothService.getBondState().getPendingOutgoingBonding();
+ if (address.equals(pendingOutgoingAddress)) {
// we initiated the bonding
- int btClass = mBluetoothService.getRemoteClass(address);
+ BluetoothClass btClass = new BluetoothClass(mBluetoothService.getRemoteClass(address));
// try 0000 once if the device looks dumb
- switch (BluetoothClass.Device.getDevice(btClass)) {
+ switch (btClass.getDeviceClass()) {
case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES:
@@ -306,56 +510,99 @@ class BluetoothEventLoop {
}
}
}
- Intent intent = new Intent(BluetoothIntent.PAIRING_REQUEST_ACTION);
- intent.putExtra(BluetoothIntent.ADDRESS, address);
+ Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
+ intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, BluetoothDevice.PAIRING_VARIANT_PIN);
mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
+ return;
}
- private void onPasskeyAgentCancel(String address) {
- address = address.toUpperCase();
- mBluetoothService.cancelPin(address);
- Intent intent = new Intent(BluetoothIntent.PAIRING_CANCEL_ACTION);
- intent.putExtra(BluetoothIntent.ADDRESS, address);
+ private void onDisplayPasskey(String objectPath, int passkey, int nativeData) {
+ String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
+ if (address == null) return;
+
+ Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
+ intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
+ intent.putExtra(BluetoothDevice.EXTRA_PASSKEY, passkey);
+ intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
+ BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY);
mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
- mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_NOT_BONDED,
- BluetoothDevice.UNBOND_REASON_AUTH_CANCELED);
}
- private boolean onAuthAgentAuthorize(String address, String service, String uuid) {
+ private boolean onAgentAuthorize(String objectPath, String deviceUuid) {
+ String address = mBluetoothService.getAddressFromObjectPath(objectPath);
+ if (address == null) {
+ Log.e(TAG, "Unable to get device address in onAuthAgentAuthorize");
+ return false;
+ }
+
boolean authorized = false;
- if (mBluetoothService.isEnabled() && service.endsWith("service_audio")) {
+ ParcelUuid uuid = ParcelUuid.fromString(deviceUuid);
+ // Bluez sends the UUID of the local service being accessed, _not_ the
+ // remote service
+ if (mBluetoothService.isEnabled() &&
+ (BluetoothUuid.isAudioSource(uuid) || BluetoothUuid.isAvrcpTarget(uuid)
+ || BluetoothUuid.isAdvAudioDist(uuid))) {
BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
- authorized = a2dp.getSinkPriority(address) > BluetoothA2dp.PRIORITY_OFF;
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ authorized = a2dp.getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF;
if (authorized) {
- Log.i(TAG, "Allowing incoming A2DP connection from " + address);
+ Log.i(TAG, "Allowing incoming A2DP / AVRCP connection from " + address);
} else {
- Log.i(TAG, "Rejecting incoming A2DP connection from " + address);
+ Log.i(TAG, "Rejecting incoming A2DP / AVRCP connection from " + address);
}
} else {
- Log.i(TAG, "Rejecting incoming " + service + " connection from " + address);
+ Log.i(TAG, "Rejecting incoming " + deviceUuid + " connection from " + address);
}
+ log("onAgentAuthorize(" + objectPath + ", " + deviceUuid + ") = " + authorized);
return authorized;
}
- private void onAuthAgentCancel(String address, String service, String uuid) {
- // We immediately response to DBUS Authorize() so this should not
- // usually happen
- log("onAuthAgentCancel(" + address + ", " + service + ", " + uuid + ")");
+ private void onAgentCancel() {
+ Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_CANCEL);
+ mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
+
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_AGENT_CANCEL),
+ 1500);
+
+ return;
+ }
+
+ private void onDiscoverServicesResult(String deviceObjectPath, boolean result) {
+ String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
+ // We don't parse the xml here, instead just query Bluez for the properties.
+ if (result) {
+ mBluetoothService.updateRemoteDevicePropertiesCache(address);
+ }
+ mBluetoothService.sendUuidIntent(address);
+ mBluetoothService.makeServiceChannelCallbacks(address);
}
- private void onGetRemoteServiceChannelResult(String address, int channel) {
- IBluetoothDeviceCallback callback = mGetRemoteServiceChannelCallbacks.get(address);
- if (callback != null) {
- mGetRemoteServiceChannelCallbacks.remove(address);
- try {
- callback.onGetRemoteServiceChannelResult(address, channel);
- } catch (RemoteException e) {}
+ private void onCreateDeviceResult(String address, int result) {
+ if (DBG) log("Result of onCreateDeviceResult:" + result);
+
+ switch (result) {
+ case CREATE_DEVICE_ALREADY_EXISTS:
+ String path = mBluetoothService.getObjectPathFromAddress(address);
+ if (path != null) {
+ mBluetoothService.discoverServicesNative(path, "");
+ break;
+ }
+ Log.w(TAG, "Device exists, but we dont have the bluez path, failing");
+ // fall-through
+ case CREATE_DEVICE_FAILED:
+ mBluetoothService.sendUuidIntent(address);
+ mBluetoothService.makeServiceChannelCallbacks(address);
+ break;
+ case CREATE_DEVICE_SUCCESS:
+ // nothing to do, UUID intent's will be sent via property changed
}
}
private void onRestartRequired() {
if (mBluetoothService.isEnabled()) {
- Log.e(TAG, "*** A serious error occured (did hcid crash?) - restarting Bluetooth ***");
+ Log.e(TAG, "*** A serious error occured (did bluetoothd crash?) - " +
+ "restarting Bluetooth ***");
mHandler.sendEmptyMessage(EVENT_RESTART_BLUETOOTH);
}
}
@@ -363,4 +610,10 @@ class BluetoothEventLoop {
private static void log(String msg) {
Log.d(TAG, msg);
}
+
+ private native void initializeNativeDataNative();
+ private native void startEventLoopNative();
+ private native void stopEventLoopNative();
+ private native boolean isEventLoopRunningNative();
+ private native void cleanupNativeDataNative();
}