blob: fd87516e19dd0a4c59962da2b3f03a9691b19a36 [file] [log] [blame]
/*
* 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 <lib/support/CHIPJNIError.h>
#include <lib/support/JniReferences.h>
#include <lib/support/JniTypeWrappers.h>
#include <app/AttributePathParams.h>
#include <app/InteractionModelEngine.h>
#include <app/ReadClient.h>
#include <app/WriteClient.h>
#include <app/util/error-mapping.h>
#include <atomic>
#include <ble/BleUUID.h>
#include <controller/CHIPDeviceController.h>
#include <controller/CommissioningWindowOpener.h>
#include <controller/java/AndroidClusterExceptions.h>
#include <credentials/CHIPCert.h>
#include <jni.h>
#include <lib/support/CHIPMem.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/ErrorStr.h>
#include <lib/support/SafeInt.h>
#include <lib/support/ThreadOperationalDataset.h>
#include <lib/support/logging/CHIPLogging.h>
#include <platform/KeyValueStoreManager.h>
#include <protocols/Protocols.h>
#include <pthread.h>
#include <system/SystemClock.h>
#include <vector>
#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);
static CHIP_ERROR GetChipPathIdValue(jobject chipPathId, uint32_t wildcardValue, uint32_t & outValue);
static CHIP_ERROR ParseAttributePathList(jobject attributePathList,
std::vector<app::AttributePathParams> & outAttributePathParamsList);
static CHIP_ERROR ParseAttributePath(jobject attributePath, EndpointId & outEndpointId, ClusterId & outClusterId,
AttributeId & outAttributeId);
static CHIP_ERROR ParseEventPathList(jobject eventPathList, std::vector<app::EventPathParams> & outEventPathParamsList);
static CHIP_ERROR ParseEventPath(jobject eventPath, EndpointId & outEndpointId, ClusterId & outClusterId, EventId & outEventId,
bool & outIsUrgent);
static CHIP_ERROR IsWildcardChipPathId(jobject chipPathId, bool & isWildcard);
namespace {
JavaVM * sJVM;
pthread_t sIOThread = PTHREAD_NULL;
jclass sChipDeviceControllerExceptionCls = NULL;
} // 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 != NULL, err = CHIP_JNI_ERROR_NO_ENV);
ChipLogProgress(Controller, "Loading Java class references.");
// Get various class references need by the API.
err = JniReferences::GetInstance().GetClassRef(env, "chip/devicecontroller/ChipDeviceControllerException",
sChipDeviceControllerExceptionCls);
SuccessOrExit(err);
ChipLogProgress(Controller, "Java class references loaded.");
#ifndef JAVA_MATTER_CONTROLLER_TEST
err = AndroidChipPlatformJNI_OnLoad(jvm, reserved);
SuccessOrExit(err);
#endif // JAVA_MATTER_CONTROLLER_TEST
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 = NULL;
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;
err = chip::JniReferences::GetInstance().FindMethod(env, controllerParams, "getRootCertificate", "()[B", &getRootCertificate);
VerifyOrReturnValue(err == CHIP_NO_ERROR, err.AsInteger());
jmethodID getIntermediateCertificate;
err = chip::JniReferences::GetInstance().FindMethod(env, controllerParams, "getIntermediateCertificate", "()[B",
&getIntermediateCertificate);
VerifyOrReturnValue(err == CHIP_NO_ERROR, err.AsInteger());
jmethodID getOperationalCertificate;
err = chip::JniReferences::GetInstance().FindMethod(env, controllerParams, "getOperationalCertificate", "()[B",
&getOperationalCertificate);
VerifyOrReturnValue(err == CHIP_NO_ERROR, err.AsInteger());
jmethodID getIpk;
err = chip::JniReferences::GetInstance().FindMethod(env, controllerParams, "getIpk", "()[B", &getIpk);
VerifyOrReturnValue(err == CHIP_NO_ERROR, err.AsInteger());
jmethodID getAdminSubject;
err = chip::JniReferences::GetInstance().FindMethod(env, controllerParams, "getAdminSubject", "()J", &getAdminSubject);
VerifyOrReturnValue(err == CHIP_NO_ERROR, err.AsInteger());
jbyteArray rootCertificate = (jbyteArray) env->CallObjectMethod(controllerParams, getRootCertificate);
VerifyOrReturnValue(rootCertificate != nullptr, CHIP_ERROR_BAD_REQUEST.AsInteger());
jbyteArray intermediateCertificate = (jbyteArray) env->CallObjectMethod(controllerParams, getIntermediateCertificate);
VerifyOrReturnValue(intermediateCertificate != nullptr, CHIP_ERROR_BAD_REQUEST.AsInteger());
jbyteArray operationalCertificate = (jbyteArray) env->CallObjectMethod(controllerParams, getOperationalCertificate);
VerifyOrReturnValue(operationalCertificate != nullptr, CHIP_ERROR_BAD_REQUEST.AsInteger());
// use ipk and adminSubject from CommissioningParameters if not set in ControllerParams
CommissioningParameters commissioningParams = wrapper->GetCommissioningParameters();
Optional<Crypto::IdentityProtectionKeySpan> ipkOptional;
uint8_t ipkValue[CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES];
Crypto::IdentityProtectionKeySpan ipkTempSpan(ipkValue);
jbyteArray ipk = (jbyteArray) env->CallObjectMethod(controllerParams, getIpk);
if (ipk != nullptr)
{
JniByteArray jByteArrayIpk(env, ipk);
VerifyOrReturnValue(jByteArrayIpk.byteSpan().size() == sizeof(ipkValue), CHIP_ERROR_INTERNAL.AsInteger());
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());
}
Optional<NodeId> adminSubjectOptional;
uint64_t adminSubject = 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());
}
#endif // JAVA_MATTER_CONTROLLER_TEST
return err.AsInteger();
}
JNI_METHOD(jlong, newDeviceController)(JNIEnv * env, jobject self, jobject controllerParams)
{
chip::DeviceLayer::StackLock lock;
CHIP_ERROR err = CHIP_NO_ERROR;
AndroidDeviceControllerWrapper * wrapper = NULL;
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 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);
{
uint64_t fabricId = env->CallLongMethod(controllerParams, getFabricId);
uint16_t listenPort = env->CallIntMethod(controllerParams, getUdpListenPort);
uint16_t controllerVendorId = 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 = env->CallIntMethod(controllerParams, getFailsafeTimerSeconds);
uint16_t caseFailsafeTimerSeconds = env->CallIntMethod(controllerParams, getCASEFailsafeTimerSeconds);
bool attemptNetworkScanWiFi = env->CallBooleanMethod(controllerParams, getAttemptNetworkScanWiFi);
bool attemptNetworkScanThread = env->CallBooleanMethod(controllerParams, getAttemptNetworkScanThread);
bool skipCommissioningComplete = env->CallBooleanMethod(controllerParams, getSkipCommissioningComplete);
uint64_t adminSubject = env->CallLongMethod(controllerParams, getAdminSubject);
jobject countryCodeOptional = env->CallObjectMethod(controllerParams, getCountryCode);
jobject regulatoryLocationOptional = env->CallObjectMethod(controllerParams, getRegulatoryLocation);
#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, &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 countryCode;
err = chip::JniReferences::GetInstance().GetOptionalValue(countryCodeOptional, countryCode);
SuccessOrExit(err);
if (countryCode != nullptr)
{
jstring countryCodeStr = static_cast<jstring>(countryCode);
JniUtfString countryCodeJniString(env, countryCodeStr);
VerifyOrExit(countryCodeJniString.size() == 2, err = CHIP_ERROR_INVALID_ARGUMENT);
chip::Controller::CommissioningParameters commissioningParams = wrapper->GetCommissioningParameters();
commissioningParams.SetCountryCode(countryCodeJniString.charSpan());
// The wrapper internally has reserved storage for the country code and will copy the value.
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, NULL, IOThreadMain, NULL);
VerifyOrExit(pthreadErr == 0, err = CHIP_ERROR_POSIX(pthreadErr));
}
result = wrapper->ToJNIHandle();
exit:
if (err != CHIP_NO_ERROR)
{
if (wrapper != NULL)
{
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;
jobject deviceAttestationDelegateRef = env->NewGlobalRef(deviceAttestationDelegate);
VerifyOrExit(deviceAttestationDelegateRef != nullptr, err = CHIP_JNI_ERROR_NULL_OBJECT);
JniReferences::GetInstance().GetClassRef(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(deviceAttestationDelegateRef, timeoutSecs,
shouldWaitAfterDeviceAttestation);
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, setAttestationTrustStoreDelegate)
(JNIEnv * env, jobject self, jlong handle, jobject attestationTrustStoreDelegate)
{
chip::DeviceLayer::StackLock lock;
CHIP_ERROR err = CHIP_NO_ERROR;
AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle);
ChipLogProgress(Controller, "setAttestationTrustStoreDelegate() called");
if (attestationTrustStoreDelegate != nullptr)
{
jobject attestationTrustStoreDelegateRef = env->NewGlobalRef(attestationTrustStoreDelegate);
err = wrapper->UpdateAttestationTrustStoreBridge(attestationTrustStoreDelegateRef);
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, 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(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)
{
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());
}
if (wrapper->GetDeviceAttestationDelegateBridge() != nullptr)
{
commissioningParams.SetDeviceAttestationDelegate(wrapper->GetDeviceAttestationDelegateBridge());
}
err = wrapper->Controller()->PairDevice(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)
{
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(discriminator)
.SetSetupPINCode(static_cast<uint32_t>(pinCode))
.SetPeerAddress(Transport::PeerAddress::UDP(const_cast<char *>(addrJniString.c_str()), port));
CommissioningParameters commissioningParams = wrapper->GetCommissioningParameters();
if (csrNonce != nullptr)
{
JniByteArray jniCsrNonce(env, csrNonce);
commissioningParams.SetCSRNonce(jniCsrNonce.byteSpan());
}
if (wrapper->GetDeviceAttestationDelegateBridge() != nullptr)
{
commissioningParams.SetDeviceAttestationDelegate(wrapper->GetDeviceAttestationDelegateBridge());
}
err = wrapper->Controller()->PairDevice(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)
{
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);
}
if (wrapper->GetDeviceAttestationDelegateBridge() != nullptr)
{
commissioningParams.SetDeviceAttestationDelegate(wrapper->GetDeviceAttestationDelegateBridge());
}
err = wrapper->Controller()->PairDevice(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(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()), port));
err = wrapper->Controller()->EstablishPASEConnection(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->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->UpdateCommissioningParameters(commissioningParams);
if (err != CHIP_NO_ERROR)
{
ChipLogError(Controller, "UpdateCommissioningParameters 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(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<uint32_t>(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<uint32_t>(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(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(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);
VerifyOrReturn(connectedDeviceCallback != nullptr, ChipLogError(Controller, "GetConnectedDeviceCallback handle is nullptr"));
err = wrapper->Controller()->GetConnectedDevice(nodeId, &connectedDeviceCallback->mOnSuccess,
&connectedDeviceCallback->mOnFailure);
VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Error invoking GetConnectedDevice"));
}
JNI_METHOD(void, releaseOperationalDevicePointer)(JNIEnv * env, jobject self, jlong devicePtr)
{
chip::DeviceLayer::StackLock lock;
OperationalDeviceProxy * device = reinterpret_cast<OperationalDeviceProxy *>(devicePtr);
if (device != NULL)
{
delete device;
}
}
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(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 wrapper->Controller()->GetCompressedFabricId();
}
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)
{
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;");
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);
if (data == nullptr)
{
ChipLogError(Controller, "GetDiscoveredDevice - not found");
}
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),
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),
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->Controller()->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<uint32_t>(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 != NULL)
{
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(void, subscribe)
(JNIEnv * env, jobject self, jlong handle, jlong callbackHandle, jlong devicePtr, jobject attributePathList, jobject eventPathList,
jint minInterval, jint maxInterval, jboolean keepSubscriptions, jboolean isFabricFiltered, jint imTimeoutMs, jobject eventMin)
{
chip::DeviceLayer::StackLock lock;
CHIP_ERROR err = CHIP_NO_ERROR;
app::ReadClient * readClient = nullptr;
jint numAttributePaths = 0;
jint numEventPaths = 0;
auto callback = reinterpret_cast<ReportCallback *>(callbackHandle);
DeviceProxy * device = reinterpret_cast<DeviceProxy *>(devicePtr);
if (device == nullptr)
{
ChipLogProgress(Controller, "Could not cast device pointer to Device object");
JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, CHIP_ERROR_INCORRECT_STATE);
return;
}
app::ReadPrepareParams params(device->GetSecureSession().Value());
params.mMinIntervalFloorSeconds = minInterval;
params.mMaxIntervalCeilingSeconds = maxInterval;
params.mKeepSubscriptions = (keepSubscriptions != JNI_FALSE);
params.mIsFabricFiltered = (isFabricFiltered != JNI_FALSE);
params.mTimeout = imTimeoutMs != 0 ? System::Clock::Milliseconds32(imTimeoutMs) : System::Clock::kZero;
if (attributePathList != nullptr)
{
SuccessOrExit(err = JniReferences::GetInstance().GetListSize(attributePathList, numAttributePaths));
}
if (numAttributePaths > 0)
{
std::unique_ptr<chip::app::AttributePathParams[]> attributePaths(new chip::app::AttributePathParams[numAttributePaths]);
for (uint8_t i = 0; i < numAttributePaths; i++)
{
jobject attributePathItem = nullptr;
SuccessOrExit(err = JniReferences::GetInstance().GetListItem(attributePathList, i, attributePathItem));
EndpointId endpointId;
ClusterId clusterId;
AttributeId attributeId;
SuccessOrExit(err = ParseAttributePath(attributePathItem, endpointId, clusterId, attributeId));
attributePaths[i] = chip::app::AttributePathParams(endpointId, clusterId, attributeId);
}
params.mpAttributePathParamsList = attributePaths.get();
params.mAttributePathParamsListSize = numAttributePaths;
attributePaths.release();
}
if (eventMin != nullptr)
{
params.mEventNumber.SetValue(static_cast<chip::EventNumber>(JniReferences::GetInstance().LongToPrimitive(eventMin)));
}
if (eventPathList != nullptr)
{
SuccessOrExit(err = JniReferences::GetInstance().GetListSize(eventPathList, numEventPaths));
}
if (numEventPaths > 0)
{
std::unique_ptr<chip::app::EventPathParams[]> eventPaths(new chip::app::EventPathParams[numEventPaths]);
for (uint8_t i = 0; i < numEventPaths; i++)
{
jobject eventPathItem = nullptr;
SuccessOrExit(err = JniReferences::GetInstance().GetListItem(eventPathList, i, eventPathItem));
EndpointId endpointId;
ClusterId clusterId;
EventId eventId;
bool isUrgent;
SuccessOrExit(err = ParseEventPath(eventPathItem, endpointId, clusterId, eventId, isUrgent));
eventPaths[i] = chip::app::EventPathParams(endpointId, clusterId, eventId, isUrgent);
}
params.mpEventPathParamsList = eventPaths.get();
params.mEventPathParamsListSize = numEventPaths;
eventPaths.release();
}
readClient = Platform::New<app::ReadClient>(app::InteractionModelEngine::GetInstance(), device->GetExchangeManager(),
callback->mClusterCacheAdapter.GetBufferedCallback(),
app::ReadClient::InteractionType::Subscribe);
SuccessOrExit(err = readClient->SendAutoResubscribeRequest(std::move(params)));
callback->mReadClient = readClient;
exit:
if (err != CHIP_NO_ERROR)
{
ChipLogError(Controller, "JNI IM Subscribe Error: %s", err.AsString());
if (err == CHIP_JNI_ERROR_EXCEPTION_THROWN)
{
env->ExceptionDescribe();
env->ExceptionClear();
}
callback->OnError(err);
if (readClient != nullptr)
{
Platform::Delete(readClient);
}
if (callback != nullptr)
{
Platform::Delete(callback);
}
}
}
JNI_METHOD(void, read)
(JNIEnv * env, jobject self, jlong handle, jlong callbackHandle, jlong devicePtr, jobject attributePathList, jobject eventPathList,
jboolean isFabricFiltered, jint imTimeoutMs, jobject eventMin)
{
chip::DeviceLayer::StackLock lock;
CHIP_ERROR err = CHIP_NO_ERROR;
auto callback = reinterpret_cast<ReportCallback *>(callbackHandle);
std::vector<app::AttributePathParams> attributePathParamsList;
std::vector<app::EventPathParams> eventPathParamsList;
app::ReadClient * readClient = nullptr;
DeviceProxy * device = reinterpret_cast<DeviceProxy *>(devicePtr);
if (device == nullptr)
{
ChipLogProgress(Controller, "Could not cast device pointer to Device object");
JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, CHIP_ERROR_INCORRECT_STATE);
return;
}
app::ReadPrepareParams params(device->GetSecureSession().Value());
SuccessOrExit(err = ParseAttributePathList(attributePathList, attributePathParamsList));
SuccessOrExit(err = ParseEventPathList(eventPathList, eventPathParamsList));
VerifyOrExit(attributePathParamsList.size() != 0 || eventPathParamsList.size() != 0, err = CHIP_ERROR_INVALID_ARGUMENT);
params.mpAttributePathParamsList = attributePathParamsList.data();
params.mAttributePathParamsListSize = attributePathParamsList.size();
params.mpEventPathParamsList = eventPathParamsList.data();
params.mEventPathParamsListSize = eventPathParamsList.size();
params.mIsFabricFiltered = (isFabricFiltered != JNI_FALSE);
params.mTimeout = imTimeoutMs != 0 ? System::Clock::Milliseconds32(imTimeoutMs) : System::Clock::kZero;
if (eventMin != nullptr)
{
params.mEventNumber.SetValue(static_cast<chip::EventNumber>(JniReferences::GetInstance().LongToPrimitive(eventMin)));
}
readClient = Platform::New<app::ReadClient>(app::InteractionModelEngine::GetInstance(), device->GetExchangeManager(),
callback->mClusterCacheAdapter.GetBufferedCallback(),
app::ReadClient::InteractionType::Read);
SuccessOrExit(err = readClient->SendRequest(params));
callback->mReadClient = readClient;
exit:
if (err != CHIP_NO_ERROR)
{
ChipLogError(Controller, "JNI IM Read Error: %s", err.AsString());
if (err == CHIP_JNI_ERROR_EXCEPTION_THROWN)
{
env->ExceptionDescribe();
env->ExceptionClear();
}
callback->OnError(err);
if (readClient != nullptr)
{
Platform::Delete(readClient);
}
if (callback != nullptr)
{
Platform::Delete(callback);
}
}
}
JNI_METHOD(void, write)
(JNIEnv * env, jobject self, jlong handle, jlong callbackHandle, jlong devicePtr, jobject attributeList, jint timedRequestTimeoutMs,
jint imTimeoutMs)
{
chip::DeviceLayer::StackLock lock;
CHIP_ERROR err = CHIP_NO_ERROR;
jint listSize = 0;
auto callback = reinterpret_cast<WriteAttributesCallback *>(callbackHandle);
app::WriteClient * writeClient;
ChipLogDetail(Controller, "IM write() called");
DeviceProxy * device = reinterpret_cast<DeviceProxy *>(devicePtr);
VerifyOrExit(device != nullptr, err = CHIP_ERROR_INCORRECT_STATE);
VerifyOrExit(device->GetSecureSession().HasValue(), err = CHIP_ERROR_MISSING_SECURE_SESSION);
VerifyOrExit(attributeList != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
SuccessOrExit(err = JniReferences::GetInstance().GetListSize(attributeList, listSize));
writeClient = Platform::New<app::WriteClient>(device->GetExchangeManager(), callback->GetChunkedWriteCallback(),
timedRequestTimeoutMs != 0 ? Optional<uint16_t>(timedRequestTimeoutMs)
: Optional<uint16_t>::Missing());
for (uint8_t i = 0; i < listSize; i++)
{
jobject attributeItem = nullptr;
uint32_t endpointId = 0;
uint32_t clusterId = 0;
uint32_t attributeId = 0;
jmethodID getEndpointIdMethod = nullptr;
jmethodID getClusterIdMethod = nullptr;
jmethodID getAttributeIdMethod = nullptr;
jmethodID hasDataVersionMethod = nullptr;
jmethodID getDataVersionMethod = nullptr;
jmethodID getTlvByteArrayMethod = nullptr;
jobject endpointIdObj = nullptr;
jobject clusterIdObj = nullptr;
jobject attributeIdObj = nullptr;
jbyteArray tlvBytesObj = nullptr;
bool hasDataVersion = false;
Optional<DataVersion> dataVersion = Optional<DataVersion>();
;
jbyte * tlvBytesObjBytes = nullptr;
jsize length = 0;
TLV::TLVReader reader;
SuccessOrExit(err = JniReferences::GetInstance().GetListItem(attributeList, i, attributeItem));
SuccessOrExit(err = JniReferences::GetInstance().FindMethod(
env, attributeItem, "getEndpointId", "()Lchip/devicecontroller/model/ChipPathId;", &getEndpointIdMethod));
SuccessOrExit(err = JniReferences::GetInstance().FindMethod(
env, attributeItem, "getClusterId", "()Lchip/devicecontroller/model/ChipPathId;", &getClusterIdMethod));
SuccessOrExit(err = JniReferences::GetInstance().FindMethod(env, attributeItem, "getAttributeId",
"()Lchip/devicecontroller/model/ChipPathId;",
&getAttributeIdMethod));
SuccessOrExit(
err = JniReferences::GetInstance().FindMethod(env, attributeItem, "hasDataVersion", "()Z", &hasDataVersionMethod));
SuccessOrExit(
err = JniReferences::GetInstance().FindMethod(env, attributeItem, "getDataVersion", "()I", &getDataVersionMethod));
SuccessOrExit(
err = JniReferences::GetInstance().FindMethod(env, attributeItem, "getTlvByteArray", "()[B", &getTlvByteArrayMethod));
endpointIdObj = env->CallObjectMethod(attributeItem, getEndpointIdMethod);
VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
VerifyOrExit(endpointIdObj != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
clusterIdObj = env->CallObjectMethod(attributeItem, getClusterIdMethod);
VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
VerifyOrExit(clusterIdObj != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
attributeIdObj = env->CallObjectMethod(attributeItem, getAttributeIdMethod);
VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
VerifyOrExit(attributeIdObj != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
SuccessOrExit(err = GetChipPathIdValue(endpointIdObj, kInvalidEndpointId, endpointId));
SuccessOrExit(err = GetChipPathIdValue(clusterIdObj, kInvalidClusterId, clusterId));
SuccessOrExit(err = GetChipPathIdValue(attributeIdObj, kInvalidAttributeId, attributeId));
hasDataVersion = static_cast<bool>(env->CallBooleanMethod(attributeItem, hasDataVersionMethod));
VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
if (hasDataVersion)
{
DataVersion dataVersionVal = static_cast<DataVersion>(env->CallIntMethod(attributeItem, getDataVersionMethod));
VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
dataVersion.SetValue(dataVersionVal);
}
tlvBytesObj = static_cast<jbyteArray>(env->CallObjectMethod(attributeItem, getTlvByteArrayMethod));
VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
VerifyOrExit(tlvBytesObj != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
tlvBytesObjBytes = env->GetByteArrayElements(tlvBytesObj, nullptr);
VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
length = env->GetArrayLength(tlvBytesObj);
VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
reader.Init(reinterpret_cast<const uint8_t *>(tlvBytesObjBytes), static_cast<size_t>(length));
reader.Next();
SuccessOrExit(
err = writeClient->PutPreencodedAttribute(
chip::app::ConcreteDataAttributePath(static_cast<EndpointId>(endpointId), static_cast<ClusterId>(clusterId),
static_cast<AttributeId>(attributeId), dataVersion),
reader));
}
err = writeClient->SendWriteRequest(device->GetSecureSession().Value(),
imTimeoutMs != 0 ? System::Clock::Milliseconds32(imTimeoutMs) : System::Clock::kZero);
SuccessOrExit(err);
callback->mWriteClient = writeClient;
exit:
if (err != CHIP_NO_ERROR)
{
ChipLogError(Controller, "JNI IM Write Error: %s", err.AsString());
if (err == CHIP_JNI_ERROR_EXCEPTION_THROWN)
{
env->ExceptionDescribe();
env->ExceptionClear();
}
callback->OnError(writeClient, err);
if (writeClient != nullptr)
{
Platform::Delete(writeClient);
}
if (callback != nullptr)
{
Platform::Delete(callback);
}
}
}
JNI_METHOD(void, invoke)
(JNIEnv * env, jobject self, jlong handle, jlong callbackHandle, jlong devicePtr, jobject invokeElement, jint timedRequestTimeoutMs,
jint imTimeoutMs)
{
chip::DeviceLayer::StackLock lock;
CHIP_ERROR err = CHIP_NO_ERROR;
auto callback = reinterpret_cast<InvokeCallback *>(callbackHandle);
app::CommandSender * commandSender;
uint32_t endpointId = 0;
uint32_t clusterId = 0;
uint32_t commandId = 0;
jmethodID getEndpointIdMethod = nullptr;
jmethodID getClusterIdMethod = nullptr;
jmethodID getCommandIdMethod = nullptr;
jmethodID getTlvByteArrayMethod = nullptr;
jobject endpointIdObj = nullptr;
jobject clusterIdObj = nullptr;
jobject commandIdObj = nullptr;
jbyteArray tlvBytesObj = nullptr;
jbyte * tlvBytesObjBytes = nullptr;
jsize length = 0;
TLV::TLVReader reader;
TLV::TLVWriter * writer = nullptr;
ChipLogDetail(Controller, "IM invoke() called");
DeviceProxy * device = reinterpret_cast<DeviceProxy *>(devicePtr);
VerifyOrExit(device != nullptr, err = CHIP_ERROR_INCORRECT_STATE);
VerifyOrExit(device->GetSecureSession().HasValue(), err = CHIP_ERROR_MISSING_SECURE_SESSION);
commandSender = Platform::New<app::CommandSender>(callback, device->GetExchangeManager(), timedRequestTimeoutMs != 0);
SuccessOrExit(err = JniReferences::GetInstance().FindMethod(
env, invokeElement, "getEndpointId", "()Lchip/devicecontroller/model/ChipPathId;", &getEndpointIdMethod));
SuccessOrExit(err = JniReferences::GetInstance().FindMethod(env, invokeElement, "getClusterId",
"()Lchip/devicecontroller/model/ChipPathId;", &getClusterIdMethod));
SuccessOrExit(err = JniReferences::GetInstance().FindMethod(env, invokeElement, "getCommandId",
"()Lchip/devicecontroller/model/ChipPathId;", &getCommandIdMethod));
SuccessOrExit(
err = JniReferences::GetInstance().FindMethod(env, invokeElement, "getTlvByteArray", "()[B", &getTlvByteArrayMethod));
endpointIdObj = env->CallObjectMethod(invokeElement, getEndpointIdMethod);
VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
VerifyOrExit(endpointIdObj != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
clusterIdObj = env->CallObjectMethod(invokeElement, getClusterIdMethod);
VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
VerifyOrExit(clusterIdObj != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
commandIdObj = env->CallObjectMethod(invokeElement, getCommandIdMethod);
VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
VerifyOrExit(commandIdObj != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
SuccessOrExit(err = GetChipPathIdValue(endpointIdObj, kInvalidEndpointId, endpointId));
SuccessOrExit(err = GetChipPathIdValue(clusterIdObj, kInvalidClusterId, clusterId));
SuccessOrExit(err = GetChipPathIdValue(commandIdObj, kInvalidCommandId, commandId));
tlvBytesObj = static_cast<jbyteArray>(env->CallObjectMethod(invokeElement, getTlvByteArrayMethod));
VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
VerifyOrExit(tlvBytesObj != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT);
tlvBytesObjBytes = env->GetByteArrayElements(tlvBytesObj, nullptr);
VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
length = env->GetArrayLength(tlvBytesObj);
VerifyOrExit(!env->ExceptionCheck(), err = CHIP_JNI_ERROR_EXCEPTION_THROWN);
SuccessOrExit(err = commandSender->PrepareCommand(app::CommandPathParams(static_cast<EndpointId>(endpointId), /* group id */ 0,
static_cast<ClusterId>(clusterId),
static_cast<CommandId>(commandId),
app::CommandPathFlags::kEndpointIdValid),
false));
writer = commandSender->GetCommandDataIBTLVWriter();
VerifyOrExit(writer != nullptr, err = CHIP_ERROR_INCORRECT_STATE);
reader.Init(reinterpret_cast<const uint8_t *>(tlvBytesObjBytes), static_cast<size_t>(length));
reader.Next();
SuccessOrExit(err = writer->CopyContainer(TLV::ContextTag(app::CommandDataIB::Tag::kFields), reader));
SuccessOrExit(err = commandSender->FinishCommand(timedRequestTimeoutMs != 0 ? Optional<uint16_t>(timedRequestTimeoutMs)
: Optional<uint16_t>::Missing()));
SuccessOrExit(err =
commandSender->SendCommandRequest(device->GetSecureSession().Value(),
imTimeoutMs != 0 ? MakeOptional(System::Clock::Milliseconds32(imTimeoutMs))
: Optional<System::Clock::Timeout>::Missing()));
callback->mCommandSender = commandSender;
exit:
if (err != CHIP_NO_ERROR)
{
ChipLogError(Controller, "JNI IM Invoke Error: %s", err.AsString());
if (err == CHIP_JNI_ERROR_EXCEPTION_THROWN)
{
env->ExceptionDescribe();
env->ExceptionClear();
}
callback->OnError(nullptr, err);
if (commandSender != nullptr)
{
Platform::Delete(commandSender);
}
if (callback != nullptr)
{
Platform::Delete(callback);
}
}
}
/**
* Takes objects in attributePathList, converts them to app:AttributePathParams, and appends them to outAttributePathParamsList.
*/
CHIP_ERROR ParseAttributePathList(jobject attributePathList, std::vector<app::AttributePathParams> & outAttributePathParamsList)
{
jint listSize;
if (attributePathList == nullptr)
{
return CHIP_NO_ERROR;
}
ReturnErrorOnFailure(JniReferences::GetInstance().GetListSize(attributePathList, listSize));
for (uint8_t i = 0; i < listSize; i++)
{
jobject attributePathItem = nullptr;
ReturnErrorOnFailure(JniReferences::GetInstance().GetListItem(attributePathList, i, attributePathItem));
EndpointId endpointId;
ClusterId clusterId;
AttributeId attributeId;
ReturnErrorOnFailure(ParseAttributePath(attributePathItem, endpointId, clusterId, attributeId));
outAttributePathParamsList.push_back(app::AttributePathParams(endpointId, clusterId, attributeId));
}
return CHIP_NO_ERROR;
}
CHIP_ERROR ParseAttributePath(jobject attributePath, EndpointId & outEndpointId, ClusterId & outClusterId,
AttributeId & outAttributeId)
{
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
jmethodID getEndpointIdMethod = nullptr;
jmethodID getClusterIdMethod = nullptr;
jmethodID getAttributeIdMethod = nullptr;
ReturnErrorOnFailure(JniReferences::GetInstance().FindMethod(
env, attributePath, "getEndpointId", "()Lchip/devicecontroller/model/ChipPathId;", &getEndpointIdMethod));
ReturnErrorOnFailure(JniReferences::GetInstance().FindMethod(
env, attributePath, "getClusterId", "()Lchip/devicecontroller/model/ChipPathId;", &getClusterIdMethod));
ReturnErrorOnFailure(JniReferences::GetInstance().FindMethod(
env, attributePath, "getAttributeId", "()Lchip/devicecontroller/model/ChipPathId;", &getAttributeIdMethod));
jobject endpointIdObj = env->CallObjectMethod(attributePath, getEndpointIdMethod);
VerifyOrReturnError(endpointIdObj != nullptr, CHIP_ERROR_INCORRECT_STATE);
jobject clusterIdObj = env->CallObjectMethod(attributePath, getClusterIdMethod);
VerifyOrReturnError(clusterIdObj != nullptr, CHIP_ERROR_INCORRECT_STATE);
jobject attributeIdObj = env->CallObjectMethod(attributePath, getAttributeIdMethod);
VerifyOrReturnError(attributeIdObj != nullptr, CHIP_ERROR_INCORRECT_STATE);
uint32_t endpointId = 0;
ReturnErrorOnFailure(GetChipPathIdValue(endpointIdObj, kInvalidEndpointId, endpointId));
uint32_t clusterId = 0;
ReturnErrorOnFailure(GetChipPathIdValue(clusterIdObj, kInvalidClusterId, clusterId));
uint32_t attributeId = 0;
ReturnErrorOnFailure(GetChipPathIdValue(attributeIdObj, kInvalidAttributeId, attributeId));
outEndpointId = static_cast<EndpointId>(endpointId);
outClusterId = static_cast<ClusterId>(clusterId);
outAttributeId = static_cast<AttributeId>(attributeId);
return CHIP_NO_ERROR;
}
/**
* Takes objects in eventPathList, converts them to app:EventPathParams, and appends them to outEventPathParamsList.
*/
CHIP_ERROR ParseEventPathList(jobject eventPathList, std::vector<app::EventPathParams> & outEventPathParamsList)
{
jint listSize;
if (eventPathList == nullptr)
{
return CHIP_NO_ERROR;
}
ReturnErrorOnFailure(JniReferences::GetInstance().GetListSize(eventPathList, listSize));
for (uint8_t i = 0; i < listSize; i++)
{
jobject eventPathItem = nullptr;
ReturnErrorOnFailure(JniReferences::GetInstance().GetListItem(eventPathList, i, eventPathItem));
EndpointId endpointId;
ClusterId clusterId;
EventId eventId;
bool isUrgent;
ReturnErrorOnFailure(ParseEventPath(eventPathItem, endpointId, clusterId, eventId, isUrgent));
outEventPathParamsList.push_back(app::EventPathParams(endpointId, clusterId, eventId, isUrgent));
}
return CHIP_NO_ERROR;
}
CHIP_ERROR ParseEventPath(jobject eventPath, EndpointId & outEndpointId, ClusterId & outClusterId, EventId & outEventId,
bool & outIsUrgent)
{
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
jmethodID getEndpointIdMethod = nullptr;
jmethodID getClusterIdMethod = nullptr;
jmethodID getEventIdMethod = nullptr;
jmethodID isUrgentMethod = nullptr;
ReturnErrorOnFailure(JniReferences::GetInstance().FindMethod(
env, eventPath, "getEndpointId", "()Lchip/devicecontroller/model/ChipPathId;", &getEndpointIdMethod));
ReturnErrorOnFailure(JniReferences::GetInstance().FindMethod(
env, eventPath, "getClusterId", "()Lchip/devicecontroller/model/ChipPathId;", &getClusterIdMethod));
ReturnErrorOnFailure(JniReferences::GetInstance().FindMethod(env, eventPath, "getEventId",
"()Lchip/devicecontroller/model/ChipPathId;", &getEventIdMethod));
ReturnErrorOnFailure(JniReferences::GetInstance().FindMethod(env, eventPath, "isUrgent", "()Z", &isUrgentMethod));
jobject endpointIdObj = env->CallObjectMethod(eventPath, getEndpointIdMethod);
VerifyOrReturnError(endpointIdObj != nullptr, CHIP_ERROR_INCORRECT_STATE);
jobject clusterIdObj = env->CallObjectMethod(eventPath, getClusterIdMethod);
VerifyOrReturnError(clusterIdObj != nullptr, CHIP_ERROR_INCORRECT_STATE);
jobject eventIdObj = env->CallObjectMethod(eventPath, getEventIdMethod);
VerifyOrReturnError(eventIdObj != nullptr, CHIP_ERROR_INCORRECT_STATE);
jboolean isUrgent = env->CallBooleanMethod(eventPath, isUrgentMethod);
uint32_t endpointId = 0;
ReturnErrorOnFailure(GetChipPathIdValue(endpointIdObj, kInvalidEndpointId, endpointId));
uint32_t clusterId = 0;
ReturnErrorOnFailure(GetChipPathIdValue(clusterIdObj, kInvalidClusterId, clusterId));
uint32_t eventId = 0;
ReturnErrorOnFailure(GetChipPathIdValue(eventIdObj, kInvalidEventId, eventId));
outEndpointId = static_cast<EndpointId>(endpointId);
outClusterId = static_cast<ClusterId>(clusterId);
outEventId = static_cast<EventId>(eventId);
outIsUrgent = (isUrgent == JNI_TRUE);
return CHIP_NO_ERROR;
}
CHIP_ERROR GetChipPathIdValue(jobject chipPathId, uint32_t wildcardValue, uint32_t & outValue)
{
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
bool idIsWildcard = false;
ReturnErrorOnFailure(IsWildcardChipPathId(chipPathId, idIsWildcard));
if (idIsWildcard)
{
outValue = wildcardValue;
return CHIP_NO_ERROR;
}
jmethodID getIdMethod = nullptr;
ReturnErrorOnFailure(JniReferences::GetInstance().FindMethod(env, chipPathId, "getId", "()J", &getIdMethod));
outValue = static_cast<uint32_t>(env->CallLongMethod(chipPathId, getIdMethod));
VerifyOrReturnError(!env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN);
return CHIP_NO_ERROR;
}
CHIP_ERROR IsWildcardChipPathId(jobject chipPathId, bool & isWildcard)
{
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
jmethodID getTypeMethod = nullptr;
ReturnErrorOnFailure(JniReferences::GetInstance().FindMethod(
env, chipPathId, "getType", "()Lchip/devicecontroller/model/ChipPathId$IdType;", &getTypeMethod));
jobject idType = env->CallObjectMethod(chipPathId, getTypeMethod);
VerifyOrReturnError(!env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN);
VerifyOrReturnError(idType != nullptr, CHIP_JNI_ERROR_NULL_OBJECT);
jmethodID nameMethod = nullptr;
ReturnErrorOnFailure(JniReferences::GetInstance().FindMethod(env, idType, "name", "()Ljava/lang/String;", &nameMethod));
jstring typeNameString = static_cast<jstring>(env->CallObjectMethod(idType, nameMethod));
VerifyOrReturnError(!env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN);
VerifyOrReturnError(typeNameString != nullptr, CHIP_JNI_ERROR_NULL_OBJECT);
JniUtfString typeNameJniString(env, typeNameString);
isWildcard = strncmp(typeNameJniString.c_str(), "WILDCARD", 8) == 0;
return CHIP_NO_ERROR;
}
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 = NULL;
#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 NULL;
}
// 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, NULL);
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().GetClassRef(env, "chip/devicecontroller/PaseVerifierParams", paramsClass);
JniClass paseVerifierParamsClass(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().GetClassRef(env, "chip/devicecontroller/NetworkLocation", locationClass);
JniClass networkLocationClass(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;
}