Skip to content

Commit

Permalink
[Android] Implement check in delegate (project-chip#32557)
Browse files Browse the repository at this point in the history
* Implement Android check in delegate

* Restyled by whitespace

* Restyled by google-java-format

* Restyled by clang-format

* Update documentation

* Restyled by whitespace

* Restyled by google-java-format

* Fix jni object reference issue, add chiptool callback

* Restyled by google-java-format

* Restyled by clang-format

* Update kotlin codestyle

* remove public

* fix typo

* Restyled by clang-format

* remove chip

* Modify from comment

---------

Co-authored-by: Restyled.io <[email protected]>
  • Loading branch information
2 people authored and huangxuyong committed Mar 19, 2024
1 parent 28a4912 commit b82bba5
Show file tree
Hide file tree
Showing 10 changed files with 387 additions and 2 deletions.
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();
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();
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 @@ -1434,6 +1439,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

0 comments on commit b82bba5

Please sign in to comment.