Skip to content

Commit

Permalink
Implement Android Group message
Browse files Browse the repository at this point in the history
  • Loading branch information
joonhaengHeo committed Sep 13, 2023
1 parent d453502 commit bbb65fa
Show file tree
Hide file tree
Showing 7 changed files with 379 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import chip.devicecontroller.ChipDeviceController
import chip.devicecontroller.ChipIdLookup
import chip.devicecontroller.ClusterIDMapping.GroupKeyManagement
import chip.devicecontroller.ClusterIDMapping.Groups
import chip.devicecontroller.InvokeCallback
import chip.devicecontroller.ReportCallback
import chip.devicecontroller.ResubscriptionAttemptCallback
Expand All @@ -29,6 +31,7 @@ import chip.devicecontroller.model.InvokeElement
import chip.devicecontroller.model.NodeState
import chip.jsontlv.putJsonString
import chip.tlv.AnonymousTag
import chip.tlv.ContextSpecificTag
import chip.tlv.TlvReader
import chip.tlv.TlvWriter
import com.google.chip.chiptool.ChipClient
Expand All @@ -40,7 +43,9 @@ import java.util.Optional
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlin.concurrent.thread

class WildcardFragment : Fragment() {
private var _binding: WildcardFragmentBinding? = null
Expand Down Expand Up @@ -138,6 +143,85 @@ class WildcardFragment : Fragment() {
binding.isUrgentLabel.visibility = getVisibility(subscribeBtnOn)
binding.isUrgentSp.visibility = getVisibility(subscribeBtnOn)
binding.shutdownSubscriptionBtn.visibility = getVisibility(subscribeBtnOn)

val groupIds = deviceController.availableGroupIds
Log.d(TAG, "groupId : $groupIds")
if (groupIds.isEmpty()) {
deviceController.addGroup(0x4141, "TestName")
deviceController.addKeySet(0xAAAA, 0, 0x000000000021dfe0, hexStringToByteArray("d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"))
deviceController.bindKeySet(0x4141, 0xAAAA)

val tlvWriter = TlvWriter()
tlvWriter.startStructure(AnonymousTag)
tlvWriter.startStructure(ContextSpecificTag(GroupKeyManagement.KeySetWriteCommandField.GroupKeySet.id))
tlvWriter.put(ContextSpecificTag(0), 42U)
tlvWriter.put(ContextSpecificTag(1), 0U)
tlvWriter.put(ContextSpecificTag(2), hexStringToByteArray("d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"))
tlvWriter.put(ContextSpecificTag(3), 2220000U)
tlvWriter.put(ContextSpecificTag(4), hexStringToByteArray("d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"))
tlvWriter.put(ContextSpecificTag(5), 2220001U)
tlvWriter.put(ContextSpecificTag(6), hexStringToByteArray("d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"))
tlvWriter.put(ContextSpecificTag(7), 2220002U)
tlvWriter.endStructure()
tlvWriter.endStructure()

val tlvWriter2 = TlvWriter()
tlvWriter2.startArray(AnonymousTag)
tlvWriter2.startStructure(AnonymousTag)
tlvWriter2.put(ContextSpecificTag(1), 0x4141U)
tlvWriter2.put(ContextSpecificTag(2), 42U)
tlvWriter2.endStructure()
tlvWriter2.endArray()

val tlvWriter3 = TlvWriter()
tlvWriter3.startStructure(AnonymousTag)
tlvWriter3.put(ContextSpecificTag(0), 0x4141U)
tlvWriter3.put(ContextSpecificTag(1), "Light")
tlvWriter3.endStructure()
scope.launch {
deviceController.invoke(
object : InvokeCallback {
override fun onError(e: java.lang.Exception?) {
Log.d(TAG, "onError : ", e)
}

override fun onResponse(invokeElement: InvokeElement?, successCode: Long) {
Log.d(TAG, "onResponse")
}
},
ChipClient.getConnectedDevicePointer(requireContext(), addressUpdateFragment.deviceId),
InvokeElement.newInstance(0, GroupKeyManagement.ID, GroupKeyManagement.Command.KeySetWrite.id, tlvWriter.getEncoded(), null),
0, 0)

delay(1000)
deviceController.write(
writeAttributeCallback,
ChipClient.getConnectedDevicePointer(requireContext(), addressUpdateFragment.deviceId),
listOf(AttributeWriteRequest.newInstance(0, GroupKeyManagement.ID, GroupKeyManagement.Attribute.GroupKeyMap.id, tlvWriter2.getEncoded())),
0,
0
)
delay(1000)
deviceController.invoke(
object : InvokeCallback {
override fun onError(e: java.lang.Exception?) {
Log.d(TAG, "onError : ", e)
}

override fun onResponse(invokeElement: InvokeElement?, successCode: Long) {
Log.d(TAG, "onResponse")
}
},
ChipClient.getConnectedDevicePointer(requireContext(), addressUpdateFragment.deviceId),
InvokeElement.newInstance(0, Groups.ID, Groups.Command.AddGroup.id, tlvWriter3.getEncoded(), null),
0, 0)
}
}
for (groupId in groupIds) {
Log.d(TAG, "groupId : $groupId : ${deviceController.getGroupName(groupId)}, ${deviceController.findKeySetId(groupId).get()}")
}
val keySetIds = deviceController.keysetIds
Log.d(TAG, "keySetId : $keySetIds")
}

binding.sendBtn.setOnClickListener {
Expand Down Expand Up @@ -348,7 +432,7 @@ class WildcardFragment : Fragment() {

deviceController.write(
writeAttributeCallback,
ChipClient.getConnectedDevicePointer(requireContext(), addressUpdateFragment.deviceId),
deviceController.getGroupDevicePointer(0x4141),
listOf(writeRequest),
timedRequestTimeoutMs,
imTimeoutMs
Expand All @@ -365,7 +449,7 @@ class WildcardFragment : Fragment() {
InvokeElement.newInstance(endpointId, clusterId, commandId, null, jsonString)
deviceController.invoke(
invokeCallback,
ChipClient.getConnectedDevicePointer(requireContext(), addressUpdateFragment.deviceId),
deviceController.getGroupDevicePointer(0x4141)/*ChipClient.getConnectedDevicePointer(requireContext(), addressUpdateFragment.deviceId)*/,
invokeElement,
timedRequestTimeoutMs,
imTimeoutMs
Expand Down Expand Up @@ -641,6 +725,17 @@ class WildcardFragment : Fragment() {

fun newInstance(): WildcardFragment = WildcardFragment()

fun hexStringToByteArray(hexString: String): ByteArray {
val len = hexString.length
val data = ByteArray(len / 2)
var i = 0
while (i < len) {
data[i / 2]= ((Character.digit(hexString[i], 16) shl 4) + Character.digit(hexString[i + 1], 16)).toByte()
i += 2
}
return data
}

private val TLV_MAP =
mapOf(
"json" to
Expand Down
10 changes: 10 additions & 0 deletions src/controller/CHIPDeviceController.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,16 @@ class DLL_EXPORT DeviceController : public AbstractDnssdDiscoveryController
return nullptr;
}

Messaging::ExchangeManager * ExchangeMgr()
{
if (mSystemState)
{
return mSystemState->ExchangeMgr();
}

return nullptr;
}

CHIP_ERROR GetPeerAddressAndPort(NodeId peerId, Inet::IPAddress & addr, uint16_t & port);

/**
Expand Down
1 change: 1 addition & 0 deletions src/controller/java/AndroidDeviceControllerWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ AndroidDeviceControllerWrapper * AndroidDeviceControllerWrapper::AllocateNew(
*errInfoOnFailure = err;
return nullptr;
}
chip::Credentials::SetGroupDataProvider(&wrapper->mGroupDataProvider);
initParams.groupDataProvider = &wrapper->mGroupDataProvider;

err = wrapper->mOpCertStore.Init(wrapperStorage);
Expand Down
184 changes: 180 additions & 4 deletions src/controller/java/CHIPDeviceController-JNI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include <controller/CHIPDeviceController.h>
#include <controller/CommissioningWindowOpener.h>
#include <controller/java/AndroidClusterExceptions.h>
#include <controller/java/GroupDeviceProxy.h>
#include <credentials/CHIPCert.h>
#include <jni.h>
#include <lib/support/CHIPMem.h>
Expand Down Expand Up @@ -1273,6 +1274,183 @@ JNI_METHOD(void, releaseOperationalDevicePointer)(JNIEnv * env, jobject self, jl
}
}

JNI_METHOD(jlong, getGroupDevicePointer)(JNIEnv * env, jobject self, jlong handle, jint groupId)
{
chip::DeviceLayer::StackLock lock;
AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle);

GroupDeviceProxy * device = new GroupDeviceProxy(static_cast<GroupId>(groupId), wrapper->Controller()->GetFabricIndex(), wrapper->Controller()->ExchangeMgr());
return reinterpret_cast<jlong>(device);
}

JNI_METHOD(jobject, getAvailableGroupIds)(JNIEnv * env, jobject self, jlong handle)
{
chip::DeviceLayer::StackLock lock;
AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle);

CHIP_ERROR err = CHIP_NO_ERROR;
chip::Credentials::GroupDataProvider * groupDataProvider = chip::Credentials::GetGroupDataProvider();
auto it = groupDataProvider->IterateGroupInfo(wrapper->Controller()->GetFabricIndex());

jobject groupIds;
err = chip::JniReferences::GetInstance().CreateArrayList(groupIds);

chip::Credentials::GroupDataProvider::GroupInfo group;

if (it)
{
while (it->Next(group))
{
jobject jGroupId;
chip::JniReferences::GetInstance().CreateBoxedObject<jint>("java/lang/Integer", "(I)V", static_cast<jint>(group.group_id), jGroupId);
chip::JniReferences::GetInstance().AddToList(groupIds, jGroupId);
}
}

return groupIds;
}

JNI_METHOD(jstring, getGroupName)(JNIEnv * env, jobject self, jlong handle, jint jGroupId)
{
chip::DeviceLayer::StackLock lock;
AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle);

chip::Credentials::GroupDataProvider * groupDataProvider = chip::Credentials::GetGroupDataProvider();
auto it = groupDataProvider->IterateGroupInfo(wrapper->Controller()->GetFabricIndex());

GroupId groupId = static_cast<GroupId>(jGroupId);
chip::Credentials::GroupDataProvider::GroupInfo group;

if (it)
{
while (it->Next(group))
{
if (group.group_id == groupId) {
return env->NewStringUTF(group.name);
}
}
}

return nullptr;
}

JNI_METHOD(jobject, findKeySetId)(JNIEnv * env, jobject self, jlong handle, jint jGroupId)
{
chip::DeviceLayer::StackLock lock;
AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle);

chip::Credentials::GroupDataProvider * groupDataProvider = chip::Credentials::GetGroupDataProvider();
auto iter = groupDataProvider->IterateGroupKeys(wrapper->Controller()->GetFabricIndex());
chip::Credentials::GroupDataProvider::GroupKey groupKey;
GroupId groupId = static_cast<GroupId>(jGroupId);
jobject wrapperKeyId;
while (iter->Next(groupKey))
{
if (groupKey.group_id == groupId)
{
jobject jKeyId;
chip::JniReferences::GetInstance().CreateBoxedObject<jint>("java/lang/Integer", "(I)V", static_cast<jint>(groupKey.keyset_id), jKeyId);
chip::JniReferences::GetInstance().CreateOptional(jKeyId, wrapperKeyId);
iter->Release();
return wrapperKeyId;
}
}
iter->Release();
chip::JniReferences::GetInstance().CreateOptional(nullptr, wrapperKeyId);
return wrapperKeyId;
}

JNI_METHOD(jboolean, addGroup)(JNIEnv * env, jobject self, jlong handle, jint jGroupId, jstring groupName)
{
chip::DeviceLayer::StackLock lock;
AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle);

chip::Credentials::GroupDataProvider * groupDataProvider = chip::Credentials::GetGroupDataProvider();
chip::Credentials::GroupDataProvider::GroupInfo group;

chip::JniUtfString jniGroupName(env, groupName);
group.SetName(jniGroupName.charSpan());
group.group_id = static_cast<GroupId>(jGroupId);

CHIP_ERROR err = groupDataProvider->SetGroupInfo(wrapper->Controller()->GetFabricIndex(), group);

return err == CHIP_NO_ERROR ? JNI_TRUE : JNI_FALSE;
}

JNI_METHOD(jboolean, bindKeySet)(JNIEnv * env, jobject self, jlong handle, jint jGroupId, jint jKeySetId)
{
chip::DeviceLayer::StackLock lock;
AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle);

chip::Credentials::GroupDataProvider * groupDataProvider = chip::Credentials::GetGroupDataProvider();
auto iter = groupDataProvider->IterateGroupKeys(wrapper->Controller()->GetFabricIndex());
size_t current_count = iter->Count();

iter->Release();
CHIP_ERROR err = groupDataProvider->SetGroupKeyAt(wrapper->Controller()->GetFabricIndex(), current_count, chip::Credentials::GroupDataProvider::GroupKey(static_cast<uint16_t>(jGroupId), static_cast<uint16_t>(jKeySetId)));
return err == CHIP_NO_ERROR ? JNI_TRUE : JNI_FALSE;
}

JNI_METHOD(jboolean, addKeySet)(JNIEnv * env, jobject self, jlong handle, jint jKeySetId, jint jKeyPolicy, jlong validityTime, jbyteArray epochKey)
{
chip::DeviceLayer::StackLock lock;
AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle);

chip::Credentials::GroupDataProvider * groupDataProvider = chip::Credentials::GetGroupDataProvider();
uint8_t compressed_fabric_id[sizeof(chip::FabricId)];
chip::MutableByteSpan compressed_fabric_id_span(compressed_fabric_id);
VerifyOrReturnValue(wrapper->Controller()->GetCompressedFabricIdBytes(compressed_fabric_id_span) == CHIP_NO_ERROR, JNI_FALSE);

chip::Credentials::GroupDataProvider::SecurityPolicy keyPolicy = static_cast<chip::Credentials::GroupDataProvider::SecurityPolicy>(jKeyPolicy);
chip::JniByteArray jniEpochKey(env, epochKey);
size_t epochKeySize = static_cast<size_t>(jniEpochKey.size());
if ((keyPolicy != chip::Credentials::GroupDataProvider::SecurityPolicy::kCacheAndSync &&
keyPolicy != chip::Credentials::GroupDataProvider::SecurityPolicy::kTrustFirst) ||
epochKeySize != chip::Credentials::GroupDataProvider::EpochKey::kLengthBytes)
{
return JNI_FALSE;
}

chip::Credentials::GroupDataProvider::KeySet keySet(static_cast<uint16_t>(jKeySetId), keyPolicy, 1);
chip::Credentials::GroupDataProvider::EpochKey epoch_key;
epoch_key.start_time = static_cast<uint64_t>(validityTime);
memcpy(epoch_key.key, jniEpochKey.byteSpan().data(), chip::Credentials::GroupDataProvider::EpochKey::kLengthBytes);
memcpy(keySet.epoch_keys, &epoch_key, sizeof(chip::Credentials::GroupDataProvider::EpochKey));

VerifyOrReturnValue(groupDataProvider->SetKeySet(wrapper->Controller()->GetFabricIndex(), compressed_fabric_id_span, keySet) == CHIP_NO_ERROR, JNI_FALSE);

return JNI_TRUE;
}

JNI_METHOD(jobject, getKeysetIds)(JNIEnv * env, jobject self, jlong handle)
{
chip::DeviceLayer::StackLock lock;
AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle);

CHIP_ERROR err = CHIP_NO_ERROR;
chip::Credentials::GroupDataProvider * groupDataProvider = chip::Credentials::GetGroupDataProvider();
auto it = groupDataProvider->IterateKeySets(wrapper->Controller()->GetFabricIndex());

jobject keySetIds;
err = chip::JniReferences::GetInstance().CreateArrayList(keySetIds);

chip::Credentials::GroupDataProvider::KeySet keySet;

if (it)
{
while (it->Next(keySet))
{
jobject jKeySetId;
chip::JniReferences::GetInstance().CreateBoxedObject<jint>("java/lang/Integer", "(I)V", static_cast<jint>(keySet.keyset_id), jKeySetId);
chip::JniReferences::GetInstance().AddToList(keySetIds, jKeySetId);
ChipLogProgress(Controller, "keySet : %u, %u", keySet.keyset_id, static_cast<uint8_t>(keySet.policy));
}
it->Release();
}

return keySetIds;
}

JNI_METHOD(jint, getFabricIndex)(JNIEnv * env, jobject self, jlong handle)
{
chip::DeviceLayer::StackLock lock;
Expand Down Expand Up @@ -2131,10 +2309,8 @@ JNI_METHOD(void, invoke)
SuccessOrExit(err = commandSender->FinishCommand(convertedTimedRequestTimeoutMs != 0
? Optional<uint16_t>(convertedTimedRequestTimeoutMs)
: Optional<uint16_t>::Missing()));
SuccessOrExit(err =
commandSender->SendCommandRequest(device->GetSecureSession().Value(),
imTimeoutMs != 0 ? MakeOptional(System::Clock::Milliseconds32(imTimeoutMs))
: Optional<System::Clock::Timeout>::Missing()));

SuccessOrExit(err = device->GetSecureSession().Value()->IsGroupSession() ? commandSender->SendGroupCommandRequest(device->GetSecureSession().Value()) : commandSender->SendCommandRequest(device->GetSecureSession().Value(), imTimeoutMs != 0 ? MakeOptional(System::Clock::Milliseconds32(imTimeoutMs)) : Optional<System::Clock::Timeout>::Missing()));

callback->mCommandSender = commandSender;

Expand Down
Loading

0 comments on commit bbb65fa

Please sign in to comment.