Loading android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp +229 −78 Original line number Diff line number Diff line Loading @@ -38,11 +38,24 @@ using bluetooth::Uuid; namespace android { // OOB_LE_BD_ADDR_SIZE is 6 bytes addres + 1 byte address type #define OOB_LE_BD_ADDR_SIZE 7 // Both // OOB_ADDRESS_SIZE is 6 bytes address + 1 byte address type #define OOB_ADDRESS_SIZE 7 #define OOB_C_SIZE 16 #define OOB_R_SIZE 16 #define OOB_NAME_MAX_SIZE 256 // Classic #define OOB_DATA_LEN_SIZE 2 #define OOB_COD_SIZE 3 // LE #define OOB_TK_SIZE 16 #define OOB_LE_SC_C_SIZE 16 #define OOB_LE_SC_R_SIZE 16 #define OOB_LE_FLAG_SIZE 1 #define OOB_LE_ROLE_SIZE 1 #define OOB_LE_APPEARANCE_SIZE 2 #define TRANSPORT_AUTO 0 #define TRANSPORT_BREDR 1 #define TRANSPORT_LE 2 const jint INVALID_FD = -1; Loading Loading @@ -857,106 +870,243 @@ static jbyteArray callByteArrayGetter(JNIEnv* env, jobject object, return (jbyteArray)env->CallObjectMethod(object, myMethod); } static jboolean createBondOutOfBandNative(JNIEnv* env, jobject obj, jbyteArray address, jint transport, jobject oobData) { bt_out_of_band_data_t oob_data; static jint callIntGetter(JNIEnv* env, jobject object, const char* className, const char* methodName) { jclass myClass = env->FindClass(className); jmethodID myMethod = env->GetMethodID(myClass, methodName, "()I"); return env->CallIntMethod(object, myMethod); } static jboolean set_data(JNIEnv* env, bt_oob_data_t& oob_data, jobject oobData, jint transport) { // Need both arguments to be non NULL if (oobData == NULL) { ALOGE("%s: oobData is null! Nothing to do.", __func__); return JNI_FALSE; } memset(&oob_data, 0, sizeof(oob_data)); if (!sBluetoothInterface) return JNI_FALSE; jbyteArray address = callByteArrayGetter( env, oobData, "android/bluetooth/OobData", "getDeviceAddressWithType"); jbyte* addr = env->GetByteArrayElements(address, NULL); if (addr == NULL) { // Check the data int len = env->GetArrayLength(address); if (len != OOB_ADDRESS_SIZE) { ALOGE("%s: addressBytes must be 7 bytes in length (address plus type) 6+1!", __func__); jniThrowIOException(env, EINVAL); return JNI_FALSE; } jbyte* leBtDeviceAddressBytes = NULL; jbyte* smTKBytes = NULL; jbyte* leScCBytes = NULL; jbyte* leScRBytes = NULL; jbyteArray leBtDeviceAddress = NULL; jbyteArray smTK = NULL; jbyteArray leScC = NULL; jbyteArray leScR = NULL; int status = BT_STATUS_FAIL; leBtDeviceAddress = callByteArrayGetter( env, oobData, "android/bluetooth/OobData", "getLeBluetoothDeviceAddress"); if (leBtDeviceAddress != NULL) { leBtDeviceAddressBytes = env->GetByteArrayElements(leBtDeviceAddress, NULL); int len = env->GetArrayLength(leBtDeviceAddress); if (len != OOB_LE_BD_ADDR_SIZE) { ALOGI( "%s: wrong length of leBtDeviceAddress, should be empty or %d bytes.", __func__, OOB_LE_BD_ADDR_SIZE); // Convert the address from byte[] jbyte* addressBytes = env->GetByteArrayElements(address, NULL); if (addressBytes == NULL) { ALOGE("%s: addressBytes cannot be null!", __func__); jniThrowIOException(env, EINVAL); goto done; } memcpy(oob_data.le_bt_dev_addr, leBtDeviceAddressBytes, len); return JNI_FALSE; } memcpy(oob_data.address, addressBytes, len); smTK = callByteArrayGetter(env, oobData, "android/bluetooth/OobData", "getSecurityManagerTk"); if (smTK != NULL) { smTKBytes = env->GetByteArrayElements(smTK, NULL); int len = env->GetArrayLength(smTK); if (len != OOB_TK_SIZE) { ALOGI("%s: wrong length of smTK, should be empty or %d bytes.", __func__, OOB_TK_SIZE); // Get the device name byte[] java object jbyteArray deviceName = callByteArrayGetter( env, oobData, "android/bluetooth/OobData", "getDeviceName"); // Optional // Convert it to a jbyte* and copy it to the struct jbyte* deviceNameBytes = NULL; if (deviceName != NULL) { deviceNameBytes = env->GetByteArrayElements(deviceName, NULL); int len = env->GetArrayLength(deviceName); if (len > OOB_NAME_MAX_SIZE) { ALOGI( "%s: wrong length of deviceName, should be empty or less than or " "equal to %d bytes.", __func__, OOB_NAME_MAX_SIZE); jniThrowIOException(env, EINVAL); goto done; env->ReleaseByteArrayElements(deviceName, deviceNameBytes, 0); return JNI_FALSE; } memcpy(oob_data.sm_tk, smTKBytes, len); memcpy(oob_data.device_name, deviceNameBytes, len); env->ReleaseByteArrayElements(deviceName, deviceNameBytes, 0); } // Used by both classic and LE jbyteArray confirmation = callByteArrayGetter( env, oobData, "android/bluetooth/OobData", "getConfirmationHash"); if (confirmation == NULL) { ALOGE("%s: confirmation cannot be null!", __func__); jniThrowIOException(env, EINVAL); return JNI_FALSE; } leScC = callByteArrayGetter(env, oobData, "android/bluetooth/OobData", "getLeSecureConnectionsConfirmation"); if (leScC != NULL) { leScCBytes = env->GetByteArrayElements(leScC, NULL); int len = env->GetArrayLength(leScC); if (len != OOB_LE_SC_C_SIZE) { // Confirmation is mandatory jbyte* confirmationBytes = NULL; confirmationBytes = env->GetByteArrayElements(confirmation, NULL); len = env->GetArrayLength(confirmation); if (confirmationBytes == NULL || len != OOB_C_SIZE) { ALOGI( "%s: wrong length of LE SC Confirmation, should be empty or %d " "%s: wrong length of Confirmation, should be empty or %d " "bytes.", __func__, OOB_LE_SC_C_SIZE); __func__, OOB_C_SIZE); jniThrowIOException(env, EINVAL); goto done; env->ReleaseByteArrayElements(confirmation, confirmationBytes, 0); return JNI_FALSE; } memcpy(oob_data.le_sc_c, leScCBytes, len); memcpy(oob_data.c, confirmationBytes, len); env->ReleaseByteArrayElements(confirmation, confirmationBytes, 0); // Random is supposedly optional according to the specification jbyteArray randomizer = callByteArrayGetter( env, oobData, "android/bluetooth/OobData", "getRandomizerHash"); jbyte* randomizerBytes = NULL; if (randomizer != NULL) { randomizerBytes = env->GetByteArrayElements(randomizer, NULL); int len = env->GetArrayLength(randomizer); if (randomizerBytes == NULL || len != OOB_R_SIZE) { ALOGI("%s: wrong length of Random, should be empty or %d bytes.", __func__, OOB_R_SIZE); jniThrowIOException(env, EINVAL); env->ReleaseByteArrayElements(randomizer, randomizerBytes, 0); return JNI_FALSE; } memcpy(oob_data.r, randomizerBytes, len); env->ReleaseByteArrayElements(randomizer, randomizerBytes, 0); } // Transport specific data fetching/setting if (transport == TRANSPORT_BREDR) { // Classic // Not optional jbyteArray oobDataLength = callByteArrayGetter( env, oobData, "android/bluetooth/OobData", "getClassicLength"); jbyte* oobDataLengthBytes = NULL; if (oobDataLength == NULL || env->GetArrayLength(oobDataLength) != OOB_DATA_LEN_SIZE) { ALOGI("%s: wrong length of oobDataLength, should be empty or %d bytes.", __func__, OOB_DATA_LEN_SIZE); jniThrowIOException(env, EINVAL); env->ReleaseByteArrayElements(oobDataLength, oobDataLengthBytes, 0); return JNI_FALSE; } leScR = callByteArrayGetter(env, oobData, "android/bluetooth/OobData", "getLeSecureConnectionsRandom"); if (leScR != NULL) { leScRBytes = env->GetByteArrayElements(leScR, NULL); int len = env->GetArrayLength(leScR); if (len != OOB_LE_SC_R_SIZE) { ALOGI("%s: wrong length of LE SC Random, should be empty or %d bytes.", __func__, OOB_LE_SC_R_SIZE); oobDataLengthBytes = env->GetByteArrayElements(oobDataLength, NULL); memcpy(oob_data.oob_data_length, oobDataLengthBytes, len); env->ReleaseByteArrayElements(oobDataLength, oobDataLengthBytes, 0); // Optional jbyteArray classOfDevice = callByteArrayGetter( env, oobData, "android/bluetooth/OobData", "getClassOfDevice"); jbyte* classOfDeviceBytes = NULL; if (classOfDevice != NULL) { classOfDeviceBytes = env->GetByteArrayElements(classOfDevice, NULL); int len = env->GetArrayLength(classOfDevice); if (len != OOB_COD_SIZE) { ALOGI("%s: wrong length of classOfDevice, should be empty or %d bytes.", __func__, OOB_COD_SIZE); jniThrowIOException(env, EINVAL); goto done; env->ReleaseByteArrayElements(classOfDevice, classOfDeviceBytes, 0); return JNI_FALSE; } memcpy(oob_data.le_sc_r, leScRBytes, len); memcpy(oob_data.class_of_device, classOfDeviceBytes, len); env->ReleaseByteArrayElements(classOfDevice, classOfDeviceBytes, 0); } } else if (transport == TRANSPORT_LE) { // LE jbyteArray temporaryKey = callByteArrayGetter( env, oobData, "android/bluetooth/OobData", "getLeTemporaryKey"); jbyte* temporaryKeyBytes = NULL; if (temporaryKey != NULL) { temporaryKeyBytes = env->GetByteArrayElements(temporaryKey, NULL); int len = env->GetArrayLength(temporaryKey); if (len != OOB_TK_SIZE) { ALOGI("%s: wrong length of temporaryKey, should be empty or %d bytes.", __func__, OOB_TK_SIZE); jniThrowIOException(env, EINVAL); env->ReleaseByteArrayElements(temporaryKey, temporaryKeyBytes, 0); return JNI_FALSE; } memcpy(oob_data.sm_tk, temporaryKeyBytes, len); env->ReleaseByteArrayElements(temporaryKey, temporaryKeyBytes, 0); } status = sBluetoothInterface->create_bond_out_of_band((RawAddress*)addr, transport, &oob_data); jbyteArray leAppearance = callByteArrayGetter( env, oobData, "android/bluetooth/OobData", "getLeAppearance"); jbyte* leAppearanceBytes = NULL; if (leAppearance != NULL) { leAppearanceBytes = env->GetByteArrayElements(leAppearance, NULL); int len = env->GetArrayLength(leAppearance); if (len != OOB_LE_ROLE_SIZE) { ALOGI("%s: wrong length of leAppearance, should be empty or %d bytes.", __func__, OOB_LE_APPEARANCE_SIZE); jniThrowIOException(env, EINVAL); env->ReleaseByteArrayElements(leAppearance, leAppearanceBytes, 0); return JNI_FALSE; } memcpy(oob_data.le_appearance, leAppearanceBytes, len); env->ReleaseByteArrayElements(leAppearance, leAppearanceBytes, 0); } done: env->ReleaseByteArrayElements(address, addr, 0); jint leRole = callIntGetter(env, oobData, "android/bluetooth/OobData", "getLeDeviceRole"); oob_data.le_device_role = leRole; jint leFlag = callIntGetter(env, oobData, "android/bluetooth/OobData", "getLeFlag"); oob_data.le_flags = leFlag; } return JNI_TRUE; } static jboolean createBondOutOfBandNative(JNIEnv* env, jobject obj, jbyteArray address, jint transport, jobject p192Data, jobject p256Data) { // No BT interface? Can't do anything. if (!sBluetoothInterface) return JNI_FALSE; if (leBtDeviceAddress != NULL) env->ReleaseByteArrayElements(leBtDeviceAddress, leBtDeviceAddressBytes, 0); // No data? Can't do anything if (p192Data == NULL && p256Data == NULL) { ALOGE("%s: All OOB Data are null! Nothing to do.", __func__); jniThrowIOException(env, EINVAL); return JNI_FALSE; } if (smTK != NULL) env->ReleaseByteArrayElements(smTK, smTKBytes, 0); // This address is already reversed which is why its being passed... // In the future we want to remove this and just reverse the address // for the oobdata in the host stack. if (address == NULL) { ALOGE("%s: Address cannot be null! Nothing to do.", __func__); jniThrowIOException(env, EINVAL); return JNI_FALSE; } if (leScC != NULL) env->ReleaseByteArrayElements(leScC, leScCBytes, 0); // Check the data int len = env->GetArrayLength(address); if (len != 6) { ALOGE("%s: addressBytes must be 6 bytes in length (address plus type) 6+1!", __func__); jniThrowIOException(env, EINVAL); return JNI_FALSE; } if (leScR != NULL) env->ReleaseByteArrayElements(leScR, leScRBytes, 0); // Convert P192 data from Java POJO to C Struct bt_oob_data_t p192_data; if (set_data(env, p192_data, p192Data, transport) == JNI_FALSE) { jniThrowIOException(env, EINVAL); return JNI_FALSE; } // Convert P256 data from Java POJO to C Struct bt_oob_data_t p256_data; if (set_data(env, p256_data, p256Data, transport) == JNI_FALSE) { jniThrowIOException(env, EINVAL); return JNI_FALSE; } return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; return ((sBluetoothInterface->create_bond_out_of_band( (RawAddress*)address, transport, &p192_data, &p256_data)) == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean removeBondNative(JNIEnv* env, jobject obj, jbyteArray address) { Loading Loading @@ -1381,7 +1531,8 @@ static JNINativeMethod sMethods[] = { {"startDiscoveryNative", "()Z", (void*)startDiscoveryNative}, {"cancelDiscoveryNative", "()Z", (void*)cancelDiscoveryNative}, {"createBondNative", "([BI)Z", (void*)createBondNative}, {"createBondOutOfBandNative", "([BILandroid/bluetooth/OobData;)Z", {"createBondOutOfBandNative", "([BILandroid/bluetooth/OobData;Landroid/bluetooth/OobData;)Z", (void*)createBondOutOfBandNative}, {"removeBondNative", "([B)Z", (void*)removeBondNative}, {"cancelBondNative", "([B)Z", (void*)cancelBondNative}, Loading android/app/src/com/android/bluetooth/btservice/AdapterService.java +28 −9 Original line number Diff line number Diff line Loading @@ -1505,15 +1505,24 @@ public class AdapterService extends Service { } @Override public boolean createBond(BluetoothDevice device, int transport, OobData oobData) { public boolean createBond(BluetoothDevice device, int transport, OobData remoteP192Data, OobData remoteP256Data) { AdapterService service = getService(); if (service == null || !callerIsSystemOrActiveOrManagedUser(service, TAG, "createBond")) { return false; } // This conditional is required to satisfy permission dependencies // since createBond calls createBondOutOfBand with null value passed as data. // BluetoothDevice#createBond requires BLUETOOTH_ADMIN only. if (remoteP192Data == null && remoteP256Data == null) { enforceBluetoothAdminPermission(service); } else { // createBondOutOfBand() is a @SystemApi, this requires PRIVILEGED. enforceBluetoothPrivilegedPermission(service); } return service.createBond(device, transport, oobData); return service.createBond(device, transport, remoteP192Data, remoteP256Data); } @Override Loading Loading @@ -2400,7 +2409,8 @@ public class AdapterService extends Service { return mDatabaseManager; } boolean createBond(BluetoothDevice device, int transport, OobData oobData) { boolean createBond(BluetoothDevice device, int transport, OobData remoteP192Data, OobData remoteP256Data) { DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); if (deviceProp != null && deviceProp.getBondState() != BluetoothDevice.BOND_NONE) { return false; Loading @@ -2416,10 +2426,18 @@ public class AdapterService extends Service { msg.obj = device; msg.arg1 = transport; if (oobData != null) { Bundle oobDataBundle = new Bundle(); oobDataBundle.putParcelable(BondStateMachine.OOBDATA, oobData); msg.setData(oobDataBundle); Bundle remoteOobDatasBundle = new Bundle(); boolean setData = false; if (remoteP192Data != null) { remoteOobDatasBundle.putParcelable(BondStateMachine.OOBDATAP192, remoteP192Data); setData = true; } if (remoteP256Data != null) { remoteOobDatasBundle.putParcelable(BondStateMachine.OOBDATAP256, remoteP256Data); setData = true; } if (setData) { msg.setData(remoteOobDatasBundle); } mBondStateMachine.sendMessage(msg); return true; Loading Loading @@ -3437,7 +3455,8 @@ public class AdapterService extends Service { public native boolean createBondNative(byte[] address, int transport); /*package*/ native boolean createBondOutOfBandNative(byte[] address, int transport, OobData oobData); native boolean createBondOutOfBandNative(byte[] address, int transport, OobData p192Data, OobData p256Data); /*package*/ public native boolean removeBondNative(byte[] address); Loading android/app/src/com/android/bluetooth/btservice/BondStateMachine.java +21 −18 Original line number Diff line number Diff line Loading @@ -76,7 +76,8 @@ final class BondStateMachine extends StateMachine { private PendingCommandState mPendingCommandState = new PendingCommandState(); private StableState mStableState = new StableState(); public static final String OOBDATA = "oobdata"; public static final String OOBDATAP192 = "oobdatap192"; public static final String OOBDATAP256 = "oobdatap256"; @VisibleForTesting Set<BluetoothDevice> mPendingBondedDevices = new HashSet<>(); Loading Loading @@ -129,12 +130,11 @@ final class BondStateMachine extends StateMachine { switch (msg.what) { case CREATE_BOND: OobData oobData = null; if (msg.getData() != null) { oobData = msg.getData().getParcelable(OOBDATA); } createBond(dev, msg.arg1, oobData, true); OobData p192Data = (msg.getData() != null) ? msg.getData().getParcelable(OOBDATAP192) : null; OobData p256Data = (msg.getData() != null) ? msg.getData().getParcelable(OOBDATAP256) : null; createBond(dev, msg.arg1, p192Data, p256Data, true); break; case REMOVE_BOND: removeBond(dev, true); Loading Loading @@ -191,12 +191,11 @@ final class BondStateMachine extends StateMachine { switch (msg.what) { case CREATE_BOND: OobData oobData = null; if (msg.getData() != null) { oobData = msg.getData().getParcelable(OOBDATA); } result = createBond(dev, msg.arg1, oobData, false); OobData p192Data = (msg.getData() != null) ? msg.getData().getParcelable(OOBDATAP192) : null; OobData p256Data = (msg.getData() != null) ? msg.getData().getParcelable(OOBDATAP256) : null; result = createBond(dev, msg.arg1, p192Data, p256Data, false); break; case REMOVE_BOND: result = removeBond(dev, false); Loading Loading @@ -310,23 +309,27 @@ final class BondStateMachine extends StateMachine { return false; } private boolean createBond(BluetoothDevice dev, int transport, OobData oobData, boolean transition) { private boolean createBond(BluetoothDevice dev, int transport, OobData remoteP192Data, OobData remoteP256Data, boolean transition) { if (dev.getBondState() == BluetoothDevice.BOND_NONE) { infoLog("Bond address is:" + dev); byte[] addr = Utils.getBytesFromAddress(dev.getAddress()); boolean result; if (oobData != null) { result = mAdapterService.createBondOutOfBandNative(addr, transport, oobData); // If we have some data if (remoteP192Data != null || remoteP256Data != null) { result = mAdapterService.createBondOutOfBandNative(addr, transport, remoteP192Data, remoteP256Data); } else { result = mAdapterService.createBondNative(addr, transport); } BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED, mAdapterService.obfuscateAddress(dev), transport, dev.getType(), BluetoothDevice.BOND_BONDING, oobData == null ? BluetoothProtoEnums.BOND_SUB_STATE_UNKNOWN remoteP192Data == null && remoteP256Data == null ? BluetoothProtoEnums.BOND_SUB_STATE_UNKNOWN : BluetoothProtoEnums.BOND_SUB_STATE_LOCAL_OOB_DATA_PROVIDED, BluetoothProtoEnums.UNBOND_REASON_UNKNOWN); if (!result) { BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED, mAdapterService.obfuscateAddress(dev), transport, dev.getType(), Loading Loading
android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp +229 −78 Original line number Diff line number Diff line Loading @@ -38,11 +38,24 @@ using bluetooth::Uuid; namespace android { // OOB_LE_BD_ADDR_SIZE is 6 bytes addres + 1 byte address type #define OOB_LE_BD_ADDR_SIZE 7 // Both // OOB_ADDRESS_SIZE is 6 bytes address + 1 byte address type #define OOB_ADDRESS_SIZE 7 #define OOB_C_SIZE 16 #define OOB_R_SIZE 16 #define OOB_NAME_MAX_SIZE 256 // Classic #define OOB_DATA_LEN_SIZE 2 #define OOB_COD_SIZE 3 // LE #define OOB_TK_SIZE 16 #define OOB_LE_SC_C_SIZE 16 #define OOB_LE_SC_R_SIZE 16 #define OOB_LE_FLAG_SIZE 1 #define OOB_LE_ROLE_SIZE 1 #define OOB_LE_APPEARANCE_SIZE 2 #define TRANSPORT_AUTO 0 #define TRANSPORT_BREDR 1 #define TRANSPORT_LE 2 const jint INVALID_FD = -1; Loading Loading @@ -857,106 +870,243 @@ static jbyteArray callByteArrayGetter(JNIEnv* env, jobject object, return (jbyteArray)env->CallObjectMethod(object, myMethod); } static jboolean createBondOutOfBandNative(JNIEnv* env, jobject obj, jbyteArray address, jint transport, jobject oobData) { bt_out_of_band_data_t oob_data; static jint callIntGetter(JNIEnv* env, jobject object, const char* className, const char* methodName) { jclass myClass = env->FindClass(className); jmethodID myMethod = env->GetMethodID(myClass, methodName, "()I"); return env->CallIntMethod(object, myMethod); } static jboolean set_data(JNIEnv* env, bt_oob_data_t& oob_data, jobject oobData, jint transport) { // Need both arguments to be non NULL if (oobData == NULL) { ALOGE("%s: oobData is null! Nothing to do.", __func__); return JNI_FALSE; } memset(&oob_data, 0, sizeof(oob_data)); if (!sBluetoothInterface) return JNI_FALSE; jbyteArray address = callByteArrayGetter( env, oobData, "android/bluetooth/OobData", "getDeviceAddressWithType"); jbyte* addr = env->GetByteArrayElements(address, NULL); if (addr == NULL) { // Check the data int len = env->GetArrayLength(address); if (len != OOB_ADDRESS_SIZE) { ALOGE("%s: addressBytes must be 7 bytes in length (address plus type) 6+1!", __func__); jniThrowIOException(env, EINVAL); return JNI_FALSE; } jbyte* leBtDeviceAddressBytes = NULL; jbyte* smTKBytes = NULL; jbyte* leScCBytes = NULL; jbyte* leScRBytes = NULL; jbyteArray leBtDeviceAddress = NULL; jbyteArray smTK = NULL; jbyteArray leScC = NULL; jbyteArray leScR = NULL; int status = BT_STATUS_FAIL; leBtDeviceAddress = callByteArrayGetter( env, oobData, "android/bluetooth/OobData", "getLeBluetoothDeviceAddress"); if (leBtDeviceAddress != NULL) { leBtDeviceAddressBytes = env->GetByteArrayElements(leBtDeviceAddress, NULL); int len = env->GetArrayLength(leBtDeviceAddress); if (len != OOB_LE_BD_ADDR_SIZE) { ALOGI( "%s: wrong length of leBtDeviceAddress, should be empty or %d bytes.", __func__, OOB_LE_BD_ADDR_SIZE); // Convert the address from byte[] jbyte* addressBytes = env->GetByteArrayElements(address, NULL); if (addressBytes == NULL) { ALOGE("%s: addressBytes cannot be null!", __func__); jniThrowIOException(env, EINVAL); goto done; } memcpy(oob_data.le_bt_dev_addr, leBtDeviceAddressBytes, len); return JNI_FALSE; } memcpy(oob_data.address, addressBytes, len); smTK = callByteArrayGetter(env, oobData, "android/bluetooth/OobData", "getSecurityManagerTk"); if (smTK != NULL) { smTKBytes = env->GetByteArrayElements(smTK, NULL); int len = env->GetArrayLength(smTK); if (len != OOB_TK_SIZE) { ALOGI("%s: wrong length of smTK, should be empty or %d bytes.", __func__, OOB_TK_SIZE); // Get the device name byte[] java object jbyteArray deviceName = callByteArrayGetter( env, oobData, "android/bluetooth/OobData", "getDeviceName"); // Optional // Convert it to a jbyte* and copy it to the struct jbyte* deviceNameBytes = NULL; if (deviceName != NULL) { deviceNameBytes = env->GetByteArrayElements(deviceName, NULL); int len = env->GetArrayLength(deviceName); if (len > OOB_NAME_MAX_SIZE) { ALOGI( "%s: wrong length of deviceName, should be empty or less than or " "equal to %d bytes.", __func__, OOB_NAME_MAX_SIZE); jniThrowIOException(env, EINVAL); goto done; env->ReleaseByteArrayElements(deviceName, deviceNameBytes, 0); return JNI_FALSE; } memcpy(oob_data.sm_tk, smTKBytes, len); memcpy(oob_data.device_name, deviceNameBytes, len); env->ReleaseByteArrayElements(deviceName, deviceNameBytes, 0); } // Used by both classic and LE jbyteArray confirmation = callByteArrayGetter( env, oobData, "android/bluetooth/OobData", "getConfirmationHash"); if (confirmation == NULL) { ALOGE("%s: confirmation cannot be null!", __func__); jniThrowIOException(env, EINVAL); return JNI_FALSE; } leScC = callByteArrayGetter(env, oobData, "android/bluetooth/OobData", "getLeSecureConnectionsConfirmation"); if (leScC != NULL) { leScCBytes = env->GetByteArrayElements(leScC, NULL); int len = env->GetArrayLength(leScC); if (len != OOB_LE_SC_C_SIZE) { // Confirmation is mandatory jbyte* confirmationBytes = NULL; confirmationBytes = env->GetByteArrayElements(confirmation, NULL); len = env->GetArrayLength(confirmation); if (confirmationBytes == NULL || len != OOB_C_SIZE) { ALOGI( "%s: wrong length of LE SC Confirmation, should be empty or %d " "%s: wrong length of Confirmation, should be empty or %d " "bytes.", __func__, OOB_LE_SC_C_SIZE); __func__, OOB_C_SIZE); jniThrowIOException(env, EINVAL); goto done; env->ReleaseByteArrayElements(confirmation, confirmationBytes, 0); return JNI_FALSE; } memcpy(oob_data.le_sc_c, leScCBytes, len); memcpy(oob_data.c, confirmationBytes, len); env->ReleaseByteArrayElements(confirmation, confirmationBytes, 0); // Random is supposedly optional according to the specification jbyteArray randomizer = callByteArrayGetter( env, oobData, "android/bluetooth/OobData", "getRandomizerHash"); jbyte* randomizerBytes = NULL; if (randomizer != NULL) { randomizerBytes = env->GetByteArrayElements(randomizer, NULL); int len = env->GetArrayLength(randomizer); if (randomizerBytes == NULL || len != OOB_R_SIZE) { ALOGI("%s: wrong length of Random, should be empty or %d bytes.", __func__, OOB_R_SIZE); jniThrowIOException(env, EINVAL); env->ReleaseByteArrayElements(randomizer, randomizerBytes, 0); return JNI_FALSE; } memcpy(oob_data.r, randomizerBytes, len); env->ReleaseByteArrayElements(randomizer, randomizerBytes, 0); } // Transport specific data fetching/setting if (transport == TRANSPORT_BREDR) { // Classic // Not optional jbyteArray oobDataLength = callByteArrayGetter( env, oobData, "android/bluetooth/OobData", "getClassicLength"); jbyte* oobDataLengthBytes = NULL; if (oobDataLength == NULL || env->GetArrayLength(oobDataLength) != OOB_DATA_LEN_SIZE) { ALOGI("%s: wrong length of oobDataLength, should be empty or %d bytes.", __func__, OOB_DATA_LEN_SIZE); jniThrowIOException(env, EINVAL); env->ReleaseByteArrayElements(oobDataLength, oobDataLengthBytes, 0); return JNI_FALSE; } leScR = callByteArrayGetter(env, oobData, "android/bluetooth/OobData", "getLeSecureConnectionsRandom"); if (leScR != NULL) { leScRBytes = env->GetByteArrayElements(leScR, NULL); int len = env->GetArrayLength(leScR); if (len != OOB_LE_SC_R_SIZE) { ALOGI("%s: wrong length of LE SC Random, should be empty or %d bytes.", __func__, OOB_LE_SC_R_SIZE); oobDataLengthBytes = env->GetByteArrayElements(oobDataLength, NULL); memcpy(oob_data.oob_data_length, oobDataLengthBytes, len); env->ReleaseByteArrayElements(oobDataLength, oobDataLengthBytes, 0); // Optional jbyteArray classOfDevice = callByteArrayGetter( env, oobData, "android/bluetooth/OobData", "getClassOfDevice"); jbyte* classOfDeviceBytes = NULL; if (classOfDevice != NULL) { classOfDeviceBytes = env->GetByteArrayElements(classOfDevice, NULL); int len = env->GetArrayLength(classOfDevice); if (len != OOB_COD_SIZE) { ALOGI("%s: wrong length of classOfDevice, should be empty or %d bytes.", __func__, OOB_COD_SIZE); jniThrowIOException(env, EINVAL); goto done; env->ReleaseByteArrayElements(classOfDevice, classOfDeviceBytes, 0); return JNI_FALSE; } memcpy(oob_data.le_sc_r, leScRBytes, len); memcpy(oob_data.class_of_device, classOfDeviceBytes, len); env->ReleaseByteArrayElements(classOfDevice, classOfDeviceBytes, 0); } } else if (transport == TRANSPORT_LE) { // LE jbyteArray temporaryKey = callByteArrayGetter( env, oobData, "android/bluetooth/OobData", "getLeTemporaryKey"); jbyte* temporaryKeyBytes = NULL; if (temporaryKey != NULL) { temporaryKeyBytes = env->GetByteArrayElements(temporaryKey, NULL); int len = env->GetArrayLength(temporaryKey); if (len != OOB_TK_SIZE) { ALOGI("%s: wrong length of temporaryKey, should be empty or %d bytes.", __func__, OOB_TK_SIZE); jniThrowIOException(env, EINVAL); env->ReleaseByteArrayElements(temporaryKey, temporaryKeyBytes, 0); return JNI_FALSE; } memcpy(oob_data.sm_tk, temporaryKeyBytes, len); env->ReleaseByteArrayElements(temporaryKey, temporaryKeyBytes, 0); } status = sBluetoothInterface->create_bond_out_of_band((RawAddress*)addr, transport, &oob_data); jbyteArray leAppearance = callByteArrayGetter( env, oobData, "android/bluetooth/OobData", "getLeAppearance"); jbyte* leAppearanceBytes = NULL; if (leAppearance != NULL) { leAppearanceBytes = env->GetByteArrayElements(leAppearance, NULL); int len = env->GetArrayLength(leAppearance); if (len != OOB_LE_ROLE_SIZE) { ALOGI("%s: wrong length of leAppearance, should be empty or %d bytes.", __func__, OOB_LE_APPEARANCE_SIZE); jniThrowIOException(env, EINVAL); env->ReleaseByteArrayElements(leAppearance, leAppearanceBytes, 0); return JNI_FALSE; } memcpy(oob_data.le_appearance, leAppearanceBytes, len); env->ReleaseByteArrayElements(leAppearance, leAppearanceBytes, 0); } done: env->ReleaseByteArrayElements(address, addr, 0); jint leRole = callIntGetter(env, oobData, "android/bluetooth/OobData", "getLeDeviceRole"); oob_data.le_device_role = leRole; jint leFlag = callIntGetter(env, oobData, "android/bluetooth/OobData", "getLeFlag"); oob_data.le_flags = leFlag; } return JNI_TRUE; } static jboolean createBondOutOfBandNative(JNIEnv* env, jobject obj, jbyteArray address, jint transport, jobject p192Data, jobject p256Data) { // No BT interface? Can't do anything. if (!sBluetoothInterface) return JNI_FALSE; if (leBtDeviceAddress != NULL) env->ReleaseByteArrayElements(leBtDeviceAddress, leBtDeviceAddressBytes, 0); // No data? Can't do anything if (p192Data == NULL && p256Data == NULL) { ALOGE("%s: All OOB Data are null! Nothing to do.", __func__); jniThrowIOException(env, EINVAL); return JNI_FALSE; } if (smTK != NULL) env->ReleaseByteArrayElements(smTK, smTKBytes, 0); // This address is already reversed which is why its being passed... // In the future we want to remove this and just reverse the address // for the oobdata in the host stack. if (address == NULL) { ALOGE("%s: Address cannot be null! Nothing to do.", __func__); jniThrowIOException(env, EINVAL); return JNI_FALSE; } if (leScC != NULL) env->ReleaseByteArrayElements(leScC, leScCBytes, 0); // Check the data int len = env->GetArrayLength(address); if (len != 6) { ALOGE("%s: addressBytes must be 6 bytes in length (address plus type) 6+1!", __func__); jniThrowIOException(env, EINVAL); return JNI_FALSE; } if (leScR != NULL) env->ReleaseByteArrayElements(leScR, leScRBytes, 0); // Convert P192 data from Java POJO to C Struct bt_oob_data_t p192_data; if (set_data(env, p192_data, p192Data, transport) == JNI_FALSE) { jniThrowIOException(env, EINVAL); return JNI_FALSE; } // Convert P256 data from Java POJO to C Struct bt_oob_data_t p256_data; if (set_data(env, p256_data, p256Data, transport) == JNI_FALSE) { jniThrowIOException(env, EINVAL); return JNI_FALSE; } return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; return ((sBluetoothInterface->create_bond_out_of_band( (RawAddress*)address, transport, &p192_data, &p256_data)) == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE; } static jboolean removeBondNative(JNIEnv* env, jobject obj, jbyteArray address) { Loading Loading @@ -1381,7 +1531,8 @@ static JNINativeMethod sMethods[] = { {"startDiscoveryNative", "()Z", (void*)startDiscoveryNative}, {"cancelDiscoveryNative", "()Z", (void*)cancelDiscoveryNative}, {"createBondNative", "([BI)Z", (void*)createBondNative}, {"createBondOutOfBandNative", "([BILandroid/bluetooth/OobData;)Z", {"createBondOutOfBandNative", "([BILandroid/bluetooth/OobData;Landroid/bluetooth/OobData;)Z", (void*)createBondOutOfBandNative}, {"removeBondNative", "([B)Z", (void*)removeBondNative}, {"cancelBondNative", "([B)Z", (void*)cancelBondNative}, Loading
android/app/src/com/android/bluetooth/btservice/AdapterService.java +28 −9 Original line number Diff line number Diff line Loading @@ -1505,15 +1505,24 @@ public class AdapterService extends Service { } @Override public boolean createBond(BluetoothDevice device, int transport, OobData oobData) { public boolean createBond(BluetoothDevice device, int transport, OobData remoteP192Data, OobData remoteP256Data) { AdapterService service = getService(); if (service == null || !callerIsSystemOrActiveOrManagedUser(service, TAG, "createBond")) { return false; } // This conditional is required to satisfy permission dependencies // since createBond calls createBondOutOfBand with null value passed as data. // BluetoothDevice#createBond requires BLUETOOTH_ADMIN only. if (remoteP192Data == null && remoteP256Data == null) { enforceBluetoothAdminPermission(service); } else { // createBondOutOfBand() is a @SystemApi, this requires PRIVILEGED. enforceBluetoothPrivilegedPermission(service); } return service.createBond(device, transport, oobData); return service.createBond(device, transport, remoteP192Data, remoteP256Data); } @Override Loading Loading @@ -2400,7 +2409,8 @@ public class AdapterService extends Service { return mDatabaseManager; } boolean createBond(BluetoothDevice device, int transport, OobData oobData) { boolean createBond(BluetoothDevice device, int transport, OobData remoteP192Data, OobData remoteP256Data) { DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device); if (deviceProp != null && deviceProp.getBondState() != BluetoothDevice.BOND_NONE) { return false; Loading @@ -2416,10 +2426,18 @@ public class AdapterService extends Service { msg.obj = device; msg.arg1 = transport; if (oobData != null) { Bundle oobDataBundle = new Bundle(); oobDataBundle.putParcelable(BondStateMachine.OOBDATA, oobData); msg.setData(oobDataBundle); Bundle remoteOobDatasBundle = new Bundle(); boolean setData = false; if (remoteP192Data != null) { remoteOobDatasBundle.putParcelable(BondStateMachine.OOBDATAP192, remoteP192Data); setData = true; } if (remoteP256Data != null) { remoteOobDatasBundle.putParcelable(BondStateMachine.OOBDATAP256, remoteP256Data); setData = true; } if (setData) { msg.setData(remoteOobDatasBundle); } mBondStateMachine.sendMessage(msg); return true; Loading Loading @@ -3437,7 +3455,8 @@ public class AdapterService extends Service { public native boolean createBondNative(byte[] address, int transport); /*package*/ native boolean createBondOutOfBandNative(byte[] address, int transport, OobData oobData); native boolean createBondOutOfBandNative(byte[] address, int transport, OobData p192Data, OobData p256Data); /*package*/ public native boolean removeBondNative(byte[] address); Loading
android/app/src/com/android/bluetooth/btservice/BondStateMachine.java +21 −18 Original line number Diff line number Diff line Loading @@ -76,7 +76,8 @@ final class BondStateMachine extends StateMachine { private PendingCommandState mPendingCommandState = new PendingCommandState(); private StableState mStableState = new StableState(); public static final String OOBDATA = "oobdata"; public static final String OOBDATAP192 = "oobdatap192"; public static final String OOBDATAP256 = "oobdatap256"; @VisibleForTesting Set<BluetoothDevice> mPendingBondedDevices = new HashSet<>(); Loading Loading @@ -129,12 +130,11 @@ final class BondStateMachine extends StateMachine { switch (msg.what) { case CREATE_BOND: OobData oobData = null; if (msg.getData() != null) { oobData = msg.getData().getParcelable(OOBDATA); } createBond(dev, msg.arg1, oobData, true); OobData p192Data = (msg.getData() != null) ? msg.getData().getParcelable(OOBDATAP192) : null; OobData p256Data = (msg.getData() != null) ? msg.getData().getParcelable(OOBDATAP256) : null; createBond(dev, msg.arg1, p192Data, p256Data, true); break; case REMOVE_BOND: removeBond(dev, true); Loading Loading @@ -191,12 +191,11 @@ final class BondStateMachine extends StateMachine { switch (msg.what) { case CREATE_BOND: OobData oobData = null; if (msg.getData() != null) { oobData = msg.getData().getParcelable(OOBDATA); } result = createBond(dev, msg.arg1, oobData, false); OobData p192Data = (msg.getData() != null) ? msg.getData().getParcelable(OOBDATAP192) : null; OobData p256Data = (msg.getData() != null) ? msg.getData().getParcelable(OOBDATAP256) : null; result = createBond(dev, msg.arg1, p192Data, p256Data, false); break; case REMOVE_BOND: result = removeBond(dev, false); Loading Loading @@ -310,23 +309,27 @@ final class BondStateMachine extends StateMachine { return false; } private boolean createBond(BluetoothDevice dev, int transport, OobData oobData, boolean transition) { private boolean createBond(BluetoothDevice dev, int transport, OobData remoteP192Data, OobData remoteP256Data, boolean transition) { if (dev.getBondState() == BluetoothDevice.BOND_NONE) { infoLog("Bond address is:" + dev); byte[] addr = Utils.getBytesFromAddress(dev.getAddress()); boolean result; if (oobData != null) { result = mAdapterService.createBondOutOfBandNative(addr, transport, oobData); // If we have some data if (remoteP192Data != null || remoteP256Data != null) { result = mAdapterService.createBondOutOfBandNative(addr, transport, remoteP192Data, remoteP256Data); } else { result = mAdapterService.createBondNative(addr, transport); } BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED, mAdapterService.obfuscateAddress(dev), transport, dev.getType(), BluetoothDevice.BOND_BONDING, oobData == null ? BluetoothProtoEnums.BOND_SUB_STATE_UNKNOWN remoteP192Data == null && remoteP256Data == null ? BluetoothProtoEnums.BOND_SUB_STATE_UNKNOWN : BluetoothProtoEnums.BOND_SUB_STATE_LOCAL_OOB_DATA_PROVIDED, BluetoothProtoEnums.UNBOND_REASON_UNKNOWN); if (!result) { BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_BOND_STATE_CHANGED, mAdapterService.obfuscateAddress(dev), transport, dev.getType(), Loading