Changes for BT 2.1

1) Handle incoming 2.1 pairing requests
2) Modify displaying error messages on bond failures.
3) Add delay while accepting incoming pairing for certain 2.1 devices.
When MITM is on, the link key request might come more than once.
Auto accept with a delay.
4) Handle DisplayPasskey callback for pairing a 2.1 keyboard with
a 2.1 device
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index a9cec50..b1861ac 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -250,7 +250,7 @@
      * @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 @@
     /** 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 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 d0d4b84..1ed5c49 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 @@
 
     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 @@
     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 @@
             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 @@
                 addDevice(address, properties);
             }
         }
+        mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDING);
         return;
     }
 
@@ -390,7 +399,32 @@
         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 @@
         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 @@
         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 53fb03c..6ce0f5f 100644
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -1266,5 +1266,4 @@
     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 79a3247..e703ed8 100644
--- a/core/jni/android_server_BluetoothEventLoop.cpp
+++ b/core/jni/android_server_BluetoothEventLoop.cpp
@@ -52,7 +52,9 @@
 
 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 @@
                                                "(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 @@
 
 
 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 @@
 
     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) {
@@ -939,6 +943,24 @@
                                        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;
         uint32_t passkey;
@@ -951,12 +973,27 @@
         }
 
         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
         DBusMessage *reply = dbus_message_new_method_return(msg);
@@ -992,6 +1029,8 @@
 #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 @@
                    !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 de921f1..0b71acb 100644
--- a/core/jni/android_server_BluetoothService.cpp
+++ b/core/jni/android_server_BluetoothService.cpp
@@ -455,8 +455,8 @@
         }
 
         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;
         }