Out Of Band API for Secure Simple Pairing.

Change-Id: I54ded27ab85d46eef3d2cca84f2394b1ffe88ced
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 03bcadc..16a8c57 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -26,8 +26,10 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.Log;
+import android.util.Pair;
 
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.LinkedList;
@@ -863,6 +865,37 @@
         return socket;
     }
 
+    /**
+     * Read the local Out of Band Pairing Data
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     *
+     * @return Pair<byte[], byte[]> of Hash and Randomizer
+     *
+     * @hide
+     */
+    public Pair<byte[], byte[]> readOutOfBandData() {
+        if (getState() != STATE_ON) return null;
+        try {
+            byte[] hash = new byte[16];
+            byte[] randomizer = new byte[16];
+
+            byte[] ret = mService.readOutOfBandData();
+
+            if (ret  == null || ret.length != 32) return null;
+
+            hash = Arrays.copyOfRange(ret, 0, 16);
+            randomizer = Arrays.copyOfRange(ret, 16, 32);
+
+            if (DBG) {
+                Log.d(TAG, "readOutOfBandData:" + Arrays.toString(hash) +
+                  ":" + Arrays.toString(randomizer));
+            }
+            return new Pair<byte[], byte[]>(hash, randomizer);
+
+        } catch (RemoteException e) {Log.e(TAG, "", e);}
+        return null;
+    }
+
     private Set<BluetoothDevice> toDeviceSet(String[] addresses) {
         Set<BluetoothDevice> devices = new HashSet<BluetoothDevice>(addresses.length);
         for (int i = 0; i < addresses.length; i++) {
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index e77e76f..e577ec4 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -325,7 +325,9 @@
     /** The user will be prompted to enter the passkey displayed on remote device
      * @hide */
     public static final int PAIRING_VARIANT_DISPLAY_PASSKEY = 4;
-
+    /** The user will be prompted to accept or deny the OOB pairing request
+     * @hide */
+    public static final int PAIRING_VARIANT_OOB_CONSENT = 5;
     /**
      * Used as an extra field in {@link #ACTION_UUID} intents,
      * Contains the {@link android.os.ParcelUuid}s of the remote device which
@@ -464,6 +466,52 @@
     }
 
     /**
+     * Start the bonding (pairing) process with the remote device using the
+     * Out Of Band mechanism.
+     *
+     * <p>This is an asynchronous call, it will return immediately. Register
+     * for {@link #ACTION_BOND_STATE_CHANGED} intents to be notified when
+     * the bonding process completes, and its result.
+     *
+     * <p>Android system services will handle the necessary user interactions
+     * to confirm and complete the bonding process.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
+     *
+     * @param hash - Simple Secure pairing hash
+     * @param randomizer - The random key obtained using OOB
+     * @return false on immediate error, true if bonding will begin
+     *
+     * @hide
+     */
+    public boolean createBondOutOfBand(byte[] hash, byte[] randomizer) {
+        try {
+            return sService.createBondOutOfBand(mAddress, hash, randomizer);
+        } catch (RemoteException e) {Log.e(TAG, "", e);}
+        return false;
+    }
+
+    /**
+     * Set the Out Of Band data for a remote device to be used later
+     * in the pairing mechanism. Users can obtain this data through other
+     * trusted channels
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
+     *
+     * @param hash Simple Secure pairing hash
+     * @param randomizer The random key obtained using OOB
+     * @return false on error; true otherwise
+     *
+     * @hide
+     */
+    public boolean setDeviceOutOfBandData(byte[] hash, byte[] randomizer) {
+      try {
+        return sService.setDeviceOutOfBandData(mAddress, hash, randomizer);
+      } catch (RemoteException e) {Log.e(TAG, "", e);}
+      return false;
+    }
+
+    /**
      * Cancel an in-progress bonding request started with {@link #createBond}.
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
      *
@@ -617,6 +665,14 @@
     }
 
     /** @hide */
+    public boolean setRemoteOutOfBandData() {
+        try {
+          return sService.setRemoteOutOfBandData(mAddress);
+      } catch (RemoteException e) {Log.e(TAG, "", e);}
+      return false;
+    }
+
+    /** @hide */
     public boolean cancelPairingUserInput() {
         try {
             return sService.cancelPairingUserInput(mAddress);
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index ea71034..6dd8dd6 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -44,12 +44,15 @@
     boolean startDiscovery();
     boolean cancelDiscovery();
     boolean isDiscovering();
+    byte[] readOutOfBandData();
 
     boolean createBond(in String address);
+    boolean createBondOutOfBand(in String address, in byte[] hash, in byte[] randomizer);
     boolean cancelBondProcess(in String address);
     boolean removeBond(in String address);
     String[] listBonds();
     int getBondState(in String address);
+    boolean setDeviceOutOfBandData(in String address, in byte[] hash, in byte[] randomizer);
 
     String getRemoteName(in String address);
     int getRemoteClass(in String address);
@@ -60,6 +63,7 @@
     boolean setPin(in String address, in byte[] pin);
     boolean setPasskey(in String address, int passkey);
     boolean setPairingConfirmation(in String address, boolean confirm);
+    boolean setRemoteOutOfBandData(in String addres);
     boolean cancelPairingUserInput(in String address);
 
     boolean setTrust(in String address, in boolean value);
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index 094258b..9b28a9a 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -551,6 +551,17 @@
         mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
     }
 
+    private void onRequestOobData(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_OOB_CONSENT);
+        mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
+    }
+
     private boolean onAgentAuthorize(String objectPath, String deviceUuid) {
         String address = mBluetoothService.getAddressFromObjectPath(objectPath);
         if (address == null) {
@@ -583,7 +594,21 @@
         return authorized;
     }
 
-    boolean isOtherSinkInNonDisconnectingState(String address) {
+    private boolean onAgentOutOfBandDataAvailable(String objectPath) {
+        if (!mBluetoothService.isEnabled()) return false;
+
+        String address = mBluetoothService.getAddressFromObjectPath(objectPath);
+        if (address == null) return false;
+
+        if (mBluetoothService.getDeviceOutOfBandData(
+            mAdapter.getRemoteDevice(address)) != null) {
+            return true;
+        }
+        return false;
+
+    }
+
+    private boolean isOtherSinkInNonDisconnectingState(String address) {
         BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
         Set<BluetoothDevice> devices = a2dp.getNonDisconnectedSinks();
         if (devices.size() == 0) return false;
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index b2aa04d..d53ac80 100644
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -50,6 +50,7 @@
 import android.os.SystemService;
 import android.provider.Settings;
 import android.util.Log;
+import android.util.Pair;
 
 import com.android.internal.app.IBatteryStats;
 
@@ -129,6 +130,8 @@
     private final BluetoothProfileState mHfpProfileState;
 
     private BluetoothA2dpService mA2dpService;
+    private final HashMap<String, Pair<byte[], byte[]>> mDeviceOobData;
+
     private static String mDockAddress;
     private String mDockPin;
 
@@ -183,6 +186,7 @@
         mDeviceProperties = new HashMap<String, Map<String,String>>();
 
         mDeviceServiceChannelCache = new HashMap<String, Map<ParcelUuid, Integer>>();
+        mDeviceOobData = new HashMap<String, Pair<byte[], byte[]>>();
         mUuidIntentTracker = new ArrayList<String>();
         mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>();
         mServiceRecordToPid = new HashMap<Integer, Integer>();
@@ -1119,7 +1123,7 @@
         mIsDiscovering = isDiscovering;
     }
 
-    public synchronized boolean createBond(String address) {
+    private boolean isBondingFeasible(String address) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                 "Need BLUETOOTH_ADMIN permission");
         if (!isEnabledInternal()) return false;
@@ -1149,8 +1153,13 @@
                 return false;
             }
         }
+        return true;
+    }
 
-        if (!createPairedDeviceNative(address, 60000 /* 1 minute */)) {
+    public synchronized boolean createBond(String address) {
+        if (!isBondingFeasible(address)) return false;
+
+        if (!createPairedDeviceNative(address, 60000  /*1 minute*/ )) {
             return false;
         }
 
@@ -1160,6 +1169,51 @@
         return true;
     }
 
+    public synchronized boolean createBondOutOfBand(String address, byte[] hash,
+                                                    byte[] randomizer) {
+        if (!isBondingFeasible(address)) return false;
+
+        if (!createPairedDeviceOutOfBandNative(address, 60000 /* 1 minute */)) {
+            return false;
+        }
+
+        setDeviceOutOfBandData(address, hash, randomizer);
+        mBondState.setPendingOutgoingBonding(address);
+        mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
+
+        return true;
+    }
+
+    public synchronized boolean setDeviceOutOfBandData(String address, byte[] hash,
+            byte[] randomizer) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+                                                "Need BLUETOOTH_ADMIN permission");
+        if (!isEnabledInternal()) return false;
+
+        Pair <byte[], byte[]> value = new Pair<byte[], byte[]>(hash, randomizer);
+
+        if (DBG) {
+            log("Setting out of band data for:" + address + ":" +
+              Arrays.toString(hash) + ":" + Arrays.toString(randomizer));
+        }
+
+        mDeviceOobData.put(address, value);
+        return true;
+    }
+
+    Pair<byte[], byte[]> getDeviceOutOfBandData(BluetoothDevice device) {
+        return mDeviceOobData.get(device.getAddress());
+    }
+
+
+    public synchronized byte[] readOutOfBandData() {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
+                                                "Need BLUETOOTH permission");
+        if (!isEnabledInternal()) return null;
+
+        return readAdapterOutOfBandDataNative();
+    }
+
     public synchronized boolean cancelBondProcess(String address) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                 "Need BLUETOOTH_ADMIN permission");
@@ -1551,6 +1605,32 @@
         return setPairingConfirmationNative(address, confirm, data.intValue());
     }
 
+    public synchronized boolean setRemoteOutOfBandData(String address) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
+                                                "Need BLUETOOTH_ADMIN permission");
+        if (!isEnabledInternal()) return false;
+        address = address.toUpperCase();
+        Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
+        if (data == null) {
+            Log.w(TAG, "setRemoteOobData(" + address + ") called but no native data available, " +
+                  "ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
+                  " or by bluez.\n");
+            return false;
+        }
+
+        Pair<byte[], byte[]> val = mDeviceOobData.get(address);
+        byte[] hash, randomizer;
+        if (val == null) {
+            // TODO: check what should be passed in this case.
+            hash = new byte[16];
+            randomizer = new byte[16];
+        } else {
+            hash = val.first;
+            randomizer = val.second;
+        }
+        return setRemoteOutOfBandDataNative(address, hash, randomizer, data.intValue());
+    }
+
     public synchronized boolean cancelPairingUserInput(String address) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
                                                 "Need BLUETOOTH_ADMIN permission");
@@ -2084,6 +2164,9 @@
     private native boolean stopDiscoveryNative();
 
     private native boolean createPairedDeviceNative(String address, int timeout_ms);
+    private native boolean createPairedDeviceOutOfBandNative(String address, int timeout_ms);
+    private native byte[] readAdapterOutOfBandDataNative();
+
     private native boolean cancelDeviceCreationNative(String address);
     private native boolean removeDeviceNative(String objectPath);
     private native int getDeviceServiceChannelNative(String objectPath, String uuid,
@@ -2094,6 +2177,9 @@
     private native boolean setPasskeyNative(String address, int passkey, int nativeData);
     private native boolean setPairingConfirmationNative(String address, boolean confirm,
             int nativeData);
+    private native boolean setRemoteOutOfBandDataNative(String address, byte[] hash,
+                                                        byte[] randomizer, int nativeData);
+
     private native boolean setDevicePropertyBooleanNative(String objectPath, String key,
             int value);
     private native boolean createDeviceNative(String address);
diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp
index db45d6d..d8e049d 100644
--- a/core/jni/android_server_BluetoothEventLoop.cpp
+++ b/core/jni/android_server_BluetoothEventLoop.cpp
@@ -61,6 +61,8 @@
 static jmethodID method_onRequestPasskeyConfirmation;
 static jmethodID method_onRequestPairingConsent;
 static jmethodID method_onDisplayPasskey;
+static jmethodID method_onRequestOobData;
+static jmethodID method_onAgentOutOfBandDataAvailable;
 static jmethodID method_onAgentAuthorize;
 static jmethodID method_onAgentCancel;
 
@@ -105,6 +107,8 @@
 
     method_onAgentAuthorize = env->GetMethodID(clazz, "onAgentAuthorize",
                                                "(Ljava/lang/String;Ljava/lang/String;)Z");
+    method_onAgentOutOfBandDataAvailable = env->GetMethodID(clazz, "onAgentOutOfBandDataAvailable",
+                                               "(Ljava/lang/String;)Z");
     method_onAgentCancel = env->GetMethodID(clazz, "onAgentCancel", "()V");
     method_onRequestPinCode = env->GetMethodID(clazz, "onRequestPinCode",
                                                "(Ljava/lang/String;I)V");
@@ -116,6 +120,8 @@
                                                "(Ljava/lang/String;I)V");
     method_onDisplayPasskey = env->GetMethodID(clazz, "onDisplayPasskey",
                                                "(Ljava/lang/String;II)V");
+    method_onRequestOobData = env->GetMethodID(clazz, "onRequestOobData",
+                                               "(Ljava/lang/String;I)V");
 
     field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I");
 #endif
@@ -305,6 +311,7 @@
 {
     DBusMessage *msg, *reply;
     DBusError err;
+    bool oob = TRUE;
 
     if (!dbus_connection_register_object_path(nat->conn, agent_path,
             &agent_vtable, nat)) {
@@ -326,6 +333,7 @@
     }
     dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path,
                              DBUS_TYPE_STRING, &capabilities,
+                             DBUS_TYPE_BOOLEAN, &oob,
                              DBUS_TYPE_INVALID);
 
     dbus_error_init(&err);
@@ -934,6 +942,43 @@
         }
         goto success;
     } else if (dbus_message_is_method_call(msg,
+            "org.bluez.Agent", "OutOfBandAvailable")) {
+        char *object_path;
+        if (!dbus_message_get_args(msg, NULL,
+                                   DBUS_TYPE_OBJECT_PATH, &object_path,
+                                   DBUS_TYPE_INVALID)) {
+            LOGE("%s: Invalid arguments for OutOfBandData available() method", __FUNCTION__);
+            goto failure;
+        }
+
+        LOGV("... object_path = %s", object_path);
+
+        bool available =
+            env->CallBooleanMethod(nat->me, method_onAgentOutOfBandDataAvailable,
+                env->NewStringUTF(object_path));
+
+
+        // reply
+        if (available) {
+            DBusMessage *reply = dbus_message_new_method_return(msg);
+            if (!reply) {
+                LOGE("%s: Cannot create message reply\n", __FUNCTION__);
+                goto failure;
+            }
+            dbus_connection_send(nat->conn, reply, NULL);
+            dbus_message_unref(reply);
+        } else {
+            DBusMessage *reply = dbus_message_new_error(msg,
+                    "org.bluez.Error.DoesNotExist", "OutofBand data not available");
+            if (!reply) {
+                LOGE("%s: Cannot create message reply\n", __FUNCTION__);
+                goto failure;
+            }
+            dbus_connection_send(nat->conn, reply, NULL);
+            dbus_message_unref(reply);
+        }
+        goto success;
+    } else if (dbus_message_is_method_call(msg,
             "org.bluez.Agent", "RequestPinCode")) {
         char *object_path;
         if (!dbus_message_get_args(msg, NULL,
@@ -964,6 +1009,21 @@
                                        int(msg));
         goto success;
     } else if (dbus_message_is_method_call(msg,
+            "org.bluez.Agent", "RequestOobData")) {
+        char *object_path;
+        if (!dbus_message_get_args(msg, NULL,
+                                   DBUS_TYPE_OBJECT_PATH, &object_path,
+                                   DBUS_TYPE_INVALID)) {
+            LOGE("%s: Invalid arguments for RequestOobData() method", __FUNCTION__);
+            goto failure;
+        }
+
+        dbus_message_ref(msg);  // increment refcount because we pass to java
+        env->CallVoidMethod(nat->me, method_onRequestOobData,
+                                       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;
diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp
index 4420aca..daa59a6 100644
--- a/core/jni/android_server_BluetoothService.cpp
+++ b/core/jni/android_server_BluetoothService.cpp
@@ -288,6 +288,46 @@
 #endif
 }
 
+static jbyteArray readAdapterOutOfBandDataNative(JNIEnv *env, jobject object) {
+    LOGV(__FUNCTION__);
+#ifdef HAVE_BLUETOOTH
+    native_data_t *nat = get_native_data(env, object);
+    DBusError err;
+    jbyte *hash, *randomizer;
+    jbyteArray byteArray = NULL;
+    int hash_len, r_len;
+    if (nat) {
+       DBusMessage *reply = dbus_func_args(env, nat->conn,
+                           get_adapter_path(env, object),
+                           DBUS_ADAPTER_IFACE, "ReadLocalOutOfBandData",
+                           DBUS_TYPE_INVALID);
+       if (!reply) return NULL;
+
+       dbus_error_init(&err);
+       if (dbus_message_get_args(reply, &err,
+                                DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash, &hash_len,
+                                DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &randomizer, &r_len,
+                                DBUS_TYPE_INVALID)) {
+          if (hash_len == 16 && r_len == 16) {
+               byteArray = env->NewByteArray(32);
+               if (byteArray) {
+                   env->SetByteArrayRegion(byteArray, 0, 16, hash);
+                   env->SetByteArrayRegion(byteArray, 16, 16, randomizer);
+               }
+           } else {
+               LOGE("readAdapterOutOfBandDataNative: Hash len = %d, R len = %d",
+                                                                  hash_len, r_len);
+           }
+       } else {
+          LOG_AND_FREE_DBUS_ERROR(&err);
+       }
+       dbus_message_unref(reply);
+       return byteArray;
+    }
+#endif
+    return NULL;
+}
+
 static jboolean createPairedDeviceNative(JNIEnv *env, jobject object,
                                          jstring address, jint timeout_ms) {
     LOGV(__FUNCTION__);
@@ -324,6 +364,41 @@
     return JNI_FALSE;
 }
 
+static jboolean createPairedDeviceOutOfBandNative(JNIEnv *env, jobject object,
+                                                jstring address, jint timeout_ms) {
+    LOGV(__FUNCTION__);
+#ifdef HAVE_BLUETOOTH
+    native_data_t *nat = get_native_data(env, object);
+    jobject eventLoop = env->GetObjectField(object, field_mEventLoop);
+    struct event_loop_native_data_t *eventLoopNat =
+            get_EventLoop_native_data(env, eventLoop);
+
+    if (nat && eventLoopNat) {
+        const char *c_address = env->GetStringUTFChars(address, NULL);
+        LOGV("... address = %s", c_address);
+        char *context_address = (char *)calloc(BTADDR_SIZE, sizeof(char));
+        const char *capabilities = "DisplayYesNo";
+        const char *agent_path = "/android/bluetooth/remote_device_agent";
+
+        strlcpy(context_address, c_address, BTADDR_SIZE);  // for callback
+        bool ret = dbus_func_args_async(env, nat->conn, (int)timeout_ms,
+                                        onCreatePairedDeviceResult, // callback
+                                        context_address,
+                                        eventLoopNat,
+                                        get_adapter_path(env, object),
+                                        DBUS_ADAPTER_IFACE,
+                                        "CreatePairedDeviceOutOfBand",
+                                        DBUS_TYPE_STRING, &c_address,
+                                        DBUS_TYPE_OBJECT_PATH, &agent_path,
+                                        DBUS_TYPE_STRING, &capabilities,
+                                        DBUS_TYPE_INVALID);
+        env->ReleaseStringUTFChars(address, c_address);
+        return ret ? JNI_TRUE : JNI_FALSE;
+    }
+#endif
+    return JNI_FALSE;
+}
+
 static jint getDeviceServiceChannelNative(JNIEnv *env, jobject object,
                                           jstring path,
                                           jstring pattern, jint attr_id) {
@@ -490,6 +565,40 @@
     return JNI_FALSE;
 }
 
+static jboolean setRemoteOutOfBandDataNative(JNIEnv *env, jobject object, jstring address,
+                         jbyteArray hash, jbyteArray randomizer, int nativeData) {
+#ifdef HAVE_BLUETOOTH
+    LOGV(__FUNCTION__);
+    native_data_t *nat = get_native_data(env, object);
+    if (nat) {
+        DBusMessage *msg = (DBusMessage *)nativeData;
+        DBusMessage *reply = dbus_message_new_method_return(msg);
+        jbyte *h_ptr = env->GetByteArrayElements(hash, NULL);
+        jbyte *r_ptr = env->GetByteArrayElements(randomizer, NULL);
+        if (!reply) {
+            LOGE("%s: Cannot create message reply to return remote OOB data to "
+                 "D-Bus\n", __FUNCTION__);
+            dbus_message_unref(msg);
+            return JNI_FALSE;
+        }
+
+        dbus_message_append_args(reply,
+                                DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &h_ptr, 16,
+                                DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &r_ptr, 16,
+                                DBUS_TYPE_INVALID);
+
+        env->ReleaseByteArrayElements(hash, h_ptr, 0);
+        env->ReleaseByteArrayElements(randomizer, r_ptr, 0);
+
+        dbus_connection_send(nat->conn, reply, NULL);
+        dbus_message_unref(msg);
+        dbus_message_unref(reply);
+        return JNI_TRUE;
+    }
+#endif
+    return JNI_FALSE;
+}
+
 static jboolean setPinNative(JNIEnv *env, jobject object, jstring address,
                          jstring pin, int nativeData) {
 #ifdef HAVE_BLUETOOTH
@@ -907,7 +1016,10 @@
     {"startDiscoveryNative", "()Z", (void*)startDiscoveryNative},
     {"stopDiscoveryNative", "()Z", (void *)stopDiscoveryNative},
 
+    {"readAdapterOutOfBandDataNative", "()[B", (void *)readAdapterOutOfBandDataNative},
     {"createPairedDeviceNative", "(Ljava/lang/String;I)Z", (void *)createPairedDeviceNative},
+    {"createPairedDeviceOutOfBandNative", "(Ljava/lang/String;I)Z",
+                                    (void *)createPairedDeviceOutOfBandNative},
     {"cancelDeviceCreationNative", "(Ljava/lang/String;)Z", (void *)cancelDeviceCreationNative},
     {"removeDeviceNative", "(Ljava/lang/String;)Z", (void *)removeDeviceNative},
     {"getDeviceServiceChannelNative", "(Ljava/lang/String;Ljava/lang/String;I)I",
@@ -916,6 +1028,7 @@
     {"setPairingConfirmationNative", "(Ljava/lang/String;ZI)Z",
             (void *)setPairingConfirmationNative},
     {"setPasskeyNative", "(Ljava/lang/String;II)Z", (void *)setPasskeyNative},
+    {"setRemoteOutOfBandDataNative", "(Ljava/lang/String;[B[BI)Z", (void *)setRemoteOutOfBandDataNative},
     {"setPinNative", "(Ljava/lang/String;Ljava/lang/String;I)Z", (void *)setPinNative},
     {"cancelPairingUserInputNative", "(Ljava/lang/String;I)Z",
             (void *)cancelPairingUserInputNative},