| /* |
| * Copyright (c) 2020-2022 Project CHIP Authors |
| * All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| /** |
| * @file |
| * Implementation of JNI bridge for CHIP Device Controller for Android apps |
| * |
| */ |
| #include "AndroidCallbacks.h" |
| #include "AndroidCommissioningWindowOpener.h" |
| #include "AndroidCurrentFabricRemover.h" |
| #include "AndroidDeviceControllerWrapper.h" |
| #include "AndroidInteractionClient.h" |
| #include <lib/support/CHIPJNIError.h> |
| #include <lib/support/JniReferences.h> |
| #include <lib/support/JniTypeWrappers.h> |
| |
| #include <app/AttributePathParams.h> |
| #include <app/DataVersionFilter.h> |
| #include <app/InteractionModelEngine.h> |
| #include <app/ReadClient.h> |
| #include <app/WriteClient.h> |
| #include <atomic> |
| #include <ble/BleUUID.h> |
| #include <controller/CHIPDeviceController.h> |
| #include <controller/CommissioningWindowOpener.h> |
| #include <controller/java/GroupDeviceProxy.h> |
| #include <credentials/CHIPCert.h> |
| #include <jni.h> |
| #include <lib/core/ErrorStr.h> |
| #include <lib/support/CHIPMem.h> |
| #include <lib/support/CodeUtils.h> |
| #include <lib/support/SafeInt.h> |
| #include <lib/support/ThreadOperationalDataset.h> |
| #include <lib/support/jsontlv/JsonToTlv.h> |
| #include <lib/support/jsontlv/TlvToJson.h> |
| #include <lib/support/logging/CHIPLogging.h> |
| #include <platform/KeyValueStoreManager.h> |
| #include <protocols/Protocols.h> |
| #include <protocols/secure_channel/SessionEstablishmentDelegate.h> |
| #include <pthread.h> |
| #include <system/SystemClock.h> |
| #include <vector> |
| |
| #ifdef CHIP_DEVICE_CONFIG_DYNAMIC_SERVER |
| #include <app/dynamic_server/AccessControl.h> |
| #endif // CHIP_DEVICE_CONFIG_DYNAMIC_SERVER |
| |
| #ifdef JAVA_MATTER_CONTROLLER_TEST |
| #include <controller/ExampleOperationalCredentialsIssuer.h> |
| #else |
| #include <platform/android/AndroidChipPlatform-JNI.h> |
| #endif |
| |
| // Choose an approximation of PTHREAD_NULL if pthread.h doesn't define one. |
| #ifndef PTHREAD_NULL |
| #define PTHREAD_NULL 0 |
| #endif // PTHREAD_NULL |
| |
| using namespace chip; |
| using namespace chip::Inet; |
| using namespace chip::Controller; |
| using namespace chip::Credentials; |
| |
| #define JNI_METHOD(RETURN, METHOD_NAME) \ |
| extern "C" JNIEXPORT RETURN JNICALL Java_chip_devicecontroller_ChipDeviceController_##METHOD_NAME |
| |
| #define CDC_JNI_CALLBACK_LOCAL_REF_COUNT 256 |
| |
| static void * IOThreadMain(void * arg); |
| static CHIP_ERROR StopIOThread(); |
| static CHIP_ERROR N2J_PaseVerifierParams(JNIEnv * env, jlong setupPincode, jbyteArray pakeVerifier, jobject & outParams); |
| static CHIP_ERROR N2J_NetworkLocation(JNIEnv * env, jstring ipAddress, jint port, jint interfaceIndex, jobject & outLocation); |
| |
| namespace { |
| JavaVM * sJVM = nullptr; |
| pthread_t sIOThread = PTHREAD_NULL; |
| chip::JniGlobalReference sChipDeviceControllerExceptionCls; |
| } // namespace |
| |
| // NOTE: Remote device ID is in sync with the echo server device id |
| // At some point, we may want to add an option to connect to a device without |
| // knowing its id, because the ID can be learned on the first response that is received. |
| chip::NodeId kLocalDeviceId = chip::kTestControllerNodeId; |
| chip::NodeId kRemoteDeviceId = chip::kTestDeviceNodeId; |
| |
| jint JNI_OnLoad(JavaVM * jvm, void * reserved) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| JNIEnv * env; |
| |
| ChipLogProgress(Controller, "JNI_OnLoad() called"); |
| |
| chip::Platform::MemoryInit(); |
| |
| // Save a reference to the JVM. Will need this to call back into Java. |
| JniReferences::GetInstance().SetJavaVm(jvm, "chip/devicecontroller/ChipDeviceController"); |
| sJVM = jvm; |
| |
| // Get a JNI environment object. |
| env = JniReferences::GetInstance().GetEnvForCurrentThread(); |
| VerifyOrExit(env != nullptr, err = CHIP_JNI_ERROR_NO_ENV); |
| |
| ChipLogProgress(Controller, "Loading Java class references."); |
| |
| // Get various class references need by the API. |
| jclass controllerExceptionCls; |
| err = JniReferences::GetInstance().GetLocalClassRef(env, "chip/devicecontroller/ChipDeviceControllerException", |
| controllerExceptionCls); |
| SuccessOrExit(err = sChipDeviceControllerExceptionCls.Init(controllerExceptionCls)); |
| |
| ChipLogProgress(Controller, "Java class references loaded."); |
| |
| #ifndef JAVA_MATTER_CONTROLLER_TEST |
| err = AndroidChipPlatformJNI_OnLoad(jvm, reserved); |
| SuccessOrExit(err); |
| #endif // JAVA_MATTER_CONTROLLER_TEST |
| |
| #ifdef CHIP_DEVICE_CONFIG_DYNAMIC_SERVER |
| chip::app::dynamic_server::InitAccessControl(); |
| #endif // CHIP_DEVICE_CONFIG_DYNAMIC_SERVER |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| chip::DeviceLayer::StackUnlock unlock; |
| JNI_OnUnload(jvm, reserved); |
| } |
| |
| return (err == CHIP_NO_ERROR) ? JNI_VERSION_1_6 : JNI_ERR; |
| } |
| |
| void JNI_OnUnload(JavaVM * jvm, void * reserved) |
| { |
| chip::DeviceLayer::StackLock lock; |
| ChipLogProgress(Controller, "JNI_OnUnload() called"); |
| |
| // If the IO thread has not been stopped yet, shut it down now. |
| // TODO(arkq): Maybe we should just assert here, as the IO thread |
| // should be stopped before the library is unloaded. |
| StopIOThread(); |
| |
| sJVM = nullptr; |
| |
| chip::Platform::MemoryShutdown(); |
| } |
| |
| JNI_METHOD(jint, onNOCChainGeneration) |
| (JNIEnv * env, jobject self, jlong handle, jobject controllerParams) |
| { |
| chip::DeviceLayer::StackLock lock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| ChipLogProgress(Controller, "setNOCChain() called"); |
| |
| jmethodID getRootCertificate; |
| jmethodID getIntermediateCertificate; |
| jmethodID getOperationalCertificate; |
| jmethodID getIpk; |
| jmethodID getAdminSubject; |
| |
| jbyteArray rootCertificate = nullptr; |
| jbyteArray intermediateCertificate = nullptr; |
| jbyteArray operationalCertificate = nullptr; |
| jbyteArray ipk = nullptr; |
| |
| Optional<NodeId> adminSubjectOptional; |
| uint64_t adminSubject = chip::kUndefinedNodeId; |
| |
| CommissioningParameters commissioningParams = wrapper->GetCommissioningParameters(); |
| |
| Optional<Crypto::IdentityProtectionKeySpan> ipkOptional; |
| uint8_t ipkValue[CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES]; |
| Crypto::IdentityProtectionKeySpan ipkTempSpan(ipkValue); |
| |
| VerifyOrExit(controllerParams != nullptr, ChipLogError(Controller, "controllerParams is null!")); |
| |
| err = chip::JniReferences::GetInstance().FindMethod(env, controllerParams, "getRootCertificate", "()[B", &getRootCertificate); |
| VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(Controller, "Find getRootCertificate method fail!")); |
| |
| err = chip::JniReferences::GetInstance().FindMethod(env, controllerParams, "getIntermediateCertificate", "()[B", |
| &getIntermediateCertificate); |
| VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(Controller, "Find getIntermediateCertificate method fail!")); |
| |
| err = chip::JniReferences::GetInstance().FindMethod(env, controllerParams, "getOperationalCertificate", "()[B", |
| &getOperationalCertificate); |
| VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(Controller, "Find getOperationalCertificate method fail!")); |
| |
| err = chip::JniReferences::GetInstance().FindMethod(env, controllerParams, "getIpk", "()[B", &getIpk); |
| VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(Controller, "Find getIpk method fail!")); |
| |
| err = chip::JniReferences::GetInstance().FindMethod(env, controllerParams, "getAdminSubject", "()J", &getAdminSubject); |
| VerifyOrExit(err == CHIP_NO_ERROR, ChipLogError(Controller, "Find getAdminSubject method fail!")); |
| |
| rootCertificate = static_cast<jbyteArray>(env->CallObjectMethod(controllerParams, getRootCertificate)); |
| VerifyOrExit(rootCertificate != nullptr, err = CHIP_ERROR_BAD_REQUEST); |
| |
| intermediateCertificate = static_cast<jbyteArray>(env->CallObjectMethod(controllerParams, getIntermediateCertificate)); |
| VerifyOrExit(intermediateCertificate != nullptr, err = CHIP_ERROR_BAD_REQUEST); |
| |
| operationalCertificate = static_cast<jbyteArray>(env->CallObjectMethod(controllerParams, getOperationalCertificate)); |
| VerifyOrExit(operationalCertificate != nullptr, err = CHIP_ERROR_BAD_REQUEST); |
| |
| ipk = static_cast<jbyteArray>(env->CallObjectMethod(controllerParams, getIpk)); |
| if (ipk != nullptr) |
| { |
| JniByteArray jByteArrayIpk(env, ipk); |
| |
| if (jByteArrayIpk.byteSpan().size() != sizeof(ipkValue)) |
| { |
| ChipLogError(Controller, "Invalid IPK size %u and expect %u", static_cast<unsigned>(jByteArrayIpk.byteSpan().size()), |
| static_cast<unsigned>(sizeof(ipkValue))); |
| ExitNow(err = CHIP_ERROR_INVALID_IPK); |
| } |
| memcpy(&ipkValue[0], jByteArrayIpk.byteSpan().data(), jByteArrayIpk.byteSpan().size()); |
| |
| ipkOptional.SetValue(ipkTempSpan); |
| } |
| else if (commissioningParams.GetIpk().HasValue()) |
| { |
| // if no value pass in ControllerParams, use value from CommissioningParameters |
| ipkOptional.SetValue(commissioningParams.GetIpk().Value()); |
| } |
| |
| adminSubject = static_cast<uint64_t>(env->CallLongMethod(controllerParams, getAdminSubject)); |
| if (adminSubject == kUndefinedNodeId) |
| { |
| // if no value pass in ControllerParams, use value from CommissioningParameters |
| adminSubject = commissioningParams.GetAdminSubject().ValueOr(kUndefinedNodeId); |
| } |
| if (adminSubject != kUndefinedNodeId) |
| { |
| adminSubjectOptional.SetValue(adminSubject); |
| } |
| // NOTE: we are allowing adminSubject to not be set since the OnNOCChainGeneration callback makes this field |
| // optional and includes logic to handle the case where it is not set. It would also make sense to return |
| // an error here since that use case may not be realistic. |
| { |
| JniByteArray jByteArrayRcac(env, rootCertificate); |
| JniByteArray jByteArrayIcac(env, intermediateCertificate); |
| JniByteArray jByteArrayNoc(env, operationalCertificate); |
| |
| #ifndef JAVA_MATTER_CONTROLLER_TEST |
| err = wrapper->GetAndroidOperationalCredentialsIssuer()->NOCChainGenerated( |
| CHIP_NO_ERROR, jByteArrayNoc.byteSpan(), jByteArrayIcac.byteSpan(), jByteArrayRcac.byteSpan(), ipkOptional, |
| adminSubjectOptional); |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to SetNocChain for the device: %" CHIP_ERROR_FORMAT, err.Format()); |
| } |
| return static_cast<jint>(err.AsInteger()); |
| #endif // JAVA_MATTER_CONTROLLER_TEST |
| } |
| exit: |
| #ifndef JAVA_MATTER_CONTROLLER_TEST |
| err = wrapper->GetAndroidOperationalCredentialsIssuer()->NOCChainGenerated(err, ByteSpan(), ByteSpan(), ByteSpan(), ipkOptional, |
| adminSubjectOptional); |
| #endif // JAVA_MATTER_CONTROLLER_TEST |
| return static_cast<jint>(err.AsInteger()); |
| } |
| |
| JNI_METHOD(jlong, newDeviceController)(JNIEnv * env, jobject self, jobject controllerParams) |
| { |
| chip::DeviceLayer::StackLock lock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| AndroidDeviceControllerWrapper * wrapper = nullptr; |
| jlong result = 0; |
| |
| ChipLogProgress(Controller, "newDeviceController() called"); |
| |
| // Retrieve initialization params. |
| jmethodID getFabricId; |
| err = chip::JniReferences::GetInstance().FindMethod(env, controllerParams, "getFabricId", "()J", &getFabricId); |
| SuccessOrExit(err); |
| |
| jmethodID getUdpListenPort; |
| err = chip::JniReferences::GetInstance().FindMethod(env, controllerParams, "getUdpListenPort", "()I", &getUdpListenPort); |
| SuccessOrExit(err); |
| |
| jmethodID getControllerVendorId; |
| err = chip::JniReferences::GetInstance().FindMethod(env, controllerParams, "getControllerVendorId", "()I", |
| &getControllerVendorId); |
| |
| jmethodID getFailsafeTimerSeconds; |
| err = chip::JniReferences::GetInstance().FindMethod(env, controllerParams, "getFailsafeTimerSeconds", "()I", |
| &getFailsafeTimerSeconds); |
| SuccessOrExit(err); |
| |
| jmethodID getCASEFailsafeTimerSeconds; |
| err = chip::JniReferences::GetInstance().FindMethod(env, controllerParams, "getCASEFailsafeTimerSeconds", "()I", |
| &getCASEFailsafeTimerSeconds); |
| SuccessOrExit(err); |
| |
| jmethodID getAttemptNetworkScanWiFi; |
| err = chip::JniReferences::GetInstance().FindMethod(env, controllerParams, "getAttemptNetworkScanWiFi", "()Z", |
| &getAttemptNetworkScanWiFi); |
| SuccessOrExit(err); |
| |
| jmethodID getAttemptNetworkScanThread; |
| err = chip::JniReferences::GetInstance().FindMethod(env, controllerParams, "getAttemptNetworkScanThread", "()Z", |
| &getAttemptNetworkScanThread); |
| SuccessOrExit(err); |
| |
| jmethodID getSkipCommissioningComplete; |
| err = chip::JniReferences::GetInstance().FindMethod(env, controllerParams, "getSkipCommissioningComplete", "()Z", |
| &getSkipCommissioningComplete); |
| SuccessOrExit(err); |
| |
| jmethodID getSkipAttestationCertificateValidation; |
| err = chip::JniReferences::GetInstance().FindMethod(env, controllerParams, "getSkipAttestationCertificateValidation", "()Z", |
| &getSkipAttestationCertificateValidation); |
| SuccessOrExit(err); |
| |
| jmethodID getCountryCode; |
| err = chip::JniReferences::GetInstance().FindMethod(env, controllerParams, "getCountryCode", "()Ljava/util/Optional;", |
| &getCountryCode); |
| SuccessOrExit(err); |
| |
| jmethodID getRegulatoryLocation; |
| err = chip::JniReferences::GetInstance().FindMethod(env, controllerParams, "getRegulatoryLocation", "()Ljava/util/Optional;", |
| &getRegulatoryLocation); |
| SuccessOrExit(err); |
| |
| jmethodID getKeypairDelegate; |
| err = chip::JniReferences::GetInstance().FindMethod(env, controllerParams, "getKeypairDelegate", |
| "()Lchip/devicecontroller/KeypairDelegate;", &getKeypairDelegate); |
| SuccessOrExit(err); |
| |
| jmethodID getRootCertificate; |
| err = chip::JniReferences::GetInstance().FindMethod(env, controllerParams, "getRootCertificate", "()[B", &getRootCertificate); |
| SuccessOrExit(err); |
| |
| jmethodID getIntermediateCertificate; |
| err = chip::JniReferences::GetInstance().FindMethod(env, controllerParams, "getIntermediateCertificate", "()[B", |
| &getIntermediateCertificate); |
| SuccessOrExit(err); |
| |
| jmethodID getOperationalCertificate; |
| err = chip::JniReferences::GetInstance().FindMethod(env, controllerParams, "getOperationalCertificate", "()[B", |
| &getOperationalCertificate); |
| SuccessOrExit(err); |
| |
| jmethodID getIpk; |
| err = chip::JniReferences::GetInstance().FindMethod(env, controllerParams, "getIpk", "()[B", &getIpk); |
| SuccessOrExit(err); |
| |
| jmethodID getAdminSubject; |
| err = chip::JniReferences::GetInstance().FindMethod(env, controllerParams, "getAdminSubject", "()J", &getAdminSubject); |
| SuccessOrExit(err); |
| |
| jmethodID getEnableServerInteractions; |
| err = chip::JniReferences::GetInstance().FindMethod(env, controllerParams, "getEnableServerInteractions", "()Z", |
| &getEnableServerInteractions); |
| SuccessOrExit(err); |
| |
| { |
| uint64_t fabricId = static_cast<uint64_t>(env->CallLongMethod(controllerParams, getFabricId)); |
| uint16_t listenPort = static_cast<uint16_t>(env->CallIntMethod(controllerParams, getUdpListenPort)); |
| uint16_t controllerVendorId = static_cast<uint16_t>(env->CallIntMethod(controllerParams, getControllerVendorId)); |
| jobject keypairDelegate = env->CallObjectMethod(controllerParams, getKeypairDelegate); |
| jbyteArray rootCertificate = (jbyteArray) env->CallObjectMethod(controllerParams, getRootCertificate); |
| jbyteArray intermediateCertificate = (jbyteArray) env->CallObjectMethod(controllerParams, getIntermediateCertificate); |
| jbyteArray operationalCertificate = (jbyteArray) env->CallObjectMethod(controllerParams, getOperationalCertificate); |
| jbyteArray ipk = (jbyteArray) env->CallObjectMethod(controllerParams, getIpk); |
| uint16_t failsafeTimerSeconds = static_cast<uint16_t>(env->CallIntMethod(controllerParams, getFailsafeTimerSeconds)); |
| uint16_t caseFailsafeTimerSeconds = |
| static_cast<uint16_t>(env->CallIntMethod(controllerParams, getCASEFailsafeTimerSeconds)); |
| bool attemptNetworkScanWiFi = env->CallBooleanMethod(controllerParams, getAttemptNetworkScanWiFi); |
| bool attemptNetworkScanThread = env->CallBooleanMethod(controllerParams, getAttemptNetworkScanThread); |
| bool skipCommissioningComplete = env->CallBooleanMethod(controllerParams, getSkipCommissioningComplete); |
| bool skipAttestationCertificateValidation = |
| env->CallBooleanMethod(controllerParams, getSkipAttestationCertificateValidation); |
| uint64_t adminSubject = static_cast<uint64_t>(env->CallLongMethod(controllerParams, getAdminSubject)); |
| jobject countryCodeOptional = env->CallObjectMethod(controllerParams, getCountryCode); |
| jobject regulatoryLocationOptional = env->CallObjectMethod(controllerParams, getRegulatoryLocation); |
| bool enableServerInteractions = env->CallBooleanMethod(controllerParams, getEnableServerInteractions) == JNI_TRUE; |
| |
| jobject countryCode; |
| err = chip::JniReferences::GetInstance().GetOptionalValue(countryCodeOptional, countryCode); |
| SuccessOrExit(err); |
| |
| #ifdef JAVA_MATTER_CONTROLLER_TEST |
| std::unique_ptr<chip::Controller::ExampleOperationalCredentialsIssuer> opCredsIssuer( |
| new chip::Controller::ExampleOperationalCredentialsIssuer()); |
| #else |
| std::unique_ptr<chip::Controller::AndroidOperationalCredentialsIssuer> opCredsIssuer( |
| new chip::Controller::AndroidOperationalCredentialsIssuer()); |
| #endif |
| wrapper = AndroidDeviceControllerWrapper::AllocateNew( |
| sJVM, self, kLocalDeviceId, fabricId, chip::kUndefinedCATs, &DeviceLayer::SystemLayer(), |
| DeviceLayer::TCPEndPointManager(), DeviceLayer::UDPEndPointManager(), std::move(opCredsIssuer), keypairDelegate, |
| rootCertificate, intermediateCertificate, operationalCertificate, ipk, listenPort, controllerVendorId, |
| failsafeTimerSeconds, attemptNetworkScanWiFi, attemptNetworkScanThread, skipCommissioningComplete, |
| skipAttestationCertificateValidation, static_cast<jstring>(countryCode), enableServerInteractions, &err); |
| SuccessOrExit(err); |
| |
| if (caseFailsafeTimerSeconds > 0) |
| { |
| CommissioningParameters commissioningParams = wrapper->GetCommissioningParameters(); |
| commissioningParams.SetCASEFailsafeTimerSeconds(caseFailsafeTimerSeconds); |
| err = wrapper->UpdateCommissioningParameters(commissioningParams); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "UpdateCommissioningParameters failed. Err = %" CHIP_ERROR_FORMAT, err.Format()); |
| SuccessOrExit(err); |
| } |
| } |
| |
| if (adminSubject != kUndefinedNodeId) |
| { |
| // if there is a valid adminSubject in the ControllerParams, then remember it |
| CommissioningParameters commissioningParams = wrapper->GetCommissioningParameters(); |
| commissioningParams.SetAdminSubject(adminSubject); |
| err = wrapper->UpdateCommissioningParameters(commissioningParams); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "UpdateCommissioningParameters failed. Err = %" CHIP_ERROR_FORMAT, err.Format()); |
| SuccessOrExit(err); |
| } |
| } |
| |
| jobject regulatoryLocation; |
| err = chip::JniReferences::GetInstance().GetOptionalValue(regulatoryLocationOptional, regulatoryLocation); |
| SuccessOrExit(err); |
| |
| if (regulatoryLocation != nullptr) |
| { |
| using namespace app::Clusters::GeneralCommissioning; |
| |
| jint regulatoryLocationJint = chip::JniReferences::GetInstance().IntegerToPrimitive(regulatoryLocation); |
| VerifyOrExit(chip::CanCastTo<RegulatoryLocationTypeEnum>(regulatoryLocationJint), err = CHIP_ERROR_INVALID_ARGUMENT); |
| |
| auto regulatoryLocationType = static_cast<RegulatoryLocationTypeEnum>(regulatoryLocationJint); |
| VerifyOrExit(regulatoryLocationType >= RegulatoryLocationTypeEnum::kIndoor, err = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(regulatoryLocationType <= RegulatoryLocationTypeEnum::kIndoorOutdoor, err = CHIP_ERROR_INVALID_ARGUMENT); |
| |
| chip::Controller::CommissioningParameters commissioningParams = wrapper->GetCommissioningParameters(); |
| commissioningParams.SetDeviceRegulatoryLocation(regulatoryLocationType); |
| err = wrapper->UpdateCommissioningParameters(commissioningParams); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "UpdateCommissioningParameters failed. Err = %" CHIP_ERROR_FORMAT, err.Format()); |
| SuccessOrExit(err); |
| } |
| } |
| } |
| |
| // Create and start the IO thread. Must be called after Controller()->Init |
| if (sIOThread == PTHREAD_NULL) |
| { |
| int pthreadErr = pthread_create(&sIOThread, nullptr, IOThreadMain, nullptr); |
| VerifyOrExit(pthreadErr == 0, err = CHIP_ERROR_POSIX(pthreadErr)); |
| } |
| |
| result = wrapper->ToJNIHandle(); |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| if (wrapper != nullptr) |
| { |
| delete wrapper; |
| } |
| |
| if (err != CHIP_JNI_ERROR_EXCEPTION_THROWN) |
| { |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| } |
| } |
| |
| return result; |
| } |
| |
| JNI_METHOD(void, setDeviceAttestationDelegate) |
| (JNIEnv * env, jobject self, jlong handle, jint failSafeExpiryTimeoutSecs, jobject deviceAttestationDelegate) |
| { |
| chip::DeviceLayer::StackLock lock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| ChipLogProgress(Controller, "setDeviceAttestationDelegate() called"); |
| if (deviceAttestationDelegate != nullptr) |
| { |
| chip::Optional<uint16_t> timeoutSecs = chip::MakeOptional(static_cast<uint16_t>(failSafeExpiryTimeoutSecs)); |
| bool shouldWaitAfterDeviceAttestation = false; |
| jclass deviceAttestationDelegateCls = nullptr; |
| JniReferences::GetInstance().GetLocalClassRef(env, "chip/devicecontroller/DeviceAttestationDelegate", |
| deviceAttestationDelegateCls); |
| VerifyOrExit(deviceAttestationDelegateCls != nullptr, err = CHIP_JNI_ERROR_TYPE_NOT_FOUND); |
| |
| if (env->IsInstanceOf(deviceAttestationDelegate, deviceAttestationDelegateCls)) |
| { |
| shouldWaitAfterDeviceAttestation = true; |
| } |
| |
| err = wrapper->UpdateDeviceAttestationDelegateBridge(deviceAttestationDelegate, timeoutSecs, |
| shouldWaitAfterDeviceAttestation); |
| } |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to set device attestation delegate."); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| } |
| } |
| |
| JNI_METHOD(void, setAttestationTrustStoreDelegate) |
| (JNIEnv * env, jobject self, jlong handle, jobject attestationTrustStoreDelegate, jobject cdTrustKeys) |
| { |
| chip::DeviceLayer::StackLock lock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| ChipLogProgress(Controller, "setAttestationTrustStoreDelegate() called"); |
| |
| if (attestationTrustStoreDelegate != nullptr) |
| { |
| err = wrapper->UpdateAttestationTrustStoreBridge(attestationTrustStoreDelegate, cdTrustKeys); |
| SuccessOrExit(err); |
| } |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to set device attestation delegate."); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| } |
| } |
| |
| JNI_METHOD(void, startOTAProvider)(JNIEnv * env, jobject self, jlong handle, jobject otaProviderDelegate) |
| { |
| #if CHIP_DEVICE_CONFIG_DYNAMIC_SERVER |
| chip::DeviceLayer::StackLock lock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| VerifyOrExit(wrapper != nullptr, err = CHIP_ERROR_INCORRECT_STATE); |
| |
| ChipLogProgress(Controller, "startOTAProvider() called"); |
| err = wrapper->StartOTAProvider(otaProviderDelegate); |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to start OTA Provider. : %" CHIP_ERROR_FORMAT, err.Format()); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| } |
| #endif |
| } |
| |
| JNI_METHOD(void, finishOTAProvider)(JNIEnv * env, jobject self, jlong handle) |
| { |
| #if CHIP_DEVICE_CONFIG_DYNAMIC_SERVER |
| chip::DeviceLayer::StackLock lock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| VerifyOrExit(wrapper != nullptr, err = CHIP_ERROR_INCORRECT_STATE); |
| |
| ChipLogProgress(Controller, "finishOTAProvider() called"); |
| |
| err = wrapper->FinishOTAProvider(); |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to finish OTA Provider. : %" CHIP_ERROR_FORMAT, err.Format()); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| } |
| #endif |
| } |
| |
| JNI_METHOD(void, commissionDevice) |
| (JNIEnv * env, jobject self, jlong handle, jlong deviceId, jbyteArray csrNonce, jobject networkCredentials) |
| { |
| chip::DeviceLayer::StackLock lock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| ChipLogProgress(Controller, "commissionDevice() called"); |
| |
| CommissioningParameters commissioningParams = wrapper->GetCommissioningParameters(); |
| if (networkCredentials != nullptr) |
| { |
| err = wrapper->ApplyNetworkCredentials(commissioningParams, networkCredentials); |
| VerifyOrExit(err == CHIP_NO_ERROR, err = CHIP_ERROR_INVALID_ARGUMENT); |
| } |
| if (wrapper->GetDeviceAttestationDelegateBridge() != nullptr) |
| { |
| commissioningParams.SetDeviceAttestationDelegate(wrapper->GetDeviceAttestationDelegateBridge()); |
| } |
| if (csrNonce != nullptr) |
| { |
| JniByteArray jniCsrNonce(env, csrNonce); |
| commissioningParams.SetCSRNonce(jniCsrNonce.byteSpan()); |
| } |
| err = wrapper->Controller()->Commission(static_cast<chip::NodeId>(deviceId), commissioningParams); |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to commission the device."); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| } |
| } |
| |
| JNI_METHOD(void, pairDevice) |
| (JNIEnv * env, jobject self, jlong handle, jlong deviceId, jint connObj, jlong pinCode, jbyteArray csrNonce, |
| jobject networkCredentials, jobject icdRegistrationInfo) |
| { |
| chip::DeviceLayer::StackLock lock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| ChipLogProgress(Controller, "pairDevice() called with device ID, connection object, and pincode"); |
| |
| if (!chip::CanCastTo<uint32_t>(pinCode)) |
| { |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, CHIP_ERROR_INVALID_ARGUMENT); |
| return; |
| } |
| |
| RendezvousParameters rendezvousParams = RendezvousParameters() |
| .SetSetupPINCode(static_cast<uint32_t>(pinCode)) |
| #if CONFIG_NETWORK_LAYER_BLE |
| .SetConnectionObject(reinterpret_cast<BLE_CONNECTION_OBJECT>(connObj)) |
| #endif |
| .SetPeerAddress(Transport::PeerAddress::BLE()); |
| |
| CommissioningParameters commissioningParams = wrapper->GetCommissioningParameters(); |
| wrapper->ApplyNetworkCredentials(commissioningParams, networkCredentials); |
| |
| if (csrNonce != nullptr) |
| { |
| JniByteArray jniCsrNonce(env, csrNonce); |
| commissioningParams.SetCSRNonce(jniCsrNonce.byteSpan()); |
| } |
| |
| commissioningParams.SetICDRegistrationStrategy(ICDRegistrationStrategy::kBeforeComplete); |
| wrapper->ApplyICDRegistrationInfo(commissioningParams, icdRegistrationInfo); |
| |
| if (wrapper->GetDeviceAttestationDelegateBridge() != nullptr) |
| { |
| commissioningParams.SetDeviceAttestationDelegate(wrapper->GetDeviceAttestationDelegateBridge()); |
| } |
| err = wrapper->Controller()->PairDevice(static_cast<chip::NodeId>(deviceId), rendezvousParams, commissioningParams); |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to pair the device."); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| } |
| } |
| |
| JNI_METHOD(void, pairDeviceWithAddress) |
| (JNIEnv * env, jobject self, jlong handle, jlong deviceId, jstring address, jint port, jint discriminator, jlong pinCode, |
| jbyteArray csrNonce, jobject icdRegistrationInfo) |
| { |
| chip::DeviceLayer::StackLock lock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| ChipLogProgress(Controller, "pairDeviceWithAddress() called"); |
| |
| if (!chip::CanCastTo<uint32_t>(pinCode)) |
| { |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, CHIP_ERROR_INVALID_ARGUMENT); |
| return; |
| } |
| |
| JniUtfString addrJniString(env, address); |
| |
| RendezvousParameters rendezvousParams = |
| RendezvousParameters() |
| .SetDiscriminator(static_cast<uint16_t>(discriminator)) |
| .SetSetupPINCode(static_cast<uint32_t>(pinCode)) |
| .SetPeerAddress(Transport::PeerAddress::UDP(const_cast<char *>(addrJniString.c_str()), static_cast<uint16_t>(port))); |
| |
| CommissioningParameters commissioningParams = wrapper->GetCommissioningParameters(); |
| if (csrNonce != nullptr) |
| { |
| JniByteArray jniCsrNonce(env, csrNonce); |
| commissioningParams.SetCSRNonce(jniCsrNonce.byteSpan()); |
| } |
| |
| commissioningParams.SetICDRegistrationStrategy(ICDRegistrationStrategy::kBeforeComplete); |
| wrapper->ApplyICDRegistrationInfo(commissioningParams, icdRegistrationInfo); |
| |
| if (wrapper->GetDeviceAttestationDelegateBridge() != nullptr) |
| { |
| commissioningParams.SetDeviceAttestationDelegate(wrapper->GetDeviceAttestationDelegateBridge()); |
| } |
| err = wrapper->Controller()->PairDevice(static_cast<chip::NodeId>(deviceId), rendezvousParams, commissioningParams); |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to pair the device."); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| } |
| } |
| |
| JNI_METHOD(void, pairDeviceWithCode) |
| (JNIEnv * env, jobject self, jlong handle, jlong deviceId, jstring setUpCode, jboolean discoverOnce, |
| jboolean useOnlyOnNetworkDiscovery, jbyteArray csrNonce, jobject networkCredentials, jobject icdRegistrationInfo) |
| { |
| chip::DeviceLayer::StackLock lock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| ChipLogProgress(Controller, "pairDeviceWithCode() called"); |
| |
| JniUtfString setUpCodeJniString(env, setUpCode); |
| |
| CommissioningParameters commissioningParams = wrapper->GetCommissioningParameters(); |
| |
| auto discoveryType = DiscoveryType::kAll; |
| if (useOnlyOnNetworkDiscovery) |
| { |
| discoveryType = DiscoveryType::kDiscoveryNetworkOnly; |
| } |
| |
| if (discoverOnce) |
| { |
| discoveryType = DiscoveryType::kDiscoveryNetworkOnlyWithoutPASEAutoRetry; |
| } |
| |
| if (csrNonce != nullptr) |
| { |
| JniByteArray jniCsrNonce(env, csrNonce); |
| commissioningParams.SetCSRNonce(jniCsrNonce.byteSpan()); |
| } |
| |
| if (networkCredentials != nullptr) |
| { |
| wrapper->ApplyNetworkCredentials(commissioningParams, networkCredentials); |
| } |
| |
| commissioningParams.SetICDRegistrationStrategy(ICDRegistrationStrategy::kBeforeComplete); |
| wrapper->ApplyICDRegistrationInfo(commissioningParams, icdRegistrationInfo); |
| |
| if (wrapper->GetDeviceAttestationDelegateBridge() != nullptr) |
| { |
| commissioningParams.SetDeviceAttestationDelegate(wrapper->GetDeviceAttestationDelegateBridge()); |
| } |
| err = wrapper->Controller()->PairDevice(static_cast<chip::NodeId>(deviceId), setUpCodeJniString.c_str(), commissioningParams, |
| discoveryType); |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to pair the device."); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| } |
| } |
| |
| JNI_METHOD(void, establishPaseConnection)(JNIEnv * env, jobject self, jlong handle, jlong deviceId, jint connObj, jlong pinCode) |
| { |
| chip::DeviceLayer::StackLock lock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| if (!chip::CanCastTo<uint32_t>(pinCode)) |
| { |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, CHIP_ERROR_INVALID_ARGUMENT); |
| return; |
| } |
| |
| RendezvousParameters rendezvousParams = RendezvousParameters() |
| .SetSetupPINCode(static_cast<uint32_t>(pinCode)) |
| #if CONFIG_NETWORK_LAYER_BLE |
| .SetConnectionObject(reinterpret_cast<BLE_CONNECTION_OBJECT>(connObj)) |
| #endif |
| .SetPeerAddress(Transport::PeerAddress::BLE()); |
| |
| err = wrapper->Controller()->EstablishPASEConnection(static_cast<chip::NodeId>(deviceId), rendezvousParams); |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to establish PASE connection."); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| } |
| } |
| |
| JNI_METHOD(void, establishPaseConnectionByAddress) |
| (JNIEnv * env, jobject self, jlong handle, jlong deviceId, jstring address, jint port, jlong pinCode) |
| { |
| chip::DeviceLayer::StackLock lock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| if (!chip::CanCastTo<uint32_t>(pinCode)) |
| { |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, CHIP_ERROR_INVALID_ARGUMENT); |
| return; |
| } |
| |
| JniUtfString addrJniString(env, address); |
| |
| RendezvousParameters rendezvousParams = |
| RendezvousParameters() |
| .SetSetupPINCode(static_cast<uint32_t>(pinCode)) |
| .SetPeerAddress(Transport::PeerAddress::UDP(const_cast<char *>(addrJniString.c_str()), static_cast<uint16_t>(port))); |
| |
| err = wrapper->Controller()->EstablishPASEConnection(static_cast<chip::NodeId>(deviceId), rendezvousParams); |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to establish PASE connection."); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| } |
| } |
| |
| JNI_METHOD(void, continueCommissioning) |
| (JNIEnv * env, jobject self, jlong handle, jlong devicePtr, jboolean ignoreAttestationFailure) |
| { |
| chip::DeviceLayer::StackLock lock; |
| ChipLogProgress(Controller, "continueCommissioning() called."); |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| DeviceAttestationDelegateBridge * deviceAttestationDelegateBridge = wrapper->GetDeviceAttestationDelegateBridge(); |
| auto lastAttestationResult = deviceAttestationDelegateBridge ? deviceAttestationDelegateBridge->attestationVerificationResult() |
| : chip::Credentials::AttestationVerificationResult::kSuccess; |
| chip::DeviceProxy * deviceProxy = reinterpret_cast<chip::DeviceProxy *>(devicePtr); |
| err = wrapper->Controller()->ContinueCommissioningAfterDeviceAttestation( |
| deviceProxy, ignoreAttestationFailure ? chip::Credentials::AttestationVerificationResult::kSuccess : lastAttestationResult); |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to continue commissioning."); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| } |
| } |
| |
| JNI_METHOD(void, setUseJavaCallbackForNOCRequest) |
| (JNIEnv * env, jobject self, jlong handle, jboolean useCallback) |
| { |
| ChipLogProgress(Controller, "setUseJavaCallbackForNOCRequest() called"); |
| chip::DeviceLayer::StackLock lock; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| #ifndef JAVA_MATTER_CONTROLLER_TEST |
| wrapper->GetAndroidOperationalCredentialsIssuer()->SetUseJavaCallbackForNOCRequest(useCallback); |
| #endif |
| |
| if (useCallback) |
| { |
| // if we are assigning a callback, then make the device commissioner delegate verification to the |
| // PartialDACVerifier so that DAC chain and CD validation can be performed by custom code |
| // triggered by ChipDeviceController.NOCChainIssuer.onNOCChainGenerationNeeded(). |
| wrapper->Controller()->SetDeviceAttestationVerifier(wrapper->GetPartialDACVerifier()); |
| } |
| else |
| { |
| // if we are setting callback to null, then make the device commissioner use the default verifier |
| wrapper->Controller()->SetDeviceAttestationVerifier(GetDeviceAttestationVerifier()); |
| } |
| } |
| |
| JNI_METHOD(void, updateCommissioningNetworkCredentials) |
| (JNIEnv * env, jobject self, jlong handle, jobject networkCredentials) |
| { |
| ChipLogProgress(Controller, "updateCommissioningNetworkCredentials() called"); |
| chip::DeviceLayer::StackLock lock; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| CommissioningParameters commissioningParams = wrapper->GetAutoCommissioner()->GetCommissioningParameters(); |
| CHIP_ERROR err = wrapper->ApplyNetworkCredentials(commissioningParams, networkCredentials); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "ApplyNetworkCredentials failed. Err = %" CHIP_ERROR_FORMAT, err.Format()); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| return; |
| } |
| err = wrapper->GetAutoCommissioner()->SetCommissioningParameters(commissioningParams); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "SetCommissioningParameters failed. Err = %" CHIP_ERROR_FORMAT, err.Format()); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| return; |
| } |
| |
| // Only invoke NetworkCredentialsReady when called in response to NetworkScan result |
| if (wrapper->Controller()->GetCommissioningStage() == CommissioningStage::kNeedsNetworkCreds) |
| { |
| err = wrapper->Controller()->NetworkCredentialsReady(); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "NetworkCredentialsReady failed. Err = %" CHIP_ERROR_FORMAT, err.Format()); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| } |
| } |
| } |
| |
| JNI_METHOD(void, updateCommissioningICDRegistrationInfo) |
| (JNIEnv * env, jobject self, jlong handle, jobject icdRegistrationInfo) |
| { |
| ChipLogProgress(Controller, "updateCommissioningICDRegistrationInfo() called"); |
| chip::DeviceLayer::StackLock lock; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| CommissioningParameters commissioningParams = wrapper->GetAutoCommissioner()->GetCommissioningParameters(); |
| CHIP_ERROR err = wrapper->ApplyICDRegistrationInfo(commissioningParams, icdRegistrationInfo); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "ApplyICDRegistrationInfo failed. Err = %" CHIP_ERROR_FORMAT, err.Format()); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| return; |
| } |
| err = wrapper->GetAutoCommissioner()->SetCommissioningParameters(commissioningParams); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "SetCommissioningParameters failed. Err = %" CHIP_ERROR_FORMAT, err.Format()); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| return; |
| } |
| |
| // Only invoke ICDRegistrationInfoReady when called in ICDRegistartionInfo stage. |
| if (wrapper->Controller()->GetCommissioningStage() == CommissioningStage::kICDGetRegistrationInfo) |
| { |
| err = wrapper->Controller()->ICDRegistrationInfoReady(); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "ICDRegistrationInfoReady failed. Err = %" CHIP_ERROR_FORMAT, err.Format()); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| } |
| } |
| } |
| |
| jint GetCalendarFieldID(JNIEnv * env, const char * method) |
| { |
| jclass calendarCls = env->FindClass("java/util/Calendar"); |
| jfieldID fieldID = env->GetStaticFieldID(calendarCls, method, "I"); |
| return env->GetStaticIntField(calendarCls, fieldID); |
| } |
| |
| CHIP_ERROR GetEpochTime(JNIEnv * env, jobject calendar, uint32_t & epochTime) |
| { |
| using namespace ASN1; |
| ASN1UniversalTime universalTime; |
| |
| jmethodID getMethod = nullptr; |
| |
| jint yearID = GetCalendarFieldID(env, "YEAR"); |
| jint monthID = GetCalendarFieldID(env, "MONTH"); |
| jint dayID = GetCalendarFieldID(env, "DAY_OF_MONTH"); |
| jint hourID = GetCalendarFieldID(env, "HOUR_OF_DAY"); |
| jint minuteID = GetCalendarFieldID(env, "MINUTE"); |
| jint secondID = GetCalendarFieldID(env, "SECOND"); |
| |
| if (calendar == nullptr) |
| { |
| return CHIP_ERROR_INVALID_ARGUMENT; |
| } |
| |
| ReturnErrorOnFailure(JniReferences::GetInstance().FindMethod(env, calendar, "get", "(I)I", &getMethod)); |
| |
| universalTime.Year = static_cast<uint16_t>(env->CallIntMethod(calendar, getMethod, yearID)); |
| // The first month of the year in the Gregorian and Julian calendars is JANUARY which is 0. See detailed in |
| // https://docs.oracle.com/javase/8/docs/api/java/util/Calendar.html#MONTH |
| universalTime.Month = static_cast<uint8_t>(static_cast<uint8_t>(env->CallIntMethod(calendar, getMethod, monthID)) + 1u); |
| universalTime.Day = static_cast<uint8_t>(env->CallIntMethod(calendar, getMethod, dayID)); |
| universalTime.Hour = static_cast<uint8_t>(env->CallIntMethod(calendar, getMethod, hourID)); |
| universalTime.Minute = static_cast<uint8_t>(env->CallIntMethod(calendar, getMethod, minuteID)); |
| universalTime.Second = static_cast<uint8_t>(env->CallIntMethod(calendar, getMethod, secondID)); |
| |
| ReturnErrorOnFailure(ASN1ToChipEpochTime(universalTime, epochTime)); |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| JNI_METHOD(jbyteArray, createRootCertificate) |
| (JNIEnv * env, jclass clazz, jobject jKeypair, jlong issuerId, jobject fabricId, jobject validityStart, jobject validityEnd) |
| { |
| #ifdef JAVA_MATTER_CONTROLLER_TEST |
| return nullptr; |
| #else |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| uint32_t allocatedCertLength = chip::Credentials::kMaxDERCertLength; |
| chip::Platform::ScopedMemoryBuffer<uint8_t> outBuf; |
| jbyteArray outRcac = nullptr; |
| CHIPP256KeypairBridge keypair; |
| Optional<FabricId> fabric = Optional<FabricId>(); |
| |
| VerifyOrExit(outBuf.Alloc(allocatedCertLength), err = CHIP_ERROR_NO_MEMORY); |
| |
| keypair.SetDelegate(jKeypair); |
| err = keypair.Initialize(Crypto::ECPKeyTarget::ECDSA); |
| SuccessOrExit(err); |
| |
| if (fabricId != nullptr) |
| { |
| jlong jfabricId = chip::JniReferences::GetInstance().LongToPrimitive(fabricId); |
| fabric = MakeOptional(static_cast<FabricId>(jfabricId)); |
| } |
| |
| { |
| MutableByteSpan rcac(outBuf.Get(), allocatedCertLength); |
| |
| uint32_t start; |
| uint32_t end; |
| |
| err = GetEpochTime(env, validityStart, start); |
| SuccessOrExit(err); |
| |
| err = GetEpochTime(env, validityEnd, end); |
| SuccessOrExit(err); |
| |
| err = AndroidOperationalCredentialsIssuer::GenerateRootCertificate(keypair, static_cast<uint64_t>(issuerId), fabric, start, |
| end, rcac); |
| SuccessOrExit(err); |
| |
| err = JniReferences::GetInstance().N2J_ByteArray(env, rcac.data(), static_cast<uint32_t>(rcac.size()), outRcac); |
| SuccessOrExit(err); |
| } |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to create Root Certificate. Err = %" CHIP_ERROR_FORMAT, err.Format()); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| } |
| |
| return outRcac; |
| #endif |
| } |
| |
| JNI_METHOD(jbyteArray, createIntermediateCertificate) |
| (JNIEnv * env, jclass clazz, jobject rootKeypair, jbyteArray rootCertificate, jbyteArray intermediatePublicKey, jlong issuerId, |
| jobject fabricId, jobject validityStart, jobject validityEnd) |
| { |
| #ifdef JAVA_MATTER_CONTROLLER_TEST |
| return nullptr; |
| #else |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| uint32_t allocatedCertLength = chip::Credentials::kMaxDERCertLength; |
| chip::Platform::ScopedMemoryBuffer<uint8_t> outBuf; |
| jbyteArray outIcac = nullptr; |
| CHIPP256KeypairBridge keypair; |
| Optional<FabricId> fabric = Optional<FabricId>(); |
| |
| chip::JniByteArray jniRcac(env, rootCertificate); |
| chip::JniByteArray jnipublicKey(env, intermediatePublicKey); |
| |
| Credentials::P256PublicKeySpan publicKeySpan(reinterpret_cast<const uint8_t *>(jnipublicKey.data())); |
| Crypto::P256PublicKey publicKey{ publicKeySpan }; |
| |
| VerifyOrExit(outBuf.Alloc(allocatedCertLength), err = CHIP_ERROR_NO_MEMORY); |
| |
| keypair.SetDelegate(rootKeypair); |
| err = keypair.Initialize(Crypto::ECPKeyTarget::ECDSA); |
| SuccessOrExit(err); |
| |
| if (fabricId != nullptr) |
| { |
| jlong jfabricId = chip::JniReferences::GetInstance().LongToPrimitive(fabricId); |
| fabric = MakeOptional(static_cast<FabricId>(jfabricId)); |
| } |
| |
| { |
| MutableByteSpan icac(outBuf.Get(), allocatedCertLength); |
| |
| uint32_t start; |
| uint32_t end; |
| |
| err = GetEpochTime(env, validityStart, start); |
| SuccessOrExit(err); |
| |
| err = GetEpochTime(env, validityEnd, end); |
| SuccessOrExit(err); |
| |
| err = AndroidOperationalCredentialsIssuer::GenerateIntermediateCertificate( |
| keypair, jniRcac.byteSpan(), publicKey, static_cast<uint64_t>(issuerId), fabric, start, end, icac); |
| SuccessOrExit(err); |
| |
| ChipLogByteSpan(Controller, icac); |
| |
| err = JniReferences::GetInstance().N2J_ByteArray(env, icac.data(), static_cast<uint32_t>(icac.size()), outIcac); |
| SuccessOrExit(err); |
| } |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to create Intermediate Certificate. Err = %" CHIP_ERROR_FORMAT, err.Format()); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| } |
| |
| return outIcac; |
| #endif |
| } |
| |
| JNI_METHOD(jbyteArray, createOperationalCertificate) |
| (JNIEnv * env, jclass clazz, jobject signingKeypair, jbyteArray signingCertificate, jbyteArray operationalPublicKey, jlong fabricId, |
| jlong nodeId, jobject caseAuthenticatedTags, jobject validityStart, jobject validityEnd) |
| { |
| #ifdef JAVA_MATTER_CONTROLLER_TEST |
| return nullptr; |
| #else |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| uint32_t allocatedCertLength = chip::Credentials::kMaxDERCertLength; |
| chip::Platform::ScopedMemoryBuffer<uint8_t> outBuf; |
| jbyteArray outNoc = nullptr; |
| CHIPP256KeypairBridge keypair; |
| |
| chip::JniByteArray jniCert(env, signingCertificate); |
| chip::JniByteArray jnipublicKey(env, operationalPublicKey); |
| |
| Credentials::P256PublicKeySpan publicKeySpan(reinterpret_cast<const uint8_t *>(jnipublicKey.data())); |
| Crypto::P256PublicKey publicKey{ publicKeySpan }; |
| |
| chip::CATValues cats = chip::kUndefinedCATs; |
| if (caseAuthenticatedTags != nullptr) |
| { |
| jint size; |
| JniReferences::GetInstance().GetListSize(caseAuthenticatedTags, size); |
| VerifyOrExit(static_cast<size_t>(size) <= chip::kMaxSubjectCATAttributeCount, err = CHIP_ERROR_INVALID_ARGUMENT); |
| |
| for (jint i = 0; i < size; i++) |
| { |
| jobject cat = nullptr; |
| JniReferences::GetInstance().GetListItem(caseAuthenticatedTags, i, cat); |
| VerifyOrExit(cat != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); |
| cats.values[i] = static_cast<uint32_t>(JniReferences::GetInstance().IntegerToPrimitive(cat)); |
| } |
| } |
| |
| VerifyOrExit(outBuf.Alloc(allocatedCertLength), err = CHIP_ERROR_NO_MEMORY); |
| |
| keypair.SetDelegate(signingKeypair); |
| err = keypair.Initialize(Crypto::ECPKeyTarget::ECDSA); |
| SuccessOrExit(err); |
| { |
| MutableByteSpan noc(outBuf.Get(), allocatedCertLength); |
| |
| uint32_t start; |
| uint32_t end; |
| |
| err = GetEpochTime(env, validityStart, start); |
| SuccessOrExit(err); |
| |
| err = GetEpochTime(env, validityEnd, end); |
| SuccessOrExit(err); |
| |
| err = AndroidOperationalCredentialsIssuer::GenerateOperationalCertificate( |
| keypair, jniCert.byteSpan(), publicKey, static_cast<uint64_t>(fabricId), static_cast<uint64_t>(nodeId), cats, start, |
| end, noc); |
| SuccessOrExit(err); |
| |
| ChipLogByteSpan(Controller, noc); |
| |
| err = JniReferences::GetInstance().N2J_ByteArray(env, noc.data(), static_cast<uint32_t>(noc.size()), outNoc); |
| SuccessOrExit(err); |
| } |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to create Intermediate Certificate. Err = %" CHIP_ERROR_FORMAT, err.Format()); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| } |
| |
| return outNoc; |
| #endif |
| } |
| |
| JNI_METHOD(jbyteArray, publicKeyFromCSR) |
| (JNIEnv * env, jclass clazz, jbyteArray certificateSigningRequest) |
| { |
| jbyteArray outJbytes = nullptr; |
| |
| chip::JniByteArray jniCsr(env, certificateSigningRequest); |
| P256PublicKey publicKey; |
| CHIP_ERROR err = VerifyCertificateSigningRequest(jniCsr.byteSpan().data(), jniCsr.byteSpan().size(), publicKey); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "publicKeyFromCSR: %" CHIP_ERROR_FORMAT, err.Format()); |
| return nullptr; |
| } |
| |
| err = JniReferences::GetInstance().N2J_ByteArray(env, publicKey.Bytes(), static_cast<jsize>(publicKey.Length()), outJbytes); |
| SuccessOrExit(err); |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to publicKeyFromCSR. Err = %" CHIP_ERROR_FORMAT, err.Format()); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| } |
| |
| return outJbytes; |
| } |
| |
| JNI_METHOD(jbyteArray, convertX509CertToMatterCert) |
| (JNIEnv * env, jobject self, jbyteArray x509Cert) |
| { |
| chip::DeviceLayer::StackLock lock; |
| |
| uint32_t allocatedCertLength = chip::Credentials::kMaxCHIPCertLength; |
| chip::Platform::ScopedMemoryBuffer<uint8_t> outBuf; |
| jbyteArray outJbytes = nullptr; |
| JniByteArray x509CertBytes(env, x509Cert); |
| |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| VerifyOrExit(outBuf.Alloc(allocatedCertLength), err = CHIP_ERROR_NO_MEMORY); |
| |
| { |
| MutableByteSpan outBytes(outBuf.Get(), allocatedCertLength); |
| |
| err = chip::Credentials::ConvertX509CertToChipCert(x509CertBytes.byteSpan(), outBytes); |
| SuccessOrExit(err); |
| |
| VerifyOrExit(chip::CanCastTo<uint32_t>(outBytes.size()), err = CHIP_ERROR_INTERNAL); |
| |
| err = JniReferences::GetInstance().N2J_ByteArray(env, outBytes.data(), static_cast<jsize>(outBytes.size()), outJbytes); |
| SuccessOrExit(err); |
| } |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to convert X509 cert to CHIP cert. Err = %" CHIP_ERROR_FORMAT, err.Format()); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| } |
| |
| return outJbytes; |
| } |
| |
| JNI_METHOD(jbyteArray, extractSkidFromPaaCert) |
| (JNIEnv * env, jobject self, jbyteArray paaCert) |
| { |
| uint32_t allocatedCertLength = chip::Credentials::kMaxCHIPCertLength; |
| chip::Platform::ScopedMemoryBuffer<uint8_t> outBuf; |
| jbyteArray outJbytes = nullptr; |
| JniByteArray paaCertBytes(env, paaCert); |
| |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| VerifyOrExit(outBuf.Alloc(allocatedCertLength), err = CHIP_ERROR_NO_MEMORY); |
| { |
| MutableByteSpan outBytes(outBuf.Get(), allocatedCertLength); |
| |
| err = chip::Crypto::ExtractSKIDFromX509Cert(paaCertBytes.byteSpan(), outBytes); |
| SuccessOrExit(err); |
| |
| VerifyOrExit(chip::CanCastTo<uint32_t>(outBytes.size()), err = CHIP_ERROR_INTERNAL); |
| |
| err = JniReferences::GetInstance().N2J_ByteArray(env, outBytes.data(), static_cast<jsize>(outBytes.size()), outJbytes); |
| SuccessOrExit(err); |
| } |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to extract skid frome X509 cert. Err = %" CHIP_ERROR_FORMAT, err.Format()); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| } |
| |
| return outJbytes; |
| } |
| |
| JNI_METHOD(void, unpairDevice)(JNIEnv * env, jobject self, jlong handle, jlong deviceId) |
| { |
| chip::DeviceLayer::StackLock lock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| ChipLogProgress(Controller, "unpairDevice() called with device ID"); |
| |
| err = wrapper->Controller()->UnpairDevice(static_cast<chip::NodeId>(deviceId)); |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to unpair the device."); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| } |
| } |
| |
| JNI_METHOD(void, unpairDeviceCallback)(JNIEnv * env, jobject self, jlong handle, jlong deviceId, jobject callback) |
| { |
| chip::DeviceLayer::StackLock lock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| ChipLogProgress(Controller, "unpairDeviceCallback() called with device ID and callback object"); |
| |
| err = AndroidCurrentFabricRemover::RemoveCurrentFabric(wrapper->Controller(), static_cast<NodeId>(deviceId), callback); |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to unpair the device."); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| } |
| } |
| |
| JNI_METHOD(void, stopDevicePairing)(JNIEnv * env, jobject self, jlong handle, jlong deviceId) |
| { |
| chip::DeviceLayer::StackLock lock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| ChipLogProgress(Controller, "stopDevicePairing() called with device ID"); |
| |
| err = wrapper->Controller()->StopPairing(static_cast<chip::NodeId>(deviceId)); |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to unpair the device."); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| } |
| } |
| |
| JNI_METHOD(jlong, getDeviceBeingCommissionedPointer)(JNIEnv * env, jobject self, jlong handle, jlong nodeId) |
| { |
| chip::DeviceLayer::StackLock lock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| CommissioneeDeviceProxy * commissioneeDevice = nullptr; |
| err = wrapper->Controller()->GetDeviceBeingCommissioned(static_cast<NodeId>(nodeId), &commissioneeDevice); |
| |
| if (commissioneeDevice == nullptr) |
| { |
| ChipLogError(Controller, "Commissionee device was nullptr"); |
| err = CHIP_ERROR_INCORRECT_STATE; |
| } |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to get commissionee device: %s", ErrorStr(err)); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| return 0; |
| } |
| |
| return reinterpret_cast<jlong>(commissioneeDevice); |
| } |
| |
| JNI_METHOD(void, getConnectedDevicePointer)(JNIEnv * env, jobject self, jlong handle, jlong nodeId, jlong callbackHandle) |
| { |
| chip::DeviceLayer::StackLock lock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| GetConnectedDeviceCallback * connectedDeviceCallback = reinterpret_cast<GetConnectedDeviceCallback *>(callbackHandle); |
| VerifyOrExit(connectedDeviceCallback != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); |
| err = wrapper->Controller()->GetConnectedDevice(static_cast<chip::NodeId>(nodeId), &connectedDeviceCallback->mOnSuccess, |
| &connectedDeviceCallback->mOnFailure); |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "GetConnectedDevice failed: %" CHIP_ERROR_FORMAT, err.Format()); |
| OperationalSessionSetup::ConnnectionFailureInfo failureInfo( |
| chip::ScopedNodeId(static_cast<chip::NodeId>(nodeId), wrapper->Controller()->GetFabricIndex()), err, |
| SessionEstablishmentStage::kUnknown); |
| connectedDeviceCallback->mOnFailure.mCall(&connectedDeviceCallback->mOnFailure.mContext, failureInfo); |
| } |
| } |
| |
| JNI_METHOD(void, releaseOperationalDevicePointer)(JNIEnv * env, jobject self, jlong devicePtr) |
| { |
| chip::DeviceLayer::StackLock lock; |
| OperationalDeviceProxy * device = reinterpret_cast<OperationalDeviceProxy *>(devicePtr); |
| if (device != nullptr) |
| { |
| delete device; |
| } |
| } |
| |
| JNI_METHOD(jlong, getGroupDevicePointer)(JNIEnv * env, jobject self, jlong handle, jint groupId) |
| { |
| chip::DeviceLayer::StackLock lock; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| VerifyOrReturnValue(wrapper != nullptr, 0, ChipLogError(Controller, "wrapper is null")); |
| |
| GroupDeviceProxy * device = new GroupDeviceProxy(static_cast<GroupId>(groupId), wrapper->Controller()->GetFabricIndex(), |
| wrapper->Controller()->ExchangeMgr()); |
| |
| if (device == nullptr) |
| { |
| CHIP_ERROR err = CHIP_ERROR_NO_MEMORY; |
| ChipLogError(Controller, "GroupDeviceProxy handle is nullptr: %s", ErrorStr(err)); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| return 0; |
| } |
| |
| return reinterpret_cast<jlong>(device); |
| } |
| |
| JNI_METHOD(void, releaseGroupDevicePointer)(JNIEnv * env, jobject self, jlong devicePtr) |
| { |
| chip::DeviceLayer::StackLock lock; |
| GroupDeviceProxy * device = reinterpret_cast<GroupDeviceProxy *>(devicePtr); |
| if (device != nullptr) |
| { |
| delete device; |
| } |
| } |
| |
| JNI_METHOD(jobject, getAvailableGroupIds)(JNIEnv * env, jobject self, jlong handle) |
| { |
| chip::DeviceLayer::StackLock lock; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| VerifyOrReturnValue(wrapper != nullptr, nullptr, ChipLogError(Controller, "wrapper is null")); |
| |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| chip::Credentials::GroupDataProvider * groupDataProvider = chip::Credentials::GetGroupDataProvider(); |
| auto it = groupDataProvider->IterateGroupInfo(wrapper->Controller()->GetFabricIndex()); |
| |
| jobject groupIds; |
| err = chip::JniReferences::GetInstance().CreateArrayList(groupIds); |
| |
| chip::Credentials::GroupDataProvider::GroupInfo group; |
| |
| if (it) |
| { |
| while (it->Next(group)) |
| { |
| jobject jGroupId; |
| chip::JniReferences::GetInstance().CreateBoxedObject<jint>("java/lang/Integer", "(I)V", |
| static_cast<jint>(group.group_id), jGroupId); |
| chip::JniReferences::GetInstance().AddToList(groupIds, jGroupId); |
| } |
| } |
| |
| return groupIds; |
| } |
| |
| JNI_METHOD(jstring, getGroupName)(JNIEnv * env, jobject self, jlong handle, jint jGroupId) |
| { |
| chip::DeviceLayer::StackLock lock; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| VerifyOrReturnValue(wrapper != nullptr, nullptr, ChipLogError(Controller, "wrapper is null")); |
| |
| chip::Credentials::GroupDataProvider * groupDataProvider = chip::Credentials::GetGroupDataProvider(); |
| auto it = groupDataProvider->IterateGroupInfo(wrapper->Controller()->GetFabricIndex()); |
| |
| GroupId groupId = static_cast<GroupId>(jGroupId); |
| chip::Credentials::GroupDataProvider::GroupInfo group; |
| |
| if (it) |
| { |
| while (it->Next(group)) |
| { |
| if (group.group_id == groupId) |
| { |
| return env->NewStringUTF(group.name); |
| } |
| } |
| } |
| |
| return nullptr; |
| } |
| |
| JNI_METHOD(jobject, findKeySetId)(JNIEnv * env, jobject self, jlong handle, jint jGroupId) |
| { |
| chip::DeviceLayer::StackLock lock; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| VerifyOrReturnValue(wrapper != nullptr, nullptr, ChipLogError(Controller, "wrapper is null")); |
| |
| chip::Credentials::GroupDataProvider * groupDataProvider = chip::Credentials::GetGroupDataProvider(); |
| auto iter = groupDataProvider->IterateGroupKeys(wrapper->Controller()->GetFabricIndex()); |
| chip::Credentials::GroupDataProvider::GroupKey groupKey; |
| GroupId groupId = static_cast<GroupId>(jGroupId); |
| jobject wrapperKeyId; |
| |
| if (iter) |
| { |
| while (iter->Next(groupKey)) |
| { |
| if (groupKey.group_id == groupId) |
| { |
| jobject jKeyId; |
| chip::JniReferences::GetInstance().CreateBoxedObject<jint>("java/lang/Integer", "(I)V", |
| static_cast<jint>(groupKey.keyset_id), jKeyId); |
| chip::JniReferences::GetInstance().CreateOptional(jKeyId, wrapperKeyId); |
| iter->Release(); |
| return wrapperKeyId; |
| } |
| } |
| iter->Release(); |
| } |
| chip::JniReferences::GetInstance().CreateOptional(nullptr, wrapperKeyId); |
| return wrapperKeyId; |
| } |
| |
| JNI_METHOD(jboolean, addGroup)(JNIEnv * env, jobject self, jlong handle, jint jGroupId, jstring groupName) |
| { |
| chip::DeviceLayer::StackLock lock; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| VerifyOrReturnValue(wrapper != nullptr, JNI_FALSE, ChipLogError(Controller, "wrapper is null")); |
| |
| chip::Credentials::GroupDataProvider * groupDataProvider = chip::Credentials::GetGroupDataProvider(); |
| chip::Credentials::GroupDataProvider::GroupInfo group; |
| |
| chip::JniUtfString jniGroupName(env, groupName); |
| group.SetName(jniGroupName.charSpan()); |
| group.group_id = static_cast<GroupId>(jGroupId); |
| |
| CHIP_ERROR err = groupDataProvider->SetGroupInfo(wrapper->Controller()->GetFabricIndex(), group); |
| |
| return err == CHIP_NO_ERROR ? JNI_TRUE : JNI_FALSE; |
| } |
| |
| JNI_METHOD(jboolean, removeGroup)(JNIEnv * env, jobject self, jlong handle, jint jGroupId) |
| { |
| chip::DeviceLayer::StackLock lock; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| VerifyOrReturnValue(wrapper != nullptr, JNI_FALSE, ChipLogError(Controller, "wrapper is null")); |
| |
| chip::Credentials::GroupDataProvider * groupDataProvider = chip::Credentials::GetGroupDataProvider(); |
| CHIP_ERROR err = groupDataProvider->RemoveGroupInfo(wrapper->Controller()->GetFabricIndex(), static_cast<GroupId>(jGroupId)); |
| |
| return err == CHIP_NO_ERROR ? JNI_TRUE : JNI_FALSE; |
| } |
| |
| JNI_METHOD(jobject, getKeySetIds)(JNIEnv * env, jobject self, jlong handle) |
| { |
| chip::DeviceLayer::StackLock lock; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| VerifyOrReturnValue(wrapper != nullptr, nullptr, ChipLogError(Controller, "wrapper is null")); |
| |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| chip::Credentials::GroupDataProvider * groupDataProvider = chip::Credentials::GetGroupDataProvider(); |
| auto it = groupDataProvider->IterateKeySets(wrapper->Controller()->GetFabricIndex()); |
| |
| jobject keySetIds; |
| err = chip::JniReferences::GetInstance().CreateArrayList(keySetIds); |
| |
| chip::Credentials::GroupDataProvider::KeySet keySet; |
| |
| if (it) |
| { |
| while (it->Next(keySet)) |
| { |
| jobject jKeySetId; |
| chip::JniReferences::GetInstance().CreateBoxedObject<jint>("java/lang/Integer", "(I)V", |
| static_cast<jint>(keySet.keyset_id), jKeySetId); |
| chip::JniReferences::GetInstance().AddToList(keySetIds, jKeySetId); |
| } |
| it->Release(); |
| } |
| |
| return keySetIds; |
| } |
| |
| JNI_METHOD(jobject, getKeySecurityPolicy)(JNIEnv * env, jobject self, jlong handle, int jKeySetId) |
| { |
| chip::DeviceLayer::StackLock lock; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| VerifyOrReturnValue(wrapper != nullptr, nullptr, ChipLogError(Controller, "wrapper is null")); |
| |
| chip::Credentials::GroupDataProvider * groupDataProvider = chip::Credentials::GetGroupDataProvider(); |
| auto it = groupDataProvider->IterateKeySets(wrapper->Controller()->GetFabricIndex()); |
| |
| chip::Credentials::GroupDataProvider::KeySet keySet; |
| |
| uint16_t keySetId = static_cast<uint16_t>(jKeySetId); |
| jobject wrapperKeyPolicy; |
| |
| if (it) |
| { |
| while (it->Next(keySet)) |
| { |
| if (keySet.keyset_id == keySetId) |
| { |
| jobject jKeyPolicy; |
| chip::JniReferences::GetInstance().CreateBoxedObject<jint>("java/lang/Integer", "(I)V", |
| static_cast<jint>(keySet.policy), jKeyPolicy); |
| chip::JniReferences::GetInstance().CreateOptional(jKeyPolicy, wrapperKeyPolicy); |
| it->Release(); |
| return wrapperKeyPolicy; |
| } |
| } |
| it->Release(); |
| } |
| chip::JniReferences::GetInstance().CreateOptional(nullptr, wrapperKeyPolicy); |
| return wrapperKeyPolicy; |
| } |
| |
| JNI_METHOD(jboolean, bindKeySet)(JNIEnv * env, jobject self, jlong handle, jint jGroupId, jint jKeySetId) |
| { |
| chip::DeviceLayer::StackLock lock; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| VerifyOrReturnValue(wrapper != nullptr, JNI_FALSE, ChipLogError(Controller, "wrapper is null")); |
| |
| chip::Credentials::GroupDataProvider * groupDataProvider = chip::Credentials::GetGroupDataProvider(); |
| auto iter = groupDataProvider->IterateGroupKeys(wrapper->Controller()->GetFabricIndex()); |
| size_t current_count = iter->Count(); |
| |
| iter->Release(); |
| CHIP_ERROR err = groupDataProvider->SetGroupKeyAt( |
| wrapper->Controller()->GetFabricIndex(), current_count, |
| chip::Credentials::GroupDataProvider::GroupKey(static_cast<uint16_t>(jGroupId), static_cast<uint16_t>(jKeySetId))); |
| return err == CHIP_NO_ERROR ? JNI_TRUE : JNI_FALSE; |
| } |
| |
| JNI_METHOD(jboolean, unbindKeySet)(JNIEnv * env, jobject self, jlong handle, jint jGroupId, jint jKeySetId) |
| { |
| chip::DeviceLayer::StackLock lock; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| VerifyOrReturnValue(wrapper != nullptr, JNI_FALSE, ChipLogError(Controller, "wrapper is null")); |
| |
| size_t index = 0; |
| chip::Credentials::GroupDataProvider * groupDataProvider = chip::Credentials::GetGroupDataProvider(); |
| auto iter = groupDataProvider->IterateGroupKeys(wrapper->Controller()->GetFabricIndex()); |
| size_t maxCount = iter->Count(); |
| chip::Credentials::GroupDataProvider::GroupKey groupKey; |
| |
| GroupId groupId = static_cast<GroupId>(jGroupId); |
| uint16_t keysetId = static_cast<uint16_t>(jKeySetId); |
| |
| while (iter->Next(groupKey)) |
| { |
| if (groupKey.group_id == groupId && groupKey.keyset_id == keysetId) |
| { |
| break; |
| } |
| index++; |
| } |
| iter->Release(); |
| if (index >= maxCount) |
| { |
| return JNI_FALSE; |
| } |
| |
| CHIP_ERROR err = groupDataProvider->RemoveGroupKeyAt(wrapper->Controller()->GetFabricIndex(), index); |
| |
| return err == CHIP_NO_ERROR ? JNI_TRUE : JNI_FALSE; |
| } |
| |
| JNI_METHOD(jboolean, addKeySet) |
| (JNIEnv * env, jobject self, jlong handle, jint jKeySetId, jint jKeyPolicy, jlong validityTime, jbyteArray epochKey) |
| { |
| chip::DeviceLayer::StackLock lock; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| VerifyOrReturnValue(wrapper != nullptr, JNI_FALSE, ChipLogError(Controller, "wrapper is null")); |
| |
| chip::Credentials::GroupDataProvider * groupDataProvider = chip::Credentials::GetGroupDataProvider(); |
| uint8_t compressed_fabric_id[sizeof(chip::FabricId)]; |
| chip::MutableByteSpan compressed_fabric_id_span(compressed_fabric_id); |
| VerifyOrReturnValue(wrapper->Controller()->GetCompressedFabricIdBytes(compressed_fabric_id_span) == CHIP_NO_ERROR, JNI_FALSE); |
| |
| chip::Credentials::GroupDataProvider::SecurityPolicy keyPolicy = |
| static_cast<chip::Credentials::GroupDataProvider::SecurityPolicy>(jKeyPolicy); |
| chip::JniByteArray jniEpochKey(env, epochKey); |
| size_t epochKeySize = static_cast<size_t>(jniEpochKey.size()); |
| if ((keyPolicy != chip::Credentials::GroupDataProvider::SecurityPolicy::kCacheAndSync && |
| keyPolicy != chip::Credentials::GroupDataProvider::SecurityPolicy::kTrustFirst) || |
| epochKeySize != chip::Credentials::GroupDataProvider::EpochKey::kLengthBytes) |
| { |
| return JNI_FALSE; |
| } |
| |
| chip::Credentials::GroupDataProvider::KeySet keySet(static_cast<uint16_t>(jKeySetId), keyPolicy, 1); |
| chip::Credentials::GroupDataProvider::EpochKey epoch_key; |
| epoch_key.start_time = static_cast<uint64_t>(validityTime); |
| memcpy(epoch_key.key, jniEpochKey.byteSpan().data(), chip::Credentials::GroupDataProvider::EpochKey::kLengthBytes); |
| memcpy(keySet.epoch_keys, &epoch_key, sizeof(chip::Credentials::GroupDataProvider::EpochKey)); |
| |
| VerifyOrReturnValue(groupDataProvider->SetKeySet(wrapper->Controller()->GetFabricIndex(), compressed_fabric_id_span, keySet) == |
| CHIP_NO_ERROR, |
| JNI_FALSE); |
| |
| return JNI_TRUE; |
| } |
| |
| JNI_METHOD(jboolean, removeKeySet)(JNIEnv * env, jobject self, jlong handle, jint jKeySetId) |
| { |
| chip::DeviceLayer::StackLock lock; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| VerifyOrReturnValue(wrapper != nullptr, JNI_FALSE, ChipLogError(Controller, "wrapper is null")); |
| |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| chip::Credentials::GroupDataProvider * groupDataProvider = chip::Credentials::GetGroupDataProvider(); |
| |
| size_t index = 0; |
| auto iter = groupDataProvider->IterateGroupKeys(wrapper->Controller()->GetFabricIndex()); |
| uint16_t keysetId = static_cast<uint16_t>(jKeySetId); |
| chip::Credentials::GroupDataProvider::GroupKey groupKey; |
| if (iter) |
| { |
| while (iter->Next(groupKey)) |
| { |
| if (groupKey.keyset_id == keysetId) |
| { |
| err = groupDataProvider->RemoveGroupKeyAt(wrapper->Controller()->GetFabricIndex(), index); |
| if (err != CHIP_NO_ERROR) |
| { |
| break; |
| } |
| } |
| index++; |
| } |
| iter->Release(); |
| if (err == CHIP_NO_ERROR) |
| { |
| err = groupDataProvider->RemoveKeySet(wrapper->Controller()->GetFabricIndex(), keysetId); |
| } |
| return err == CHIP_NO_ERROR ? JNI_TRUE : JNI_FALSE; |
| } |
| |
| return JNI_FALSE; |
| } |
| |
| JNI_METHOD(jint, getFabricIndex)(JNIEnv * env, jobject self, jlong handle) |
| { |
| chip::DeviceLayer::StackLock lock; |
| |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| return wrapper->Controller()->GetFabricIndex(); |
| } |
| |
| JNI_METHOD(void, shutdownSubscriptions) |
| (JNIEnv * env, jobject self, jobject handle, jobject fabricIndex, jobject peerNodeId, jobject subscriptionId) |
| { |
| chip::DeviceLayer::StackLock lock; |
| if (fabricIndex == nullptr && peerNodeId == nullptr && subscriptionId == nullptr) |
| { |
| app::InteractionModelEngine::GetInstance()->ShutdownAllSubscriptions(); |
| return; |
| } |
| |
| if (fabricIndex != nullptr && peerNodeId != nullptr && subscriptionId == nullptr) |
| { |
| jint jFabricIndex = chip::JniReferences::GetInstance().IntegerToPrimitive(fabricIndex); |
| jlong jPeerNodeId = chip::JniReferences::GetInstance().LongToPrimitive(peerNodeId); |
| app::InteractionModelEngine::GetInstance()->ShutdownSubscriptions(static_cast<chip::FabricIndex>(jFabricIndex), |
| static_cast<chip::NodeId>(jPeerNodeId)); |
| return; |
| } |
| |
| if (fabricIndex != nullptr && peerNodeId == nullptr && subscriptionId == nullptr) |
| { |
| jint jFabricIndex = chip::JniReferences::GetInstance().IntegerToPrimitive(fabricIndex); |
| app::InteractionModelEngine::GetInstance()->ShutdownSubscriptions(static_cast<chip::FabricIndex>(jFabricIndex)); |
| return; |
| } |
| |
| if (fabricIndex != nullptr && peerNodeId != nullptr && subscriptionId != nullptr) |
| { |
| jint jFabricIndex = chip::JniReferences::GetInstance().IntegerToPrimitive(fabricIndex); |
| jlong jPeerNodeId = chip::JniReferences::GetInstance().LongToPrimitive(peerNodeId); |
| jlong jSubscriptionId = chip::JniReferences::GetInstance().LongToPrimitive(subscriptionId); |
| app::InteractionModelEngine::GetInstance()->ShutdownSubscription( |
| chip::ScopedNodeId(static_cast<chip::NodeId>(jPeerNodeId), static_cast<chip::FabricIndex>(jFabricIndex)), |
| static_cast<chip::SubscriptionId>(jSubscriptionId)); |
| return; |
| } |
| ChipLogError(Controller, "Failed to shutdown subscriptions with correct input paramemeter"); |
| } |
| |
| JNI_METHOD(jstring, getIpAddress)(JNIEnv * env, jobject self, jlong handle, jlong deviceId) |
| { |
| chip::DeviceLayer::StackLock lock; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| chip::Inet::IPAddress addr; |
| uint16_t port; |
| char addrStr[50]; |
| |
| CHIP_ERROR err = wrapper->Controller()->GetPeerAddressAndPort(static_cast<chip::NodeId>(deviceId), addr, port); |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to get device address."); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| return nullptr; |
| } |
| |
| addr.ToString(addrStr); |
| return env->NewStringUTF(addrStr); |
| } |
| |
| JNI_METHOD(jlong, generateCompressedFabricId) |
| (JNIEnv * env, jobject self, jbyteArray rcac, jbyteArray noc) |
| { |
| chip::DeviceLayer::StackLock lock; |
| CompressedFabricId compressedFabricId; |
| FabricId fabricId; |
| NodeId nodeId; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| chip::JniByteArray jniRcac(env, rcac); |
| chip::JniByteArray jniNoc(env, noc); |
| err = ExtractNodeIdFabricIdCompressedFabricIdFromOpCerts(jniRcac.byteSpan(), jniNoc.byteSpan(), compressedFabricId, fabricId, |
| nodeId); |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to extract compressed fabric ID."); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| } |
| |
| return static_cast<jlong>(compressedFabricId); |
| } |
| |
| JNI_METHOD(jobject, getNetworkLocation)(JNIEnv * env, jobject self, jlong handle, jlong deviceId) |
| { |
| chip::DeviceLayer::StackLock lock; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| Transport::PeerAddress addr; |
| jobject networkLocation; |
| char addrStr[50]; |
| |
| CHIP_ERROR err = wrapper->Controller()->GetPeerAddress(static_cast<chip::NodeId>(deviceId), addr); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to get device address."); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| return nullptr; |
| } |
| |
| addr.GetIPAddress().ToString(addrStr); |
| |
| err = N2J_NetworkLocation(env, env->NewStringUTF(addrStr), static_cast<jint>(addr.GetPort()), |
| static_cast<jint>(addr.GetInterface().GetPlatformInterface()), networkLocation); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to create NetworkLocation"); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| } |
| |
| return networkLocation; |
| } |
| |
| JNI_METHOD(jlong, getCompressedFabricId)(JNIEnv * env, jobject self, jlong handle) |
| { |
| chip::DeviceLayer::StackLock lock; |
| |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| return static_cast<jlong>(wrapper->Controller()->GetCompressedFabricId()); |
| } |
| |
| JNI_METHOD(jlong, getControllerNodeId)(JNIEnv * env, jobject self, jlong handle) |
| { |
| chip::DeviceLayer::StackLock lock; |
| |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| return static_cast<jlong>(wrapper->Controller()->GetNodeId()); |
| } |
| |
| JNI_METHOD(void, discoverCommissionableNodes)(JNIEnv * env, jobject self, jlong handle) |
| { |
| chip::DeviceLayer::StackLock lock; |
| |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| chip::Dnssd::DiscoveryFilter filter(Dnssd::DiscoveryFilterType::kNone, (uint64_t) 0); |
| |
| CHIP_ERROR err = wrapper->Controller()->DiscoverCommissionableNodes(filter); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "Failed to discoverCommissionableNodes"); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| } |
| } |
| |
| JNI_METHOD(jobject, getDiscoveredDevice)(JNIEnv * env, jobject self, jlong handle, jint idx) |
| { |
| chip::DeviceLayer::StackLock lock; |
| |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| const Dnssd::DiscoveredNodeData * data = wrapper->Controller()->GetDiscoveredDevice(idx); |
| |
| if (data == nullptr) |
| { |
| ChipLogError(Controller, "GetDiscoveredDevice - not found"); |
| return nullptr; |
| } |
| |
| jclass discoveredDeviceCls = env->FindClass("chip/devicecontroller/DiscoveredDevice"); |
| jmethodID constructor = env->GetMethodID(discoveredDeviceCls, "<init>", "()V"); |
| |
| jfieldID discrminatorID = env->GetFieldID(discoveredDeviceCls, "discriminator", "J"); |
| jfieldID ipAddressID = env->GetFieldID(discoveredDeviceCls, "ipAddress", "Ljava/lang/String;"); |
| jfieldID portID = env->GetFieldID(discoveredDeviceCls, "port", "I"); |
| jfieldID deviceTypeID = env->GetFieldID(discoveredDeviceCls, "deviceType", "J"); |
| jfieldID vendorIdID = env->GetFieldID(discoveredDeviceCls, "vendorId", "I"); |
| jfieldID productIdID = env->GetFieldID(discoveredDeviceCls, "productId", "I"); |
| jfieldID rotatingIdID = env->GetFieldID(discoveredDeviceCls, "rotatingId", "[B"); |
| jfieldID instanceNameID = env->GetFieldID(discoveredDeviceCls, "instanceName", "Ljava/lang/String;"); |
| jfieldID deviceNameID = env->GetFieldID(discoveredDeviceCls, "deviceName", "Ljava/lang/String;"); |
| jfieldID pairingInstructionID = env->GetFieldID(discoveredDeviceCls, "pairingInstruction", "Ljava/lang/String;"); |
| |
| jmethodID setCommissioningModeID = env->GetMethodID(discoveredDeviceCls, "setCommissioningMode", "(I)V"); |
| jmethodID setPairingHintID = env->GetMethodID(discoveredDeviceCls, "setPairingHint", "(I)V"); |
| |
| jobject discoveredObj = env->NewObject(discoveredDeviceCls, constructor); |
| |
| env->SetLongField(discoveredObj, discrminatorID, data->commissionData.longDiscriminator); |
| |
| char ipAddress[100]; |
| data->resolutionData.ipAddress[0].ToString(ipAddress, 100); |
| jstring jniipAdress = env->NewStringUTF(ipAddress); |
| |
| env->SetObjectField(discoveredObj, ipAddressID, jniipAdress); |
| env->SetIntField(discoveredObj, portID, static_cast<jint>(data->resolutionData.port)); |
| env->SetLongField(discoveredObj, deviceTypeID, static_cast<jlong>(data->commissionData.deviceType)); |
| env->SetIntField(discoveredObj, vendorIdID, static_cast<jint>(data->commissionData.vendorId)); |
| env->SetIntField(discoveredObj, productIdID, static_cast<jint>(data->commissionData.productId)); |
| |
| jbyteArray jRotatingId; |
| CHIP_ERROR err = JniReferences::GetInstance().N2J_ByteArray( |
| env, data->commissionData.rotatingId, static_cast<jsize>(data->commissionData.rotatingIdLen), jRotatingId); |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "jRotatingId N2J_ByteArray error : %" CHIP_ERROR_FORMAT, err.Format()); |
| return nullptr; |
| } |
| env->SetObjectField(discoveredObj, rotatingIdID, static_cast<jobject>(jRotatingId)); |
| env->SetObjectField(discoveredObj, instanceNameID, env->NewStringUTF(data->commissionData.instanceName)); |
| env->SetObjectField(discoveredObj, deviceNameID, env->NewStringUTF(data->commissionData.deviceName)); |
| env->SetObjectField(discoveredObj, pairingInstructionID, env->NewStringUTF(data->commissionData.pairingInstruction)); |
| |
| env->CallVoidMethod(discoveredObj, setCommissioningModeID, static_cast<jint>(data->commissionData.commissioningMode)); |
| env->CallVoidMethod(discoveredObj, setPairingHintID, static_cast<jint>(data->commissionData.pairingHint)); |
| |
| return discoveredObj; |
| } |
| |
| JNI_METHOD(jboolean, openPairingWindow)(JNIEnv * env, jobject self, jlong handle, jlong devicePtr, jint duration) |
| { |
| chip::DeviceLayer::StackLock lock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| DeviceProxy * chipDevice = reinterpret_cast<DeviceProxy *>(devicePtr); |
| if (chipDevice == nullptr) |
| { |
| ChipLogProgress(Controller, "Could not cast device pointer to Device object"); |
| return false; |
| } |
| |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| err = AutoCommissioningWindowOpener::OpenBasicCommissioningWindow(wrapper->Controller(), chipDevice->GetDeviceId(), |
| System::Clock::Seconds16(duration)); |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "OpenPairingWindow failed: %" CHIP_ERROR_FORMAT, err.Format()); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| JNI_METHOD(jboolean, openPairingWindowWithPIN) |
| (JNIEnv * env, jobject self, jlong handle, jlong devicePtr, jint duration, jlong iteration, jint discriminator, |
| jobject setupPinCode) |
| { |
| VerifyOrReturnValue(chip::CanCastTo<uint32_t>(iteration), false); |
| |
| chip::DeviceLayer::StackLock lock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| DeviceProxy * chipDevice = reinterpret_cast<DeviceProxy *>(devicePtr); |
| if (chipDevice == nullptr) |
| { |
| ChipLogProgress(Controller, "Could not cast device pointer to Device object"); |
| return false; |
| } |
| |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| Optional<uint32_t> pinCode = Optional<uint32_t>(); |
| if (setupPinCode != nullptr) |
| { |
| jlong jsetupPinCode = chip::JniReferences::GetInstance().LongToPrimitive(setupPinCode); |
| pinCode = MakeOptional(static_cast<uint32_t>(jsetupPinCode)); |
| } |
| |
| chip::SetupPayload setupPayload; |
| err = AutoCommissioningWindowOpener::OpenCommissioningWindow( |
| wrapper->Controller(), chipDevice->GetDeviceId(), System::Clock::Seconds16(duration), static_cast<uint32_t>(iteration), |
| static_cast<uint16_t>(discriminator), pinCode, NullOptional, setupPayload); |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "OpenPairingWindow failed: %" CHIP_ERROR_FORMAT, err.Format()); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| JNI_METHOD(jboolean, openPairingWindowCallback) |
| (JNIEnv * env, jobject self, jlong handle, jlong devicePtr, jint duration, jobject jcallback) |
| { |
| chip::DeviceLayer::StackLock lock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| DeviceProxy * chipDevice = reinterpret_cast<DeviceProxy *>(devicePtr); |
| if (chipDevice == nullptr) |
| { |
| ChipLogProgress(Controller, "Could not cast device pointer to Device object"); |
| return false; |
| } |
| |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| err = AndroidCommissioningWindowOpener::OpenBasicCommissioningWindow(wrapper->Controller(), chipDevice->GetDeviceId(), |
| System::Clock::Seconds16(duration), jcallback); |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "OpenPairingWindow failed: %" CHIP_ERROR_FORMAT, err.Format()); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| JNI_METHOD(jboolean, openPairingWindowWithPINCallback) |
| (JNIEnv * env, jobject self, jlong handle, jlong devicePtr, jint duration, jlong iteration, jint discriminator, |
| jobject setupPinCode, jobject jcallback) |
| { |
| VerifyOrReturnValue(chip::CanCastTo<uint32_t>(iteration), false); |
| |
| chip::DeviceLayer::StackLock lock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| DeviceProxy * chipDevice = reinterpret_cast<DeviceProxy *>(devicePtr); |
| if (chipDevice == nullptr) |
| { |
| ChipLogProgress(Controller, "Could not cast device pointer to Device object"); |
| return false; |
| } |
| |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| Optional<uint32_t> pinCode = Optional<uint32_t>(); |
| if (setupPinCode != nullptr) |
| { |
| jlong jsetupPinCode = chip::JniReferences::GetInstance().LongToPrimitive(setupPinCode); |
| pinCode = MakeOptional(static_cast<uint32_t>(jsetupPinCode)); |
| } |
| |
| chip::SetupPayload setupPayload; |
| err = AndroidCommissioningWindowOpener::OpenCommissioningWindow( |
| wrapper->Controller(), chipDevice->GetDeviceId(), System::Clock::Seconds16(duration), static_cast<uint32_t>(iteration), |
| static_cast<uint16_t>(discriminator), pinCode, NullOptional, jcallback, setupPayload); |
| |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "OpenPairingWindow failed: %" CHIP_ERROR_FORMAT, err.Format()); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| JNI_METHOD(void, shutdownCommissioning) |
| (JNIEnv * env, jobject self, jlong handle) |
| { |
| chip::DeviceLayer::StackLock lock; |
| |
| // Stop the IO thread, so that the controller can be safely shut down. |
| StopIOThread(); |
| |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| wrapper->Shutdown(); |
| } |
| |
| JNI_METHOD(jbyteArray, getAttestationChallenge) |
| (JNIEnv * env, jobject self, jlong handle, jlong devicePtr) |
| { |
| chip::DeviceLayer::StackLock lock; |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| ByteSpan attestationChallenge; |
| jbyteArray attestationChallengeJbytes = nullptr; |
| |
| DeviceProxy * chipDevice = reinterpret_cast<DeviceProxy *>(devicePtr); |
| if (chipDevice == nullptr) |
| { |
| ChipLogProgress(Controller, "Could not cast device pointer to Device object"); |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, CHIP_ERROR_INCORRECT_STATE); |
| return nullptr; |
| } |
| |
| err = chipDevice->GetAttestationChallenge(attestationChallenge); |
| SuccessOrExit(err); |
| VerifyOrExit(attestationChallenge.size() == 16, err = CHIP_ERROR_INVALID_ARGUMENT); |
| |
| err = JniReferences::GetInstance().N2J_ByteArray(env, attestationChallenge.data(), |
| static_cast<jsize>(attestationChallenge.size()), attestationChallengeJbytes); |
| SuccessOrExit(err); |
| |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| } |
| return attestationChallengeJbytes; |
| } |
| |
| JNI_METHOD(void, deleteDeviceController)(JNIEnv * env, jobject self, jlong handle) |
| { |
| chip::DeviceLayer::StackLock lock; |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| ChipLogProgress(Controller, "deleteDeviceController() called"); |
| |
| if (wrapper != nullptr) |
| { |
| delete wrapper; |
| } |
| } |
| |
| JNI_METHOD(jobject, computePaseVerifier) |
| (JNIEnv * env, jobject self, jlong handle, jlong devicePtr, jlong setupPincode, jlong iterations, jbyteArray salt) |
| { |
| chip::DeviceLayer::StackLock lock; |
| |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| jobject params; |
| jbyteArray verifierBytes; |
| Spake2pVerifier verifier; |
| Spake2pVerifierSerialized serializedVerifier; |
| MutableByteSpan serializedVerifierSpan(serializedVerifier); |
| JniByteArray jniSalt(env, salt); |
| |
| ChipLogProgress(Controller, "computePaseVerifier() called"); |
| |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| |
| VerifyOrExit(chip::CanCastTo<uint32_t>(iterations), err = CHIP_ERROR_INVALID_ARGUMENT); |
| VerifyOrExit(chip::CanCastTo<uint32_t>(setupPincode), err = CHIP_ERROR_INVALID_ARGUMENT); |
| |
| err = wrapper->Controller()->ComputePASEVerifier(static_cast<uint32_t>(iterations), static_cast<uint32_t>(setupPincode), |
| jniSalt.byteSpan(), verifier); |
| SuccessOrExit(err); |
| |
| err = verifier.Serialize(serializedVerifierSpan); |
| SuccessOrExit(err); |
| |
| err = JniReferences::GetInstance().N2J_ByteArray(env, serializedVerifier, kSpake2p_VerifierSerialized_Length, verifierBytes); |
| SuccessOrExit(err); |
| |
| err = N2J_PaseVerifierParams(env, setupPincode, verifierBytes, params); |
| SuccessOrExit(err); |
| return params; |
| exit: |
| if (err != CHIP_NO_ERROR) |
| { |
| JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err); |
| } |
| return nullptr; |
| } |
| |
| JNI_METHOD(jbyteArray, validateAndExtractCSR)(JNIEnv * env, jclass clazz, jbyteArray jCsrElements, jbyteArray jCsrNonce) |
| { |
| |
| chip::JniByteArray csrElements(env, jCsrElements); |
| chip::JniByteArray csrNonce(env, jCsrNonce); |
| |
| chip::ByteSpan csrSpan; |
| chip::ByteSpan csrNonceSpan; |
| chip::ByteSpan vendor_reserved1, vendor_reserved2, vendor_reserved3; |
| CHIP_ERROR err = chip::Credentials::DeconstructNOCSRElements(csrElements.byteSpan(), csrSpan, csrNonceSpan, vendor_reserved1, |
| vendor_reserved2, vendor_reserved3); |
| |
| VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr, |
| ChipLogError(Controller, "CsrElement decoding error: %" CHIP_ERROR_FORMAT, err.Format())); |
| VerifyOrReturnValue(csrNonceSpan.size() == Controller::kCSRNonceLength, nullptr, |
| ChipLogError(Controller, "csrNonce size is invalid")); |
| |
| // Verify that Nonce matches with what we sent |
| VerifyOrReturnValue(csrNonceSpan.data_equal(csrNonce.byteSpan()), nullptr, |
| ChipLogError(Controller, "csrNonce is not matched!")); |
| |
| jbyteArray javaCsr; |
| chip::JniReferences::GetInstance().N2J_ByteArray(chip::JniReferences::GetInstance().GetEnvForCurrentThread(), csrSpan.data(), |
| static_cast<jsize>(csrSpan.size()), javaCsr); |
| return javaCsr; |
| } |
| |
| JNI_METHOD(jobject, getICDClientInfo)(JNIEnv * env, jobject self, jlong handle, jint jFabricIndex) |
| { |
| chip::DeviceLayer::StackLock lock; |
| |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| |
| jobject jInfo = nullptr; |
| chip::app::ICDClientInfo info; |
| chip::FabricIndex fabricIndex = static_cast<chip::FabricIndex>(jFabricIndex); |
| |
| ChipLogProgress(Controller, "getICDClientInfo(%u) called", fabricIndex); |
| AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle); |
| VerifyOrReturnValue(wrapper != nullptr, nullptr, ChipLogError(Controller, "wrapper is null")); |
| |
| err = JniReferences::GetInstance().CreateArrayList(jInfo); |
| VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr, |
| ChipLogError(Controller, "CreateArrayList failed!: %" CHIP_ERROR_FORMAT, err.Format())); |
| |
| auto iter = wrapper->getICDClientStorage()->IterateICDClientInfo(); |
| VerifyOrReturnValue(iter != nullptr, nullptr, ChipLogError(Controller, "IterateICDClientInfo failed!")); |
| app::DefaultICDClientStorage::ICDClientInfoIteratorWrapper clientInfoIteratorWrapper(iter); |
| |
| jmethodID constructor; |
| jclass infoClass; |
| JniLocalReferenceScope scope(env); |
| |
| err = JniReferences::GetInstance().GetLocalClassRef(env, "chip/devicecontroller/ICDClientInfo", infoClass); |
| VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr, |
| ChipLogError(Controller, "Find ICDClientInfo class: %" CHIP_ERROR_FORMAT, err.Format())); |
| |
| env->ExceptionClear(); |
| constructor = env->GetMethodID(infoClass, "<init>", "(JJJJ[B[B)V"); |
| VerifyOrReturnValue(constructor != nullptr, nullptr, ChipLogError(Controller, "Find GetMethodID error!")); |
| |
| while (iter->Next(info)) |
| { |
| jbyteArray jIcdAesKey = nullptr; |
| jbyteArray jIcdHmacKey = nullptr; |
| jobject jICDClientInfo = nullptr; |
| |
| if (info.peer_node.GetFabricIndex() != fabricIndex) |
| { |
| continue; |
| } |
| |
| err = chip::JniReferences::GetInstance().N2J_ByteArray(env, info.aes_key_handle.As<Crypto::Symmetric128BitsKeyByteArray>(), |
| chip::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES, jIcdAesKey); |
| VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr, |
| ChipLogError(Controller, "ICD AES KEY N2J_ByteArray error!: %" CHIP_ERROR_FORMAT, err.Format())); |
| |
| err = chip::JniReferences::GetInstance().N2J_ByteArray(env, info.hmac_key_handle.As<Crypto::Symmetric128BitsKeyByteArray>(), |
| chip::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES, jIcdHmacKey); |
| VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr, |
| ChipLogError(Controller, "ICD HMAC KEY N2J_ByteArray error!: %" CHIP_ERROR_FORMAT, err.Format())); |
| |
| jICDClientInfo = (jobject) env->NewObject(infoClass, constructor, static_cast<jlong>(info.peer_node.GetNodeId()), |
| static_cast<jlong>(info.start_icd_counter), static_cast<jlong>(info.offset), |
| static_cast<jlong>(info.monitored_subject), jIcdAesKey, jIcdHmacKey); |
| |
| err = JniReferences::GetInstance().AddToList(jInfo, jICDClientInfo); |
| VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr, |
| ChipLogError(Controller, "AddToList error!: %" CHIP_ERROR_FORMAT, err.Format())); |
| } |
| |
| return jInfo; |
| } |
| |
| JNI_METHOD(void, subscribe) |
| (JNIEnv * env, jclass clz, jlong handle, jlong callbackHandle, jlong devicePtr, jobject attributePathList, jobject eventPathList, |
| jobject dataVersionFilterList, jint minInterval, jint maxInterval, jboolean keepSubscriptions, jboolean isFabricFiltered, |
| jint imTimeoutMs, jobject eventMin) |
| { |
| CHIP_ERROR err = subscribe(env, handle, callbackHandle, devicePtr, attributePathList, eventPathList, dataVersionFilterList, |
| minInterval, maxInterval, keepSubscriptions, isFabricFiltered, imTimeoutMs, eventMin); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "JNI IM Subscribe Error: %" CHIP_ERROR_FORMAT, err.Format()); |
| } |
| } |
| |
| JNI_METHOD(void, read) |
| (JNIEnv * env, jclass clz, jlong handle, jlong callbackHandle, jlong devicePtr, jobject attributePathList, jobject eventPathList, |
| jobject dataVersionFilterList, jboolean isFabricFiltered, jint imTimeoutMs, jobject eventMin) |
| { |
| CHIP_ERROR err = read(env, handle, callbackHandle, devicePtr, attributePathList, eventPathList, dataVersionFilterList, |
| isFabricFiltered, imTimeoutMs, eventMin); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "JNI IM Read Error: %" CHIP_ERROR_FORMAT, err.Format()); |
| } |
| } |
| |
| JNI_METHOD(void, write) |
| (JNIEnv * env, jclass clz, jlong handle, jlong callbackHandle, jlong devicePtr, jobject attributeList, jint timedRequestTimeoutMs, |
| jint imTimeoutMs) |
| { |
| CHIP_ERROR err = write(env, handle, callbackHandle, devicePtr, attributeList, timedRequestTimeoutMs, imTimeoutMs); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "JNI IM Write Error: %" CHIP_ERROR_FORMAT, err.Format()); |
| } |
| } |
| |
| JNI_METHOD(void, invoke) |
| (JNIEnv * env, jclass clz, jlong handle, jlong callbackHandle, jlong devicePtr, jobject invokeElement, jint timedRequestTimeoutMs, |
| jint imTimeoutMs) |
| { |
| CHIP_ERROR err = invoke(env, handle, callbackHandle, devicePtr, invokeElement, timedRequestTimeoutMs, imTimeoutMs); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "JNI IM Invoke Error: %" CHIP_ERROR_FORMAT, err.Format()); |
| } |
| } |
| |
| JNI_METHOD(void, extendableInvoke) |
| (JNIEnv * env, jclass clz, jlong handle, jlong callbackHandle, jlong devicePtr, jobject invokeElementList, |
| jint timedRequestTimeoutMs, jint imTimeoutMs) |
| { |
| CHIP_ERROR err = |
| extendableInvoke(env, handle, callbackHandle, devicePtr, invokeElementList, timedRequestTimeoutMs, imTimeoutMs); |
| if (err != CHIP_NO_ERROR) |
| { |
| ChipLogError(Controller, "JNI IM Batch Invoke Error: %" CHIP_ERROR_FORMAT, err.Format()); |
| } |
| } |
| |
| void * IOThreadMain(void * arg) |
| { |
| JNIEnv * env; |
| JavaVMAttachArgs attachArgs; |
| |
| // Attach the IO thread to the JVM as a daemon thread. |
| // This allows the JVM to shutdown without waiting for this thread to exit. |
| attachArgs.version = JNI_VERSION_1_6; |
| attachArgs.name = (char *) "CHIP Device Controller IO Thread"; |
| attachArgs.group = nullptr; |
| #ifdef __ANDROID__ |
| sJVM->AttachCurrentThreadAsDaemon(&env, (void *) &attachArgs); |
| #else |
| sJVM->AttachCurrentThreadAsDaemon((void **) &env, (void *) &attachArgs); |
| #endif |
| |
| ChipLogProgress(Controller, "IO thread starting"); |
| chip::DeviceLayer::PlatformMgr().RunEventLoop(); |
| ChipLogProgress(Controller, "IO thread ending"); |
| |
| // Detach the thread from the JVM. |
| sJVM->DetachCurrentThread(); |
| |
| return nullptr; |
| } |
| |
| // NOTE: This function SHALL be called with the stack lock held. |
| CHIP_ERROR StopIOThread() |
| { |
| if (sIOThread != PTHREAD_NULL) |
| { |
| ChipLogProgress(Controller, "IO thread stopping"); |
| chip::DeviceLayer::StackUnlock unlock; |
| |
| chip::DeviceLayer::PlatformMgr().StopEventLoopTask(); |
| |
| pthread_join(sIOThread, nullptr); |
| sIOThread = PTHREAD_NULL; |
| } |
| |
| return CHIP_NO_ERROR; |
| } |
| |
| CHIP_ERROR N2J_PaseVerifierParams(JNIEnv * env, jlong setupPincode, jbyteArray paseVerifier, jobject & outParams) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| jmethodID constructor; |
| jclass paramsClass; |
| |
| err = JniReferences::GetInstance().GetLocalClassRef(env, "chip/devicecontroller/PaseVerifierParams", paramsClass); |
| SuccessOrExit(err); |
| |
| env->ExceptionClear(); |
| constructor = env->GetMethodID(paramsClass, "<init>", "(J[B)V"); |
| VerifyOrExit(constructor != nullptr, err = CHIP_JNI_ERROR_METHOD_NOT_FOUND); |
| |
| outParams = (jobject) env->NewObject(paramsClass, constructor, setupPincode, paseVerifier); |
| |
| VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN); |
| exit: |
| return err; |
| } |
| |
| CHIP_ERROR N2J_NetworkLocation(JNIEnv * env, jstring ipAddress, jint port, jint interfaceIndex, jobject & outLocation) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| jmethodID constructor; |
| jclass locationClass; |
| |
| err = JniReferences::GetInstance().GetLocalClassRef(env, "chip/devicecontroller/NetworkLocation", locationClass); |
| SuccessOrExit(err); |
| |
| env->ExceptionClear(); |
| constructor = env->GetMethodID(locationClass, "<init>", "(Ljava/lang/String;II)V"); |
| VerifyOrExit(constructor != nullptr, err = CHIP_JNI_ERROR_METHOD_NOT_FOUND); |
| |
| outLocation = (jobject) env->NewObject(locationClass, constructor, ipAddress, port, interfaceIndex); |
| |
| VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN); |
| exit: |
| return err; |
| } |