diff options
Diffstat (limited to 'core/java/android/server/BluetoothEventLoop.java')
-rw-r--r-- | core/java/android/server/BluetoothEventLoop.java | 513 |
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(); } |