summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJaikumar Ganesh <jaikumar@google.com>2010-09-09 15:37:57 -0700
committerJaikumar Ganesh <jaikumar@google.com>2010-09-13 11:40:21 -0700
commitcc5494c9996f809e36539b24e8b6b67683383d29 (patch)
tree5d4109c77cc1696d4665659a17b4458062fd7d0c
parentfbd2646705c52c111f041b16d7cb34cb922a2a64 (diff)
downloadframeworks_base-cc5494c9996f809e36539b24e8b6b67683383d29.zip
frameworks_base-cc5494c9996f809e36539b24e8b6b67683383d29.tar.gz
frameworks_base-cc5494c9996f809e36539b24e8b6b67683383d29.tar.bz2
Out Of Band API for Secure Simple Pairing.
Change-Id: I54ded27ab85d46eef3d2cca84f2394b1ffe88ced
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java33
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.java58
-rw-r--r--core/java/android/bluetooth/IBluetooth.aidl4
-rw-r--r--core/java/android/server/BluetoothEventLoop.java27
-rw-r--r--core/java/android/server/BluetoothService.java90
-rw-r--r--core/jni/android_server_BluetoothEventLoop.cpp60
-rw-r--r--core/jni/android_server_BluetoothService.cpp113
7 files changed, 381 insertions, 4 deletions
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.ParcelUuid;
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 @@ public final class BluetoothAdapter {
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 @@ public final class BluetoothDevice implements Parcelable {
/** 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 @@ public final class BluetoothDevice implements Parcelable {
}
/**
+ * 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 @@ public final class BluetoothDevice implements Parcelable {
}
/** @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 @@ interface IBluetooth
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 @@ interface IBluetooth
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 @@ class BluetoothEventLoop {
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 @@ class BluetoothEventLoop {
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.ServiceManager;
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 @@ public class BluetoothService extends IBluetooth.Stub {
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 @@ public class BluetoothService extends IBluetooth.Stub {
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 @@ public class BluetoothService extends IBluetooth.Stub {
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 @@ public class BluetoothService extends IBluetooth.Stub {
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 @@ public class BluetoothService extends IBluetooth.Stub {
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 @@ public class BluetoothService extends IBluetooth.Stub {
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 @@ public class BluetoothService extends IBluetooth.Stub {
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 @@ public class BluetoothService extends IBluetooth.Stub {
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_onRequestPasskey;
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 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
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 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
"(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 @@ static int register_agent(native_data_t *nat,
{
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 @@ static int register_agent(native_data_t *nat,
}
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 @@ DBusHandlerResult agent_event_filter(DBusConnection *conn,
}
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 @@ DBusHandlerResult agent_event_filter(DBusConnection *conn,
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 @@ done:
#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 @@ static jboolean createPairedDeviceNative(JNIEnv *env, jobject object,
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 @@ static jboolean setPasskeyNative(JNIEnv *env, jobject object, jstring address,
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 @@ static JNINativeMethod sMethods[] = {
{"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 @@ static JNINativeMethod sMethods[] = {
{"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},