diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index a9cec5033b661..b1861acb708b9 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -250,7 +250,7 @@ public final class BluetoothDevice implements Parcelable { * @hide */ public static final int BOND_SUCCESS = 0; /** A bond attempt failed because pins did not match, or remote device did - * not respond to pin request in time + * not respond to pin request in time * @hide */ public static final int UNBOND_REASON_AUTH_FAILED = 1; /** A bond attempt failed because the other side explicilty rejected @@ -266,9 +266,15 @@ public final class BluetoothDevice implements Parcelable { /** A bond attempt failed because a discovery is in progress * @hide */ public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5; + /** A bond attempt failed because of authentication timeout + * @hide */ + public static final int UNBOND_REASON_AUTH_TIMEOUT = 6; + /** A bond attempt failed because of repeated attempts + * @hide */ + public static final int UNBOND_REASON_REPEATED_ATTEMPTS = 7; /** An existing bond was explicitly revoked * @hide */ - public static final int UNBOND_REASON_REMOVED = 6; + public static final int UNBOND_REASON_REMOVED = 8; /** The user will be prompted to enter a pin * @hide */ @@ -278,7 +284,13 @@ public final class BluetoothDevice implements Parcelable { public static final int PAIRING_VARIANT_PASSKEY = 1; /** The user will be prompted to confirm the passkey displayed on the screen * @hide */ - public static final int PAIRING_VARIANT_CONFIRMATION = 2; + public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2; + /** The user will be prompted to accept or deny the incoming pairing request + * @hide */ + public static final int PAIRING_VARIANT_CONSENT = 3; + /** The user will be prompted to enter the passkey displayed on remote device + * @hide */ + public static final int PAIRING_VARIANT_DISPLAY_PASSKEY = 4; private static IBluetooth sService; /* Guarenteed constant after first object constructed */ diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index d0d4b84997fe3..1ed5c49e592e8 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -17,8 +17,8 @@ package android.server; import android.bluetooth.BluetoothA2dp; -import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothUuid; import android.content.Context; @@ -53,6 +53,7 @@ class BluetoothEventLoop { 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; // The time (in millisecs) to delay the pairing attempt after the first // auto pairing attempt fails. We use an exponential delay with @@ -67,9 +68,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 +80,12 @@ 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; } } }; @@ -239,6 +247,7 @@ class BluetoothEventLoop { addDevice(address, properties); } } + mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDING); return; } @@ -390,7 +399,32 @@ class BluetoothEventLoop { return address; } - private void onRequestConfirmation(String objectPath, int passkey, int nativeData) { + 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; + } + + 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; @@ -398,7 +432,7 @@ class BluetoothEventLoop { intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address)); intent.putExtra(BluetoothDevice.EXTRA_PASSKEY, passkey); intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT, - BluetoothDevice.PAIRING_VARIANT_CONFIRMATION); + BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION); mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); return; } @@ -447,6 +481,18 @@ class BluetoothEventLoop { return; } + 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); + } + private boolean onAgentAuthorize(String objectPath, String deviceUuid) { String address = mBluetoothService.getAddressFromObjectPath(objectPath); if (address == null) { diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index 53fb03cfdae0a..6ce0f5f070ba4 100644 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -1266,5 +1266,4 @@ public class BluetoothService extends IBluetooth.Stub { private native boolean setPairingConfirmationNative(String address, boolean confirm, int nativeData); private native boolean setDevicePropertyBooleanNative(String objectPath, String key, int value); - } diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp index 79a3247ef131f..e703ed85032d7 100644 --- a/core/jni/android_server_BluetoothEventLoop.cpp +++ b/core/jni/android_server_BluetoothEventLoop.cpp @@ -52,7 +52,9 @@ static jmethodID method_onGetDeviceServiceChannelResult; static jmethodID method_onRequestPinCode; static jmethodID method_onRequestPasskey; -static jmethodID method_onRequestConfirmation; +static jmethodID method_onRequestPasskeyConfirmation; +static jmethodID method_onRequestPairingConsent; +static jmethodID method_onDisplayPasskey; static jmethodID method_onAgentAuthorize; static jmethodID method_onAgentCancel; @@ -98,7 +100,11 @@ static void classInitNative(JNIEnv* env, jclass clazz) { "(Ljava/lang/String;I)V"); method_onRequestPasskey = env->GetMethodID(clazz, "onRequestPasskey", "(Ljava/lang/String;I)V"); - method_onRequestConfirmation = env->GetMethodID(clazz, "onRequestConfirmation", + method_onRequestPasskeyConfirmation = env->GetMethodID(clazz, "onRequestPasskeyConfirmation", + "(Ljava/lang/String;II)V"); + method_onRequestPairingConsent = env->GetMethodID(clazz, "onRequestPairingConsent", + "(Ljava/lang/String;I)V"); + method_onDisplayPasskey = env->GetMethodID(clazz, "onDisplayPasskey", "(Ljava/lang/String;II)V"); field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I"); @@ -230,7 +236,7 @@ static jboolean setUpEventLoop(native_data_t *nat) { const char * get_adapter_path(DBusConnection *conn) { - DBusMessage *msg, *reply = NULL; + DBusMessage *msg = NULL, *reply = NULL; DBusError err; const char *device_path = NULL; int attempt = 0; @@ -856,9 +862,7 @@ DBusHandlerResult agent_event_filter(DBusConnection *conn, if (dbus_message_is_method_call(msg, "org.bluez.Agent", "Cancel")) { - env->CallVoidMethod(nat->me, method_onAgentCancel); - // reply DBusMessage *reply = dbus_message_new_method_return(msg); if (!reply) { @@ -938,6 +942,24 @@ DBusHandlerResult agent_event_filter(DBusConnection *conn, env->NewStringUTF(object_path), int(msg)); goto success; + } else if (dbus_message_is_method_call(msg, + "org.bluez.Agent", "DisplayPasskey")) { + char *object_path; + uint32_t passkey; + if (!dbus_message_get_args(msg, NULL, + DBUS_TYPE_OBJECT_PATH, &object_path, + DBUS_TYPE_UINT32, &passkey, + DBUS_TYPE_INVALID)) { + LOGE("%s: Invalid arguments for RequestPasskey() method", __FUNCTION__); + goto failure; + } + + dbus_message_ref(msg); // increment refcount because we pass to java + env->CallVoidMethod(nat->me, method_onDisplayPasskey, + env->NewStringUTF(object_path), + passkey, + int(msg)); + goto success; } else if (dbus_message_is_method_call(msg, "org.bluez.Agent", "RequestConfirmation")) { char *object_path; @@ -951,11 +973,26 @@ DBusHandlerResult agent_event_filter(DBusConnection *conn, } dbus_message_ref(msg); // increment refcount because we pass to java - env->CallVoidMethod(nat->me, method_onRequestConfirmation, + env->CallVoidMethod(nat->me, method_onRequestPasskeyConfirmation, env->NewStringUTF(object_path), passkey, int(msg)); goto success; + } else if (dbus_message_is_method_call(msg, + "org.bluez.Agent", "RequestPairingConsent")) { + char *object_path; + if (!dbus_message_get_args(msg, NULL, + DBUS_TYPE_OBJECT_PATH, &object_path, + DBUS_TYPE_INVALID)) { + LOGE("%s: Invalid arguments for RequestPairingConsent() method", __FUNCTION__); + goto failure; + } + + dbus_message_ref(msg); // increment refcount because we pass to java + env->CallVoidMethod(nat->me, method_onRequestPairingConsent, + env->NewStringUTF(object_path), + int(msg)); + goto success; } else if (dbus_message_is_method_call(msg, "org.bluez.Agent", "Release")) { // reply @@ -992,6 +1029,8 @@ success: #define BOND_RESULT_AUTH_CANCELED 3 #define BOND_RESULT_REMOTE_DEVICE_DOWN 4 #define BOND_RESULT_DISCOVERY_IN_PROGRESS 5 +#define BOND_RESULT_AUTH_TIMEOUT 6 +#define BOND_RESULT_REPEATED_ATTEMPTS 7 void onCreatePairedDeviceResult(DBusMessage *msg, void *user, void *n) { LOGV(__FUNCTION__); @@ -1037,6 +1076,12 @@ void onCreatePairedDeviceResult(DBusMessage *msg, void *user, void *n) { !strcmp(err.message, "Discover in progress")) { LOGV("... error = %s (%s)\n", err.name, err.message); result = BOND_RESULT_DISCOVERY_IN_PROGRESS; + } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.RepeatedAttempts")) { + LOGV("... error = %s (%s)\n", err.name, err.message); + result = BOND_RESULT_REPEATED_ATTEMPTS; + } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationTimeout")) { + LOGV("... error = %s (%s)\n", err.name, err.message); + result = BOND_RESULT_AUTH_TIMEOUT; } else { LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); result = BOND_RESULT_ERROR; diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp index de921f1e08fa7..0b71acb5a85a4 100644 --- a/core/jni/android_server_BluetoothService.cpp +++ b/core/jni/android_server_BluetoothService.cpp @@ -455,8 +455,8 @@ static jboolean setPairingConfirmationNative(JNIEnv *env, jobject object, } if (!reply) { - LOGE("%s: Cannot create message reply to RequestConfirmation to " - "D-Bus\n", __FUNCTION__); + LOGE("%s: Cannot create message reply to RequestPasskeyConfirmation or" + "RequestPairingConsent to D-Bus\n", __FUNCTION__); dbus_message_unref(msg); return JNI_FALSE; }