Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Android] Implement check in delegate #32557

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import android.util.Log
import chip.devicecontroller.ChipDeviceController
import chip.devicecontroller.ControllerParams
import chip.devicecontroller.GetConnectedDeviceCallbackJni.GetConnectedDeviceCallback
import chip.devicecontroller.ICDCheckInDelegate
import chip.devicecontroller.ICDClientInfo
import chip.platform.AndroidBleManager
import chip.platform.AndroidChipPlatform
import chip.platform.ChipMdnsCallbackImpl
Expand Down Expand Up @@ -60,6 +62,23 @@ object ChipClient {
chipDeviceController.setAttestationTrustStoreDelegate(
ExampleAttestationTrustStoreDelegate(chipDeviceController)
)

chipDeviceController.setICDCheckInDelegate(
object : ICDCheckInDelegate {
override fun onCheckInComplete(info: ICDClientInfo) {
Log.d(TAG, "onCheckInComplete : $info")
}

override fun onKeyRefreshNeeded(info: ICDClientInfo): ByteArray? {
Log.d(TAG, "onKeyRefreshNeeded : $info")
return null
}

override fun onKeyRefreshDone(errorCode: Long) {
Log.d(TAG, "onKeyRefreshDone : $errorCode")
}
}
)
}

return chipDeviceController
Expand Down
162 changes: 162 additions & 0 deletions src/controller/java/AndroidCheckInDelegate.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/*
* Copyright (c) 2024 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.
*/

#include "AndroidCheckInDelegate.h"

#include <app/icd/client/RefreshKeySender.h>
#include <crypto/CHIPCryptoPAL.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/JniReferences.h>
#include <lib/support/logging/CHIPLogging.h>

#define PARSE_CLIENT_INFO(_clientInfo, _peerNodeId, _startCounter, _offset, _monitoredSubject, _jniICDAesKey, _jniICDHmacKey) \
jlong _peerNodeId = static_cast<jlong>(_clientInfo.peer_node.GetNodeId()); \
jlong _startCounter = static_cast<jlong>(_clientInfo.start_icd_counter); \
jlong _offset = static_cast<jlong>(_clientInfo.offset); \
jlong _monitoredSubject = static_cast<jlong>(_clientInfo.monitored_subject); \
chip::ByteSpan aes_buf(_clientInfo.aes_key_handle.As<Crypto::Symmetric128BitsKeyByteArray>()); \
chip::ByteSpan hmac_buf(_clientInfo.hmac_key_handle.As<Crypto::Symmetric128BitsKeyByteArray>()); \
chip::ByteArray _jniICDAesKey(env, aes_buf); \
chip::ByteArray _jniICDHmacKey(env, hmac_buf);

namespace chip {
namespace app {

CHIP_ERROR AndroidCheckInDelegate::Init(ICDClientStorage * storage, InteractionModelEngine * engine)
{
VerifyOrReturnError(storage != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(mpStorage == nullptr, CHIP_ERROR_INCORRECT_STATE);
mpStorage = storage;
mpImEngine = engine;
return CHIP_NO_ERROR;
}

CHIP_ERROR AndroidCheckInDelegate::SetDelegate(jobject checkInDelegateObj)
{
ReturnLogErrorOnFailure(mCheckInDelegate.Init(checkInDelegateObj));
return CHIP_NO_ERROR;
}

void AndroidCheckInDelegate::OnCheckInComplete(const ICDClientInfo & clientInfo)
{
ChipLogProgress(
ICD, "Check In Message processing complete: start_counter=%" PRIu32 " offset=%" PRIu32 " nodeid=" ChipLogFormatScopedNodeId,
clientInfo.start_icd_counter, clientInfo.offset, ChipLogValueScopedNodeId(clientInfo.peer_node));

VerifyOrReturn(mCheckInDelegate.HasValidObjectRef(), ChipLogProgress(ICD, "check-in delegate is not implemented!"));

JNIEnv * env = chip::JniReferences::GetInstance().GetEnvForCurrentThread();
joonhaengHeo marked this conversation as resolved.
Show resolved Hide resolved
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "JNIEnv is null!"));
PARSE_CLIENT_INFO(clientInfo, peerNodeId, startCounter, offset, monitoredSubject, jniICDAesKey, jniICDHmacKey)

jmethodID onCheckInCompleteMethodID = nullptr;
CHIP_ERROR err = chip::JniReferences::GetInstance().FindMethod(env, mCheckInDelegate.ObjectRef(), "onCheckInComplete",
"(JJJJ[B[B)V", &onCheckInCompleteMethodID);
VerifyOrReturn(err == CHIP_NO_ERROR,
ChipLogProgress(ICD, "onCheckInComplete - FindMethod is failed! : %" CHIP_ERROR_FORMAT, err.Format()));

env->CallVoidMethod(mCheckInDelegate.ObjectRef(), onCheckInCompleteMethodID, peerNodeId, startCounter, offset, monitoredSubject,
jniICDAesKey.jniValue(), jniICDHmacKey.jniValue());
}

RefreshKeySender * AndroidCheckInDelegate::OnKeyRefreshNeeded(ICDClientInfo & clientInfo, ICDClientStorage * clientStorage)
{
CHIP_ERROR err = CHIP_NO_ERROR;
RefreshKeySender::RefreshKeyBuffer newKey;

bool hasSetKey = false;
if (mCheckInDelegate.HasValidObjectRef())
{
JNIEnv * env = chip::JniReferences::GetInstance().GetEnvForCurrentThread();
joonhaengHeo marked this conversation as resolved.
Show resolved Hide resolved
VerifyOrReturnValue(env != nullptr, nullptr, ChipLogError(Controller, "JNIEnv is null!"));

PARSE_CLIENT_INFO(clientInfo, peerNodeId, startCounter, offset, monitoredSubject, jniICDAesKey, jniICDHmacKey)

jmethodID onKeyRefreshNeededMethodID = nullptr;
err = chip::JniReferences::GetInstance().FindMethod(env, mCheckInDelegate.ObjectRef(), "onKeyRefreshNeeded", "(JJJJ[B[B)V",
&onKeyRefreshNeededMethodID);
VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr,
ChipLogProgress(ICD, "onKeyRefreshNeeded - FindMethod is failed! : %" CHIP_ERROR_FORMAT, err.Format()));

jbyteArray key = static_cast<jbyteArray>(env->CallObjectMethod(mCheckInDelegate.ObjectRef(), onKeyRefreshNeededMethodID,
peerNodeId, startCounter, offset, monitoredSubject,
jniICDAesKey.jniValue(), jniICDHmacKey.jniValue()));

if (key != nullptr)
{
JniByteArray jniKey(env, key);
VerifyOrReturnValue(static_cast<size_t>(jniKey.size()) == newKey.Capacity(), nullptr,
ChipLogProgress(ICD, "Invalid key length : %d", jniKey.size()));
memcpy(newKey.Bytes(), jniKey.data(), newKey.Capacity());
hasSetKey = true;
}
}
else
{
ChipLogProgress(ICD, "check-in delegate is not implemented!");
}
if (!hasSetKey)
{
err = Crypto::DRBG_get_bytes(newKey.Bytes(), newKey.Capacity());
if (err != CHIP_NO_ERROR)
{
ChipLogError(ICD, "Generation of new key failed: %" CHIP_ERROR_FORMAT, err.Format());
return nullptr;
}
}

auto refreshKeySender = Platform::New<RefreshKeySender>(this, clientInfo, clientStorage, mpImEngine, newKey);
if (refreshKeySender == nullptr)
{
return nullptr;
}
return refreshKeySender;
}

void AndroidCheckInDelegate::OnKeyRefreshDone(RefreshKeySender * refreshKeySender, CHIP_ERROR error)
{
if (error == CHIP_NO_ERROR)
{
ChipLogProgress(ICD, "Re-registration with new key completed successfully");
}
else
{
ChipLogError(ICD, "Re-registration with new key failed with error : %" CHIP_ERROR_FORMAT, error.Format());
// The callee can take corrective action based on the error received.
}

VerifyOrReturn(mCheckInDelegate.HasValidObjectRef(), ChipLogProgress(ICD, "check-in delegate is not implemented!"));

JNIEnv * env = chip::JniReferences::GetInstance().GetEnvForCurrentThread();
VerifyOrReturn(env != nullptr, ChipLogError(Controller, "JNIEnv is null!"));

jmethodID onKeyRefreshDoneMethodID = nullptr;
CHIP_ERROR err = chip::JniReferences::GetInstance().FindMethod(env, mCheckInDelegate.ObjectRef(), "onKeyRefreshDone", "(J)V",
&onKeyRefreshDoneMethodID);
VerifyOrReturn(err == CHIP_NO_ERROR,
ChipLogProgress(ICD, "onKeyRefreshDone - FindMethod is failed! : %" CHIP_ERROR_FORMAT, err.Format()));

env->CallVoidMethod(mCheckInDelegate.ObjectRef(), onKeyRefreshDoneMethodID, static_cast<jlong>(error.AsInteger()));

if (refreshKeySender != nullptr)
{
Platform::Delete(refreshKeySender);
refreshKeySender = nullptr;
}
}
} // namespace app
} // namespace chip
52 changes: 52 additions & 0 deletions src/controller/java/AndroidCheckInDelegate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
*
* Copyright (c) 2024 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.
*/

#pragma once

#include <app/icd/client/CheckInDelegate.h>
#include <app/icd/client/ICDClientStorage.h>
#include <lib/support/JniTypeWrappers.h>

namespace chip {
namespace app {

using namespace std;

class InteractionModelEngine;

/// Callbacks for check in protocol
class AndroidCheckInDelegate : public CheckInDelegate
{
public:
virtual ~AndroidCheckInDelegate() {}
CHIP_ERROR Init(ICDClientStorage * storage, InteractionModelEngine * engine);
void OnCheckInComplete(const ICDClientInfo & clientInfo) override;
RefreshKeySender * OnKeyRefreshNeeded(ICDClientInfo & clientInfo, ICDClientStorage * clientStorage) override;
void OnKeyRefreshDone(RefreshKeySender * refreshKeySender, CHIP_ERROR error) override;

CHIP_ERROR SetDelegate(jobject checkInDelegateObj);

private:
ICDClientStorage * mpStorage = nullptr;
InteractionModelEngine * mpImEngine = nullptr;

JniGlobalReference mCheckInDelegate;
};

} // namespace app
} // namespace chip
5 changes: 5 additions & 0 deletions src/controller/java/AndroidDeviceControllerWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,11 @@ CHIP_ERROR AndroidDeviceControllerWrapper::FinishOTAProvider()
#endif
}

CHIP_ERROR AndroidDeviceControllerWrapper::SetICDCheckInDelegate(jobject checkInDelegate)
{
return mCheckInDelegate.SetDelegate(checkInDelegate);
}

void AndroidDeviceControllerWrapper::OnStatusUpdate(chip::Controller::DevicePairingDelegate::Status status)
{
chip::DeviceLayer::StackUnlock unlock;
Expand Down
6 changes: 4 additions & 2 deletions src/controller/java/AndroidDeviceControllerWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
#include <jni.h>

#include <app/icd/client/CheckInHandler.h>
#include <app/icd/client/DefaultCheckInDelegate.h>
#include <app/icd/client/DefaultICDClientStorage.h>
#include <controller/CHIPDeviceController.h>
#include <credentials/GroupDataProviderImpl.h>
Expand All @@ -43,6 +42,7 @@
#include <platform/android/CHIPP256KeypairBridge.h>
#endif // JAVA_MATTER_CONTROLLER_TEST

#include "AndroidCheckInDelegate.h"
#include "AndroidOperationalCredentialsIssuer.h"
#include "AttestationTrustStoreBridge.h"
#include "DeviceAttestationDelegateBridge.h"
Expand Down Expand Up @@ -212,6 +212,8 @@ class AndroidDeviceControllerWrapper : public chip::Controller::DevicePairingDel

chip::app::DefaultICDClientStorage * getICDClientStorage() { return &mICDClientStorage; }

CHIP_ERROR SetICDCheckInDelegate(jobject checkInDelegate);

private:
using ChipDeviceControllerPtr = std::unique_ptr<chip::Controller::DeviceCommissioner>;

Expand All @@ -225,7 +227,7 @@ class AndroidDeviceControllerWrapper : public chip::Controller::DevicePairingDel
chip::Crypto::RawKeySessionKeystore mSessionKeystore;

chip::app::DefaultICDClientStorage mICDClientStorage;
chip::app::DefaultCheckInDelegate mCheckInDelegate;
chip::app::AndroidCheckInDelegate mCheckInDelegate;
chip::app::CheckInHandler mCheckInHandler;

JavaVM * mJavaVM = nullptr;
Expand Down
4 changes: 4 additions & 0 deletions src/controller/java/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ shared_library("jni") {
"AndroidCallbacks-JNI.cpp",
"AndroidCallbacks.cpp",
"AndroidCallbacks.h",
"AndroidCheckInDelegate.cpp",
"AndroidCheckInDelegate.h",
"AndroidClusterExceptions.cpp",
"AndroidClusterExceptions.h",
"AndroidCommissioningWindowOpener.cpp",
Expand Down Expand Up @@ -459,6 +461,8 @@ android_library("java") {
"src/chip/devicecontroller/ExtendableInvokeCallbackJni.java",
"src/chip/devicecontroller/GetConnectedDeviceCallbackJni.java",
"src/chip/devicecontroller/GroupKeySecurityPolicy.java",
"src/chip/devicecontroller/ICDCheckInDelegate.java",
"src/chip/devicecontroller/ICDCheckInDelegateWrapper.java",
"src/chip/devicecontroller/ICDClientInfo.java",
"src/chip/devicecontroller/ICDDeviceInfo.java",
"src/chip/devicecontroller/ICDRegistrationInfo.java",
Expand Down
19 changes: 19 additions & 0 deletions src/controller/java/CHIPDeviceController-JNI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,25 @@ JNI_METHOD(void, finishOTAProvider)(JNIEnv * env, jobject self, jlong handle)
#endif
}

JNI_METHOD(void, setICDCheckInDelegate)(JNIEnv * env, jobject self, jlong handle, jobject checkInDelegate)
{
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, "setICDCheckInDelegate() called");

err = wrapper->SetICDCheckInDelegate(checkInDelegate);
exit:
if (err != CHIP_NO_ERROR)
{
ChipLogError(Controller, "Failed to set ICD Check-In Deleagate. : %" CHIP_ERROR_FORMAT, err.Format());
JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err);
}
}

JNI_METHOD(void, commissionDevice)
(JNIEnv * env, jobject self, jlong handle, jlong deviceId, jbyteArray csrNonce, jobject networkCredentials)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,11 @@ public void finishOTAProvider() {
finishOTAProvider(deviceControllerPtr);
}

/** Set the delegate of ICD check in */
public void setICDCheckInDelegate(ICDCheckInDelegate delegate) {
setICDCheckInDelegate(deviceControllerPtr, new ICDCheckInDelegateWrapper(delegate));
}

public void pairDevice(
BluetoothGatt bleServer,
int connId,
Expand Down Expand Up @@ -1425,6 +1430,9 @@ private native void setAttestationTrustStoreDelegate(

private native void finishOTAProvider(long deviceControllerPtr);

private native void setICDCheckInDelegate(
long deviceControllerPtr, ICDCheckInDelegateWrapper delegate);

private native void pairDevice(
long deviceControllerPtr,
long deviceId,
Expand Down
Loading
Loading