| #include "lib/core/CHIPError.h" |
| #include "lib/support/JniTypeWrappers.h" |
| #include <setup_payload/QRCodeSetupPayloadGenerator.h> |
| #include <setup_payload/QRCodeSetupPayloadParser.h> |
| |
| #include <lib/support/CHIPMem.h> |
| #include <lib/support/CodeUtils.h> |
| #include <lib/support/JniReferences.h> |
| #include <lib/support/logging/CHIPLogging.h> |
| |
| #include <vector> |
| |
| #include <jni.h> |
| |
| using namespace chip; |
| |
| #define SETUP_PAYLOAD_PARSER_JNI_ERROR_MIN 10 // avoiding collision with CHIPJNIError.h |
| #define _SETUP_PAYLOAD_PARSER_JNI_ERROR(e) CHIP_APPLICATION_ERROR(SETUP_PAYLOAD_PARSER_JNI_ERROR_MIN + (e)) |
| |
| #define SETUP_PAYLOAD_PARSER_JNI_ERROR_EXCEPTION_THROWN _SETUP_PAYLOAD_PARSER_JNI_ERROR(0) |
| #define SETUP_PAYLOAD_PARSER_JNI_ERROR_TYPE_NOT_FOUND _SETUP_PAYLOAD_PARSER_JNI_ERROR(1) |
| #define SETUP_PAYLOAD_PARSER_JNI_ERROR_METHOD_NOT_FOUND _SETUP_PAYLOAD_PARSER_JNI_ERROR(2) |
| #define SETUP_PAYLOAD_PARSER_JNI_ERROR_FIELD_NOT_FOUND _SETUP_PAYLOAD_PARSER_JNI_ERROR(3) |
| |
| #define JNI_METHOD(RETURN, METHOD_NAME) \ |
| extern "C" JNIEXPORT RETURN JNICALL Java_chip_onboardingpayload_OnboardingPayloadParser_##METHOD_NAME |
| |
| static jobject TransformSetupPayload(JNIEnv * env, SetupPayload & payload); |
| static jobject CreateCapabilitiesHashSet(JNIEnv * env, RendezvousInformationFlags flags); |
| static void TransformSetupPayloadFromJobject(JNIEnv * env, jobject jPayload, SetupPayload & payload); |
| static void CreateCapabilitiesFromHashSet(JNIEnv * env, jobject discoveryCapabilitiesObj, RendezvousInformationFlags & flags); |
| static CHIP_ERROR ThrowUnrecognizedQRCodeException(JNIEnv * env, jstring qrCodeObj); |
| static CHIP_ERROR ThrowOnboardingPayloadException(JNIEnv * env, CHIP_ERROR errToThrow); |
| |
| jint JNI_OnLoad(JavaVM * jvm, void * reserved) |
| { |
| ChipLogProgress(SetupPayload, "JNI_OnLoad() called"); |
| chip::Platform::MemoryInit(); |
| return JNI_VERSION_1_6; |
| } |
| |
| JNI_METHOD(jobject, fetchPayloadFromQrCode)(JNIEnv * env, jobject self, jstring qrCodeObj, jboolean skipPayloadValidation) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| const char * qrString = NULL; |
| SetupPayload payload; |
| |
| qrString = env->GetStringUTFChars(qrCodeObj, 0); |
| |
| err = QRCodeSetupPayloadParser(qrString).populatePayload(payload); |
| env->ReleaseStringUTFChars(qrCodeObj, qrString); |
| |
| if (skipPayloadValidation == JNI_FALSE && !payload.isValidQRCodePayload()) |
| { |
| ThrowOnboardingPayloadException(env, err); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(SetupPayload, "Error throwing OnboardingPayloadException: %" CHIP_ERROR_FORMAT, err.Format()); |
| } |
| return nullptr; |
| } |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| err = ThrowUnrecognizedQRCodeException(env, qrCodeObj); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(SetupPayload, "Error throwing UnrecognizedQRCodeException: %" CHIP_ERROR_FORMAT, err.Format()); |
| } |
| return nullptr; |
| } |
| |
| return TransformSetupPayload(env, payload); |
| } |
| |
| jobject TransformSetupPayload(JNIEnv * env, SetupPayload & payload) |
| { |
| jclass setupPayloadClass = env->FindClass("chip/onboardingpayload/OnboardingPayload"); |
| jmethodID setupConstr = env->GetMethodID(setupPayloadClass, "<init>", "()V"); |
| jobject setupPayload = env->NewObject(setupPayloadClass, setupConstr); |
| |
| jfieldID version = env->GetFieldID(setupPayloadClass, "version", "I"); |
| jfieldID vendorId = env->GetFieldID(setupPayloadClass, "vendorId", "I"); |
| jfieldID productId = env->GetFieldID(setupPayloadClass, "productId", "I"); |
| jfieldID commissioningFlow = env->GetFieldID(setupPayloadClass, "commissioningFlow", "I"); |
| jfieldID discriminator = env->GetFieldID(setupPayloadClass, "discriminator", "I"); |
| jfieldID hasShortDiscriminator = env->GetFieldID(setupPayloadClass, "hasShortDiscriminator", "Z"); |
| jfieldID setUpPinCode = env->GetFieldID(setupPayloadClass, "setupPinCode", "J"); |
| jfieldID discoveryCapabilities = env->GetFieldID(setupPayloadClass, "discoveryCapabilities", "Ljava/util/Set;"); |
| |
| env->SetIntField(setupPayload, version, payload.version); |
| env->SetIntField(setupPayload, vendorId, payload.vendorID); |
| env->SetIntField(setupPayload, productId, payload.productID); |
| env->SetIntField(setupPayload, commissioningFlow, static_cast<int>(payload.commissioningFlow)); |
| uint16_t discriminatorValue; |
| bool isShortDiscriminator = payload.discriminator.IsShortDiscriminator(); |
| if (isShortDiscriminator) |
| { |
| discriminatorValue = static_cast<uint16_t>(payload.discriminator.GetShortValue()) |
| << (SetupDiscriminator::kLongBits - SetupDiscriminator::kShortBits); |
| } |
| else |
| { |
| discriminatorValue = payload.discriminator.GetLongValue(); |
| } |
| env->SetIntField(setupPayload, discriminator, discriminatorValue); |
| env->SetBooleanField(setupPayload, hasShortDiscriminator, isShortDiscriminator); |
| env->SetLongField(setupPayload, setUpPinCode, payload.setUpPINCode); |
| |
| env->SetObjectField(setupPayload, discoveryCapabilities, |
| CreateCapabilitiesHashSet(env, payload.rendezvousInformation.ValueOr(RendezvousInformationFlag::kNone))); |
| |
| jmethodID addOptionalInfoMid = |
| env->GetMethodID(setupPayloadClass, "addOptionalQRCodeInfo", "(Lchip/onboardingpayload/OptionalQRCodeInfo;)V"); |
| |
| std::vector<OptionalQRCodeInfo> optional_info = payload.getAllOptionalVendorData(); |
| for (OptionalQRCodeInfo & info : optional_info) |
| { |
| |
| jclass optionalInfoClass = env->FindClass("chip/onboardingpayload/OptionalQRCodeInfo"); |
| jobject optionalInfo = env->AllocObject(optionalInfoClass); |
| jfieldID tag = env->GetFieldID(optionalInfoClass, "tag", "I"); |
| jfieldID type = |
| env->GetFieldID(optionalInfoClass, "type", "Lchip/onboardingpayload/OptionalQRCodeInfo$OptionalQRCodeInfoType;"); |
| jfieldID data = env->GetFieldID(optionalInfoClass, "data", "Ljava/lang/String;"); |
| jfieldID int32 = env->GetFieldID(optionalInfoClass, "int32", "I"); |
| |
| env->SetIntField(optionalInfo, tag, info.tag); |
| |
| jclass enumClass = env->FindClass("chip/onboardingpayload/OptionalQRCodeInfo$OptionalQRCodeInfoType"); |
| jfieldID enumType = nullptr; |
| |
| switch (info.type) |
| { |
| case optionalQRCodeInfoTypeString: |
| enumType = env->GetStaticFieldID(enumClass, "TYPE_STRING", |
| "Lchip/onboardingpayload/OptionalQRCodeInfo$OptionalQRCodeInfoType;"); |
| break; |
| case optionalQRCodeInfoTypeInt32: |
| enumType = env->GetStaticFieldID(enumClass, "TYPE_INT32", |
| "Lchip/onboardingpayload/OptionalQRCodeInfo$OptionalQRCodeInfoType;"); |
| break; |
| case optionalQRCodeInfoTypeInt64: |
| enumType = env->GetStaticFieldID(enumClass, "TYPE_INT64", |
| "Lchip/onboardingpayload/OptionalQRCodeInfo$OptionalQRCodeInfoType;"); |
| break; |
| case optionalQRCodeInfoTypeUInt32: |
| enumType = env->GetStaticFieldID(enumClass, "TYPE_UINT32", |
| "Lchip/onboardingpayload/OptionalQRCodeInfo$OptionalQRCodeInfoType;"); |
| break; |
| case optionalQRCodeInfoTypeUInt64: |
| enumType = env->GetStaticFieldID(enumClass, "TYPE_UINT64", |
| "Lchip/onboardingpayload/OptionalQRCodeInfo$OptionalQRCodeInfoType;"); |
| break; |
| case optionalQRCodeInfoTypeUnknown: |
| default: // Optional Type variable has to set any value. |
| enumType = env->GetStaticFieldID(enumClass, "TYPE_UNKNOWN", |
| "Lchip/onboardingpayload/OptionalQRCodeInfo$OptionalQRCodeInfoType;"); |
| break; |
| } |
| |
| if (enumType != nullptr) |
| { |
| jobject enumObj = env->GetStaticObjectField(enumClass, enumType); |
| env->SetObjectField(optionalInfo, type, enumObj); |
| } |
| |
| env->SetObjectField(optionalInfo, data, env->NewStringUTF(info.data.c_str())); |
| env->SetIntField(optionalInfo, int32, info.int32); |
| |
| env->CallVoidMethod(setupPayload, addOptionalInfoMid, optionalInfo); |
| } |
| |
| return setupPayload; |
| } |
| |
| jobject CreateCapabilitiesHashSet(JNIEnv * env, RendezvousInformationFlags flags) |
| { |
| jclass hashSetClass = env->FindClass("java/util/HashSet"); |
| jmethodID hashSetConstructor = env->GetMethodID(hashSetClass, "<init>", "()V"); |
| jobject capabilitiesHashSet = env->NewObject(hashSetClass, hashSetConstructor); |
| |
| jmethodID hashSetAddMethod = env->GetMethodID(hashSetClass, "add", "(Ljava/lang/Object;)Z"); |
| jclass capabilityEnum = env->FindClass("chip/onboardingpayload/DiscoveryCapability"); |
| |
| if (flags.Has(chip::RendezvousInformationFlag::kBLE)) |
| { |
| jfieldID bleCapability = env->GetStaticFieldID(capabilityEnum, "BLE", "Lchip/onboardingpayload/DiscoveryCapability;"); |
| jobject enumObj = env->GetStaticObjectField(capabilityEnum, bleCapability); |
| env->CallBooleanMethod(capabilitiesHashSet, hashSetAddMethod, enumObj); |
| } |
| if (flags.Has(chip::RendezvousInformationFlag::kSoftAP)) |
| { |
| jfieldID softApCapability = |
| env->GetStaticFieldID(capabilityEnum, "SOFT_AP", "Lchip/onboardingpayload/DiscoveryCapability;"); |
| jobject enumObj = env->GetStaticObjectField(capabilityEnum, softApCapability); |
| env->CallBooleanMethod(capabilitiesHashSet, hashSetAddMethod, enumObj); |
| } |
| if (flags.Has(chip::RendezvousInformationFlag::kOnNetwork)) |
| { |
| jfieldID onNetworkCapability = |
| env->GetStaticFieldID(capabilityEnum, "ON_NETWORK", "Lchip/onboardingpayload/DiscoveryCapability;"); |
| jobject enumObj = env->GetStaticObjectField(capabilityEnum, onNetworkCapability); |
| env->CallBooleanMethod(capabilitiesHashSet, hashSetAddMethod, enumObj); |
| } |
| return capabilitiesHashSet; |
| } |
| |
| JNI_METHOD(jstring, getQrCodeFromPayload)(JNIEnv * env, jobject self, jobject setupPayload) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| SetupPayload payload; |
| std::string qrString; |
| |
| TransformSetupPayloadFromJobject(env, setupPayload, payload); |
| |
| err = QRCodeSetupPayloadGenerator(payload).payloadBase38Representation(qrString); |
| if (err != CHIP_NO_ERROR) |
| { |
| ThrowOnboardingPayloadException(env, err); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(SetupPayload, "Error throwing OnboardingPayloadException: %" CHIP_ERROR_FORMAT, err.Format()); |
| } |
| return nullptr; |
| } |
| |
| return env->NewStringUTF(qrString.c_str()); |
| } |
| |
| void TransformSetupPayloadFromJobject(JNIEnv * env, jobject jPayload, SetupPayload & payload) |
| { |
| jclass setupPayloadClass = env->FindClass("chip/onboardingpayload/OnboardingPayload"); |
| |
| jfieldID version = env->GetFieldID(setupPayloadClass, "version", "I"); |
| jfieldID vendorId = env->GetFieldID(setupPayloadClass, "vendorId", "I"); |
| jfieldID productId = env->GetFieldID(setupPayloadClass, "productId", "I"); |
| jfieldID commissioningFlow = env->GetFieldID(setupPayloadClass, "commissioningFlow", "I"); |
| jfieldID discriminator = env->GetFieldID(setupPayloadClass, "discriminator", "I"); |
| jfieldID hasShortDiscriminatorFieldId = env->GetFieldID(setupPayloadClass, "hasShortDiscriminator", "Z"); |
| jfieldID setUpPinCode = env->GetFieldID(setupPayloadClass, "setupPinCode", "J"); |
| jfieldID discoveryCapabilities = env->GetFieldID(setupPayloadClass, "discoveryCapabilities", "Ljava/util/Set;"); |
| |
| payload.version = env->GetIntField(jPayload, version); |
| payload.vendorID = env->GetIntField(jPayload, vendorId); |
| payload.productID = env->GetIntField(jPayload, productId); |
| payload.commissioningFlow = static_cast<CommissioningFlow>(env->GetIntField(jPayload, commissioningFlow)); |
| jboolean hasShortDiscriminator = env->GetBooleanField(jPayload, hasShortDiscriminatorFieldId); |
| if (hasShortDiscriminator) |
| { |
| payload.discriminator.SetShortValue(env->GetShortField(jPayload, discriminator)); |
| } |
| else |
| { |
| payload.discriminator.SetLongValue(env->GetIntField(jPayload, discriminator)); |
| } |
| payload.setUpPINCode = static_cast<uint32_t>(env->GetLongField(jPayload, setUpPinCode)); |
| |
| jobject discoveryCapabilitiesObj = env->GetObjectField(jPayload, discoveryCapabilities); |
| CreateCapabilitiesFromHashSet(env, discoveryCapabilitiesObj, |
| payload.rendezvousInformation.Emplace(RendezvousInformationFlag::kNone)); |
| } |
| |
| void CreateCapabilitiesFromHashSet(JNIEnv * env, jobject discoveryCapabilitiesObj, RendezvousInformationFlags & flags) |
| { |
| jclass hashSetClass = env->FindClass("java/util/HashSet"); |
| jmethodID hashSetContainsMethod = env->GetMethodID(hashSetClass, "contains", "(Ljava/lang/Object;)Z"); |
| |
| jboolean contains; |
| jclass capabilityEnum = env->FindClass("chip/onboardingpayload/DiscoveryCapability"); |
| |
| jfieldID bleCapability = env->GetStaticFieldID(capabilityEnum, "BLE", "Lchip/onboardingpayload/DiscoveryCapability;"); |
| jobject bleObj = env->GetStaticObjectField(capabilityEnum, bleCapability); |
| contains = env->CallBooleanMethod(discoveryCapabilitiesObj, hashSetContainsMethod, bleObj); |
| if (contains) |
| { |
| flags.Set(chip::RendezvousInformationFlag::kBLE); |
| } |
| |
| jfieldID softApCapability = env->GetStaticFieldID(capabilityEnum, "SOFT_AP", "Lchip/onboardingpayload/DiscoveryCapability;"); |
| jobject softApObj = env->GetStaticObjectField(capabilityEnum, softApCapability); |
| contains = env->CallBooleanMethod(discoveryCapabilitiesObj, hashSetContainsMethod, softApObj); |
| if (contains) |
| { |
| flags.Set(chip::RendezvousInformationFlag::kSoftAP); |
| } |
| |
| jfieldID onNetworkCapability = |
| env->GetStaticFieldID(capabilityEnum, "ON_NETWORK", "Lchip/onboardingpayload/DiscoveryCapability;"); |
| jobject onNetworkObj = env->GetStaticObjectField(capabilityEnum, onNetworkCapability); |
| contains = env->CallBooleanMethod(discoveryCapabilitiesObj, hashSetContainsMethod, onNetworkObj); |
| if (contains) |
| { |
| flags.Set(chip::RendezvousInformationFlag::kOnNetwork); |
| } |
| } |
| |
| CHIP_ERROR ThrowUnrecognizedQRCodeException(JNIEnv * env, jstring qrCodeObj) |
| { |
| jclass exceptionCls = nullptr; |
| jmethodID exceptionConstructor = nullptr; |
| jthrowable exception = nullptr; |
| |
| env->ExceptionClear(); |
| |
| exceptionCls = env->FindClass("chip/onboardingpayload/UnrecognizedQrCodeException"); |
| VerifyOrReturnError(exceptionCls != NULL, SETUP_PAYLOAD_PARSER_JNI_ERROR_TYPE_NOT_FOUND); |
| exceptionConstructor = env->GetMethodID(exceptionCls, "<init>", "(Ljava/lang/String;)V"); |
| VerifyOrReturnError(exceptionConstructor != NULL, SETUP_PAYLOAD_PARSER_JNI_ERROR_METHOD_NOT_FOUND); |
| exception = (jthrowable) env->NewObject(exceptionCls, exceptionConstructor, qrCodeObj); |
| VerifyOrReturnError(exception != NULL, SETUP_PAYLOAD_PARSER_JNI_ERROR_EXCEPTION_THROWN); |
| |
| env->Throw(exception); |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR ThrowOnboardingPayloadException(JNIEnv * env, CHIP_ERROR errToThrow) |
| { |
| jclass exceptionCls = nullptr; |
| jmethodID exceptionConstructor = nullptr; |
| jthrowable exception = nullptr; |
| |
| env->ExceptionClear(); |
| |
| exceptionCls = env->FindClass("chip/onboardingpayload/OnboardingPayloadException"); |
| VerifyOrReturnError(exceptionCls != NULL, SETUP_PAYLOAD_PARSER_JNI_ERROR_TYPE_NOT_FOUND); |
| exceptionConstructor = env->GetMethodID(exceptionCls, "<init>", "(Ljava/lang/String;)V"); |
| VerifyOrReturnError(exceptionConstructor != NULL, SETUP_PAYLOAD_PARSER_JNI_ERROR_METHOD_NOT_FOUND); |
| |
| jstring jerrStr = env->NewStringUTF(ErrorStr(errToThrow)); |
| |
| exception = (jthrowable) env->NewObject(exceptionCls, exceptionConstructor, jerrStr); |
| VerifyOrReturnError(exception != NULL, SETUP_PAYLOAD_PARSER_JNI_ERROR_EXCEPTION_THROWN); |
| |
| env->Throw(exception); |
| return CHIP_NO_ERROR; |
| } |