Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d4ebd3cb authored by Martin Brabham's avatar Martin Brabham
Browse files

Modify the Out-of-Band data path

Streamline createBondOutOfBand and account for p192 and p256
data types.

Make additions to the JNI layer to transform the OobData java object
to the host stack bt_oob_data_object_t for the host stack.

Ignore-AOSP-First: Conflicts with CL previously merged internal
Bug: 178007935
Test: atest BluetoothInstrumentationTests
Tag: #feature
Change-Id: Id593df4de85d12e114aa44c64c4fb01f7e7419c0
Merged-In: Id593df4de85d12e114aa44c64c4fb01f7e7419c0
parent 3cb0089c
Loading
Loading
Loading
Loading
+229 −78
Original line number Diff line number Diff line
@@ -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;

@@ -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) {
@@ -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},
+28 −9
Original line number Diff line number Diff line
@@ -1489,15 +1489,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
@@ -2384,7 +2393,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;
@@ -2400,10 +2410,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;
@@ -3421,7 +3439,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);
+21 −18
Original line number Diff line number Diff line
@@ -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<>();

@@ -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);
@@ -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);
@@ -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(),