diff --git a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/GenericChipDeviceListener.kt b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/GenericChipDeviceListener.kt
index 12e17998fa7c15..e4542fc518e9a8 100644
--- a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/GenericChipDeviceListener.kt
+++ b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/GenericChipDeviceListener.kt
@@ -1,6 +1,7 @@
package com.google.chip.chiptool
import chip.devicecontroller.ChipDeviceController
+import chip.devicecontroller.ICDDeviceInfo
open class GenericChipDeviceListener : ChipDeviceController.CompletionListener {
override fun onConnectDeviceComplete() {
@@ -56,7 +57,12 @@ open class GenericChipDeviceListener : ChipDeviceController.CompletionListener {
// No op
}
- override fun onICDRegistrationComplete(icdNodeId: Long, icdCounter: Long) {
+ override fun onICDRegistrationComplete(
+ errorCode: Int,
+ icdNodeId: Long,
+ icdCounter: Long,
+ icdDeviceInfo: ICDDeviceInfo
+ ) {
// No op
}
}
diff --git a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/SelectActionFragment.kt b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/SelectActionFragment.kt
index dc082d5dc5b73c..a597bd934514e7 100644
--- a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/SelectActionFragment.kt
+++ b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/SelectActionFragment.kt
@@ -74,6 +74,7 @@ class SelectActionFragment : Fragment() {
binding.unpairDeviceBtn.setOnClickListener { handleUnpairDeviceClicked() }
binding.groupSettingBtn.setOnClickListener { handleGroupSettingClicked() }
binding.otaProviderBtn.setOnClickListener { handleOTAProviderClicked() }
+ binding.icdBtn.setOnClickListener { handleICDClicked() }
return binding.root
}
@@ -244,6 +245,10 @@ class SelectActionFragment : Fragment() {
showFragment(GroupSettingFragment.newInstance())
}
+ private fun handleICDClicked() {
+ showFragment(ICDFragment.newInstance())
+ }
+
companion object {
@JvmStatic fun newInstance() = SelectActionFragment()
diff --git a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/ICDFragment.kt b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/ICDFragment.kt
new file mode 100644
index 00000000000000..3b27f58c3af575
--- /dev/null
+++ b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/ICDFragment.kt
@@ -0,0 +1,61 @@
+package com.google.chip.chiptool.clusterclient
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.lifecycleScope
+import chip.devicecontroller.ChipDeviceController
+import com.google.chip.chiptool.ChipClient
+import com.google.chip.chiptool.R
+import com.google.chip.chiptool.databinding.ICDFragmentBinding
+import kotlinx.coroutines.CoroutineScope
+
+class ICDFragment : Fragment() {
+ private val deviceController: ChipDeviceController
+ get() = ChipClient.getDeviceController(requireContext())
+
+ private lateinit var scope: CoroutineScope
+
+ private lateinit var addressUpdateFragment: AddressUpdateFragment
+
+ private var _binding: ICDFragmentBinding? = null
+ private val binding
+ get() = _binding!!
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ _binding = ICDFragmentBinding.inflate(inflater, container, false)
+ scope = viewLifecycleOwner.lifecycleScope
+
+ addressUpdateFragment =
+ childFragmentManager.findFragmentById(R.id.addressUpdateFragment) as AddressUpdateFragment
+
+ val clientInfo = deviceController.icdClientInfo
+ var msg = ""
+
+ for (info in clientInfo) {
+ msg += "$info\n"
+ }
+ showMessage(msg)
+
+ return binding.root
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+
+ private fun showMessage(msg: String) {
+ requireActivity().runOnUiThread { binding.icdTv.text = msg }
+ }
+
+ companion object {
+ fun newInstance(): ICDFragment = ICDFragment()
+ }
+}
diff --git a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/provisioning/DeviceProvisioningFragment.kt b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/provisioning/DeviceProvisioningFragment.kt
index 16a411bc5eba05..88c9965d31f658 100644
--- a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/provisioning/DeviceProvisioningFragment.kt
+++ b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/provisioning/DeviceProvisioningFragment.kt
@@ -32,6 +32,7 @@ import androidx.lifecycle.lifecycleScope
import chip.devicecontroller.AttestationInfo
import chip.devicecontroller.ChipDeviceController
import chip.devicecontroller.DeviceAttestationDelegate
+import chip.devicecontroller.ICDDeviceInfo
import chip.devicecontroller.ICDRegistrationInfo
import chip.devicecontroller.NetworkCredentials
import com.google.chip.chiptool.ChipClient
@@ -293,11 +294,22 @@ class DeviceProvisioningFragment : Fragment() {
)
}
- override fun onICDRegistrationComplete(icdNodeId: Long, icdCounter: Long) {
- Log.d(TAG, "onICDRegistrationComplete - icdNodeId : $icdNodeId, icdCounter : $icdCounter")
+ override fun onICDRegistrationComplete(
+ errorCode: Int,
+ icdNodeId: Long,
+ icdCounter: Long,
+ icdDeviceInfo: ICDDeviceInfo
+ ) {
+ Log.d(
+ TAG,
+ "onICDRegistrationComplete - errorCode: $errorCode, icdNodeId : $icdNodeId, icdCounter : $icdCounter, symmetricKey : ${icdDeviceInfo.symmetricKey.toHex()}"
+ )
}
}
+ private fun ByteArray.toHex(): String =
+ joinToString(separator = "") { eachByte -> "%02x".format(eachByte) }
+
/** Callback from [DeviceProvisioningFragment] notifying any registered listeners. */
interface Callback {
/** Notifies that commissioning has been completed. */
diff --git a/examples/android/CHIPTool/app/src/main/res/layout/i_c_d_fragment.xml b/examples/android/CHIPTool/app/src/main/res/layout/i_c_d_fragment.xml
new file mode 100644
index 00000000000000..6fb4e0fc35b6ca
--- /dev/null
+++ b/examples/android/CHIPTool/app/src/main/res/layout/i_c_d_fragment.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
diff --git a/examples/android/CHIPTool/app/src/main/res/layout/select_action_fragment.xml b/examples/android/CHIPTool/app/src/main/res/layout/select_action_fragment.xml
index 2ec36b43128be4..5824e8061a4e03 100644
--- a/examples/android/CHIPTool/app/src/main/res/layout/select_action_fragment.xml
+++ b/examples/android/CHIPTool/app/src/main/res/layout/select_action_fragment.xml
@@ -127,6 +127,14 @@
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="@string/ota_provider_btn_text" />
+
+
diff --git a/examples/android/CHIPTool/app/src/main/res/values/strings.xml b/examples/android/CHIPTool/app/src/main/res/values/strings.xml
index 88bb47ffe311e8..0737ba3be27b62 100644
--- a/examples/android/CHIPTool/app/src/main/res/values/strings.xml
+++ b/examples/android/CHIPTool/app/src/main/res/values/strings.xml
@@ -274,4 +274,6 @@
Endpoint ID
Write
Node ID
+
+ Intermittently Connected Device
diff --git a/examples/java-matter-controller/BUILD.gn b/examples/java-matter-controller/BUILD.gn
index 55e595612f02e4..34de1ed5585c12 100644
--- a/examples/java-matter-controller/BUILD.gn
+++ b/examples/java-matter-controller/BUILD.gn
@@ -41,6 +41,7 @@ kotlin_binary("java-matter-controller") {
"java/src/com/matter/controller/commands/discover/DiscoverCommand.kt",
"java/src/com/matter/controller/commands/discover/DiscoverCommissionablesCommand.kt",
"java/src/com/matter/controller/commands/discover/DiscoverCommissionersCommand.kt",
+ "java/src/com/matter/controller/commands/icd/ICDListCommand.kt",
"java/src/com/matter/controller/commands/pairing/CloseSessionCommand.kt",
"java/src/com/matter/controller/commands/pairing/DiscoveryFilterType.kt",
"java/src/com/matter/controller/commands/pairing/PairAddressPaseCommand.kt",
diff --git a/examples/java-matter-controller/java/src/com/matter/controller/Main.kt b/examples/java-matter-controller/java/src/com/matter/controller/Main.kt
index 755ea6e3e33b68..d0e2ef892b0e95 100644
--- a/examples/java-matter-controller/java/src/com/matter/controller/Main.kt
+++ b/examples/java-matter-controller/java/src/com/matter/controller/Main.kt
@@ -21,6 +21,7 @@ import chip.devicecontroller.ChipDeviceController
import chip.devicecontroller.ControllerParams
import com.matter.controller.commands.common.*
import com.matter.controller.commands.discover.*
+import com.matter.controller.commands.icd.*
import com.matter.controller.commands.pairing.*
private fun getDiscoveryCommands(
@@ -69,6 +70,15 @@ private fun getImCommands(
)
}
+private fun getICDCommands(
+ controller: ChipDeviceController,
+ credentialsIssuer: CredentialsIssuer
+): List {
+ return listOf(
+ ICDListCommand(controller, credentialsIssuer),
+ )
+}
+
fun main(args: Array) {
val controller =
ChipDeviceController(
@@ -84,6 +94,7 @@ fun main(args: Array) {
commandManager.register("discover", getDiscoveryCommands(controller, credentialsIssuer))
commandManager.register("pairing", getPairingCommands(controller, credentialsIssuer))
commandManager.register("im", getImCommands(controller, credentialsIssuer))
+ commandManager.register("icd", getICDCommands(controller, credentialsIssuer))
try {
commandManager.run(args)
diff --git a/examples/java-matter-controller/java/src/com/matter/controller/commands/icd/ICDListCommand.kt b/examples/java-matter-controller/java/src/com/matter/controller/commands/icd/ICDListCommand.kt
new file mode 100644
index 00000000000000..aa32a1391f9e7b
--- /dev/null
+++ b/examples/java-matter-controller/java/src/com/matter/controller/commands/icd/ICDListCommand.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2023 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.
+ *
+ */
+package com.matter.controller.commands.icd
+
+import chip.devicecontroller.ChipDeviceController
+import com.matter.controller.commands.common.CredentialsIssuer
+import com.matter.controller.commands.common.MatterCommand
+
+class ICDListCommand(val controller: ChipDeviceController, credsIssuer: CredentialsIssuer?) :
+ MatterCommand(controller, credsIssuer, "list") {
+
+ override fun runCommand() {
+ val info = controller.icdClientInfo
+ println(" +-----------------------------------------------------------------------------+")
+ System.out.format(
+ " | %-52s %10s : %8d |\n",
+ "Known ICDs",
+ "FabricIndex",
+ controller.fabricIndex
+ )
+ println(" +-----------------------------------------------------------------------------+")
+ System.out.format(
+ " | %20s | %15s | %15s | %16s |\n",
+ "Node ID",
+ "Start Counter",
+ "Counter Offset",
+ "MonitoredSubject"
+ )
+
+ for (i in info) {
+ println(" +-----------------------------------------------------------------------------+")
+ System.out.format(
+ " | %20d | %15d | %15d | %16d |\n",
+ i.peerNodeId,
+ i.startCounter,
+ i.offset,
+ i.monitoredSubject
+ )
+ System.out.format(" | %-10s : %62s |\n", "aes key", i.icdAesKey.toHex())
+ System.out.format(" | %-10s : %62s |\n", "hamc key", i.icdHmacKey.toHex())
+ }
+ println(" +-----------------------------------------------------------------------------+")
+ }
+
+ private fun ByteArray.toHex(): String =
+ joinToString(separator = "") { eachByte -> "%02x".format(eachByte) }
+}
diff --git a/examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairingCommand.kt b/examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairingCommand.kt
index 572b309f02b74b..8ba8fddfa501d5 100644
--- a/examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairingCommand.kt
+++ b/examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairingCommand.kt
@@ -18,6 +18,7 @@
package com.matter.controller.commands.pairing
import chip.devicecontroller.ChipDeviceController
+import chip.devicecontroller.ICDDeviceInfo
import chip.devicecontroller.ICDRegistrationInfo
import chip.devicecontroller.NetworkCredentials
import com.matter.controller.commands.common.CredentialsIssuer
@@ -185,10 +186,15 @@ abstract class PairingCommand(
.updateCommissioningICDRegistrationInfo(ICDRegistrationInfo.newBuilder().build())
}
- override fun onICDRegistrationComplete(icdNodeId: Long, icdCounter: Long) {
+ override fun onICDRegistrationComplete(
+ errorCode: Int,
+ icdNodeId: Long,
+ icdCounter: Long,
+ icdDeviceInfo: ICDDeviceInfo
+ ) {
logger.log(
Level.INFO,
- "onICDRegistrationComplete with icdNodeId: $icdNodeId, icdCounter: $icdCounter"
+ "onICDRegistrationComplete with errorCode: $errorCode, icdNodeId: $icdNodeId, icdCounter: $icdCounter, symmetricKey: ${icdDeviceInfo.symmetricKey.toHex()}"
)
}
@@ -244,6 +250,9 @@ abstract class PairingCommand(
return useOnlyOnNetworkDiscovery.get()
}
+ private fun ByteArray.toHex(): String =
+ joinToString(separator = "") { eachByte -> "%02x".format(eachByte) }
+
companion object {
private val logger = Logger.getLogger(PairingCommand::class.java.name)
}
diff --git a/examples/kotlin-matter-controller/java/src/com/matter/controller/commands/pairing/PairingCommand.kt b/examples/kotlin-matter-controller/java/src/com/matter/controller/commands/pairing/PairingCommand.kt
index 0e84ff7ceabf41..712ad374ced579 100644
--- a/examples/kotlin-matter-controller/java/src/com/matter/controller/commands/pairing/PairingCommand.kt
+++ b/examples/kotlin-matter-controller/java/src/com/matter/controller/commands/pairing/PairingCommand.kt
@@ -17,6 +17,7 @@
*/
package com.matter.controller.commands.pairing
+import chip.devicecontroller.ICDDeviceInfo
import com.matter.controller.commands.common.CredentialsIssuer
import com.matter.controller.commands.common.IPAddress
import com.matter.controller.commands.common.MatterCommand
@@ -177,10 +178,15 @@ abstract class PairingCommand(
logger.log(Level.INFO, "onICDRegistrationInfoRequired")
}
- override fun onICDRegistrationComplete(icdNodeId: Long, icdCounter: Long) {
+ override fun onICDRegistrationComplete(
+ errorCode: Int,
+ icdNodeId: Long,
+ icdCounter: Long,
+ icdDeviceInfo: ICDDeviceInfo
+ ) {
logger.log(
Level.INFO,
- "onICDRegistrationComplete with icdNodeId: $icdNodeId, icdCounter: $icdCounter"
+ "onICDRegistrationComplete with errorCode: $errorCode, icdNodeId: $icdNodeId, icdCounter: $icdCounter, symmetricKey: ${icdDeviceInfo.symmetricKey.toHex()}"
)
}
@@ -212,6 +218,9 @@ abstract class PairingCommand(
return onboardingPayload.toString()
}
+ private fun ByteArray.toHex(): String =
+ joinToString(separator = "") { eachByte -> "%02x".format(eachByte) }
+
private fun String.hexToByteArray(): ByteArray {
return chunked(2).map { byteStr -> byteStr.toUByte(16).toByte() }.toByteArray()
}
diff --git a/src/controller/java/AndroidDeviceControllerWrapper.cpp b/src/controller/java/AndroidDeviceControllerWrapper.cpp
index e1579009f74175..93be8209ee4029 100644
--- a/src/controller/java/AndroidDeviceControllerWrapper.cpp
+++ b/src/controller/java/AndroidDeviceControllerWrapper.cpp
@@ -171,6 +171,13 @@ AndroidDeviceControllerWrapper * AndroidDeviceControllerWrapper::AllocateNew(
const chip::Credentials::AttestationTrustStore * testingRootStore = chip::Credentials::GetTestAttestationTrustStore();
chip::Credentials::SetDeviceAttestationVerifier(GetDefaultDACVerifier(testingRootStore));
+ *errInfoOnFailure = wrapper->mICDClientStorage.Init(wrapperStorage, &wrapper->mSessionKeystore);
+ if (*errInfoOnFailure != CHIP_NO_ERROR)
+ {
+ ChipLogError(Controller, "ICD Client Storage failure");
+ return nullptr;
+ }
+
chip::Controller::FactoryInitParams initParams;
chip::Controller::SetupParams setupParams;
@@ -358,6 +365,8 @@ AndroidDeviceControllerWrapper * AndroidDeviceControllerWrapper::AllocateNew(
*errInfoOnFailure = chip::Credentials::SetSingleIpkEpochKey(
&wrapper->mGroupDataProvider, wrapper->Controller()->GetFabricIndex(), ipkSpan, compressedFabricIdSpan);
+ wrapper->getICDClientStorage()->UpdateFabricList(wrapper->Controller()->GetFabricIndex());
+
memset(ipkBuffer.data(), 0, ipkBuffer.size());
if (*errInfoOnFailure != CHIP_NO_ERROR)
@@ -450,6 +459,8 @@ CHIP_ERROR AndroidDeviceControllerWrapper::ApplyICDRegistrationInfo(chip::Contro
chip::DeviceLayer::StackUnlock unlock;
CHIP_ERROR err = CHIP_NO_ERROR;
+ mDeviceIsICD = false;
+
VerifyOrReturnError(icdRegistrationInfo != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
JNIEnv * env = chip::JniReferences::GetInstance().GetEnvForCurrentThread();
@@ -637,6 +648,16 @@ void AndroidDeviceControllerWrapper::OnPairingDeleted(CHIP_ERROR error)
void AndroidDeviceControllerWrapper::OnCommissioningComplete(NodeId deviceId, CHIP_ERROR error)
{
chip::DeviceLayer::StackUnlock unlock;
+
+ if (error != CHIP_NO_ERROR && mDeviceIsICD)
+ {
+ CHIP_ERROR deleteEntryError = mICDClientStorage.DeleteEntry(ScopedNodeId(deviceId, Controller()->GetFabricIndex()));
+ if (deleteEntryError != CHIP_NO_ERROR)
+ {
+ ChipLogError(chipTool, "Failed to delete ICD entry: %" CHIP_ERROR_FORMAT, deleteEntryError.Format());
+ }
+ }
+
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
jmethodID onCommissioningCompleteMethod;
CHIP_ERROR err = JniReferences::GetInstance().FindMethod(env, mJavaObjectRef, "onCommissioningComplete", "(JI)V",
@@ -889,14 +910,61 @@ void AndroidDeviceControllerWrapper::OnICDRegistrationInfoRequired()
void AndroidDeviceControllerWrapper::OnICDRegistrationComplete(chip::NodeId icdNodeId, uint32_t icdCounter)
{
chip::DeviceLayer::StackUnlock unlock;
+
+ CHIP_ERROR err = CHIP_NO_ERROR;
+ chip::app::ICDClientInfo clientInfo;
+ clientInfo.peer_node = ScopedNodeId(icdNodeId, Controller()->GetFabricIndex());
+ clientInfo.monitored_subject = mAutoCommissioner.GetCommissioningParameters().GetICDMonitoredSubject().Value();
+ clientInfo.start_icd_counter = icdCounter;
+
+ ByteSpan symmetricKey = mAutoCommissioner.GetCommissioningParameters().GetICDSymmetricKey().Value();
+
+ err = mICDClientStorage.SetKey(clientInfo, symmetricKey);
+ if (err == CHIP_NO_ERROR)
+ {
+ err = mICDClientStorage.StoreEntry(clientInfo);
+ }
+
+ if (err == CHIP_NO_ERROR)
+ {
+ ChipLogProgress(Controller, "Saved ICD Symmetric key for " ChipLogFormatX64, ChipLogValueX64(icdNodeId));
+ }
+ else
+ {
+ mICDClientStorage.RemoveKey(clientInfo);
+ ChipLogError(Controller, "Failed to persist symmetric key for " ChipLogFormatX64 ": %s", ChipLogValueX64(icdNodeId),
+ err.AsString());
+ }
+
+ mDeviceIsICD = true;
+
JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
jmethodID onICDRegistrationCompleteMethod;
- CHIP_ERROR err = JniReferences::GetInstance().FindMethod(env, mJavaObjectRef, "onICDRegistrationComplete", "(JJ)V",
- &onICDRegistrationCompleteMethod);
- VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Error finding Java method: %" CHIP_ERROR_FORMAT, err.Format()));
-
- env->CallVoidMethod(mJavaObjectRef, onICDRegistrationCompleteMethod, static_cast(icdNodeId),
- static_cast(icdCounter));
+ jclass icdDeviceInfoClass = nullptr;
+ jmethodID icdDeviceInfoStructCtor = nullptr;
+ jobject icdDeviceInfoObj = nullptr;
+ jbyteArray jSymmetricKey = nullptr;
+ CHIP_ERROR methodErr =
+ JniReferences::GetInstance().FindMethod(env, mJavaObjectRef, "onICDRegistrationComplete",
+ "(IJJLchip/devicecontroller/ICDDeviceInfo;)V", &onICDRegistrationCompleteMethod);
+ VerifyOrReturn(methodErr == CHIP_NO_ERROR,
+ ChipLogError(Controller, "Error finding Java method: %" CHIP_ERROR_FORMAT, methodErr.Format()));
+
+ methodErr = chip::JniReferences::GetInstance().GetClassRef(env, "chip/devicecontroller/ICDDeviceInfo", icdDeviceInfoClass);
+ VerifyOrReturn(methodErr == CHIP_NO_ERROR, ChipLogError(Controller, "Could not find class ICDDeviceInfo"));
+
+ icdDeviceInfoStructCtor = env->GetMethodID(icdDeviceInfoClass, "", "([B)V");
+ VerifyOrReturn(icdDeviceInfoStructCtor != nullptr, ChipLogError(Controller, "Could not find ICDDeviceInfo constructor"));
+
+ methodErr =
+ JniReferences::GetInstance().N2J_ByteArray(env, symmetricKey.data(), static_cast(symmetricKey.size()), jSymmetricKey);
+ VerifyOrReturn(methodErr == CHIP_NO_ERROR,
+ ChipLogError(Controller, "Error Parsing Symmetric Key: %" CHIP_ERROR_FORMAT, methodErr.Format()));
+
+ icdDeviceInfoObj = env->NewObject(icdDeviceInfoClass, icdDeviceInfoStructCtor, jSymmetricKey);
+
+ env->CallVoidMethod(mJavaObjectRef, onICDRegistrationCompleteMethod, static_cast(err.AsInteger()),
+ static_cast(icdNodeId), static_cast(icdCounter), icdDeviceInfoObj);
}
CHIP_ERROR AndroidDeviceControllerWrapper::SyncGetKeyValue(const char * key, void * value, uint16_t & size)
diff --git a/src/controller/java/AndroidDeviceControllerWrapper.h b/src/controller/java/AndroidDeviceControllerWrapper.h
index 6594c41fcdd66f..982609376e9319 100644
--- a/src/controller/java/AndroidDeviceControllerWrapper.h
+++ b/src/controller/java/AndroidDeviceControllerWrapper.h
@@ -24,6 +24,7 @@
#include
+#include
#include
#include
#include
@@ -205,6 +206,8 @@ class AndroidDeviceControllerWrapper : public chip::Controller::DevicePairingDel
CHIP_ERROR FinishOTAProvider();
+ chip::app::DefaultICDClientStorage * getICDClientStorage() { return &mICDClientStorage; }
+
private:
using ChipDeviceControllerPtr = std::unique_ptr;
@@ -217,6 +220,8 @@ class AndroidDeviceControllerWrapper : public chip::Controller::DevicePairingDel
// TODO: This may need to be injected as a SessionKeystore*
chip::Crypto::RawKeySessionKeystore mSessionKeystore;
+ chip::app::DefaultICDClientStorage mICDClientStorage;
+
JavaVM * mJavaVM = nullptr;
jobject mJavaObjectRef = nullptr;
#ifdef JAVA_MATTER_CONTROLLER_TEST
@@ -249,6 +254,7 @@ class AndroidDeviceControllerWrapper : public chip::Controller::DevicePairingDel
#if CHIP_DEVICE_CONFIG_DYNAMIC_SERVER
OTAProviderDelegateBridge * mOtaProviderBridge = nullptr;
#endif
+ bool mDeviceIsICD = false;
uint8_t mICDSymmetricKey[chip::Crypto::kAES_CCM128_Key_Length];
AndroidDeviceControllerWrapper(ChipDeviceControllerPtr controller,
diff --git a/src/controller/java/BUILD.gn b/src/controller/java/BUILD.gn
index e481e74feeda1c..ecb775027b45f6 100644
--- a/src/controller/java/BUILD.gn
+++ b/src/controller/java/BUILD.gn
@@ -447,6 +447,8 @@ android_library("java") {
"src/chip/devicecontroller/DiscoveredDevice.java",
"src/chip/devicecontroller/GetConnectedDeviceCallbackJni.java",
"src/chip/devicecontroller/GroupKeySecurityPolicy.java",
+ "src/chip/devicecontroller/ICDClientInfo.java",
+ "src/chip/devicecontroller/ICDDeviceInfo.java",
"src/chip/devicecontroller/ICDRegistrationInfo.java",
"src/chip/devicecontroller/InvokeCallback.java",
"src/chip/devicecontroller/InvokeCallbackJni.java",
diff --git a/src/controller/java/CHIPDeviceController-JNI.cpp b/src/controller/java/CHIPDeviceController-JNI.cpp
index e7d6b31be580ce..9b3e67b6bf253b 100644
--- a/src/controller/java/CHIPDeviceController-JNI.cpp
+++ b/src/controller/java/CHIPDeviceController-JNI.cpp
@@ -658,10 +658,7 @@ JNI_METHOD(void, pairDevice)
}
commissioningParams.SetICDRegistrationStrategy(ICDRegistrationStrategy::kBeforeComplete);
- if (icdRegistrationInfo != nullptr)
- {
- wrapper->ApplyICDRegistrationInfo(commissioningParams, icdRegistrationInfo);
- }
+ wrapper->ApplyICDRegistrationInfo(commissioningParams, icdRegistrationInfo);
if (wrapper->GetDeviceAttestationDelegateBridge() != nullptr)
{
@@ -708,10 +705,7 @@ JNI_METHOD(void, pairDeviceWithAddress)
}
commissioningParams.SetICDRegistrationStrategy(ICDRegistrationStrategy::kBeforeComplete);
- if (icdRegistrationInfo != nullptr)
- {
- wrapper->ApplyICDRegistrationInfo(commissioningParams, icdRegistrationInfo);
- }
+ wrapper->ApplyICDRegistrationInfo(commissioningParams, icdRegistrationInfo);
if (wrapper->GetDeviceAttestationDelegateBridge() != nullptr)
{
@@ -763,10 +757,7 @@ JNI_METHOD(void, pairDeviceWithCode)
}
commissioningParams.SetICDRegistrationStrategy(ICDRegistrationStrategy::kBeforeComplete);
- if (icdRegistrationInfo != nullptr)
- {
- wrapper->ApplyICDRegistrationInfo(commissioningParams, icdRegistrationInfo);
- }
+ wrapper->ApplyICDRegistrationInfo(commissioningParams, icdRegistrationInfo);
if (wrapper->GetDeviceAttestationDelegateBridge() != nullptr)
{
@@ -2152,6 +2143,73 @@ JNI_METHOD(jobject, computePaseVerifier)
return nullptr;
}
+JNI_METHOD(jobject, getICDClientInfo)(JNIEnv * env, jobject self, jlong handle, jint jFabricIndex)
+{
+ chip::DeviceLayer::StackLock lock;
+
+ CHIP_ERROR err = CHIP_NO_ERROR;
+
+ jobject jInfo = nullptr;
+ chip::app::ICDClientInfo info;
+ chip::FabricIndex fabricIndex = static_cast(jFabricIndex);
+
+ ChipLogProgress(Controller, "getICDClientInfo(%u) called", fabricIndex);
+ AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle);
+ VerifyOrReturnValue(wrapper != nullptr, nullptr, ChipLogError(Controller, "wrapper is null"));
+
+ err = JniReferences::GetInstance().CreateArrayList(jInfo);
+ VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr,
+ ChipLogError(Controller, "CreateArrayList failed!: %" CHIP_ERROR_FORMAT, err.Format()));
+
+ auto iter = wrapper->getICDClientStorage()->IterateICDClientInfo();
+
+ jmethodID constructor;
+ jclass infoClass;
+ JniLocalReferenceManager manager(env);
+
+ err = JniReferences::GetInstance().GetLocalClassRef(env, "chip/devicecontroller/ICDClientInfo", infoClass);
+ VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr,
+ ChipLogError(Controller, "Find ICDClientInfo class: %" CHIP_ERROR_FORMAT, err.Format()));
+
+ env->ExceptionClear();
+ constructor = env->GetMethodID(infoClass, "", "(JJJJ[B[B)V");
+ VerifyOrReturnValue(constructor != nullptr, nullptr, ChipLogError(Controller, "Find GetMethodID error!"));
+
+ while (iter->Next(info))
+ {
+ jbyteArray jIcdAesKey = nullptr;
+ jbyteArray jIcdHmacKey = nullptr;
+ jobject jICDClientInfo = nullptr;
+
+ if (info.peer_node.GetFabricIndex() != fabricIndex)
+ {
+ continue;
+ }
+
+ err = chip::JniReferences::GetInstance().N2J_ByteArray(env, info.aes_key_handle.As(),
+ chip::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES, jIcdAesKey);
+ VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr,
+ ChipLogError(Controller, "ICD AES KEY N2J_ByteArray error!: %" CHIP_ERROR_FORMAT, err.Format()));
+
+ err = chip::JniReferences::GetInstance().N2J_ByteArray(env, info.hmac_key_handle.As(),
+ chip::CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES, jIcdHmacKey);
+ VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr,
+ ChipLogError(Controller, "ICD HMAC KEY N2J_ByteArray error!: %" CHIP_ERROR_FORMAT, err.Format()));
+
+ jICDClientInfo = (jobject) env->NewObject(infoClass, constructor, static_cast(info.peer_node.GetNodeId()),
+ static_cast(info.start_icd_counter), static_cast(info.offset),
+ static_cast(info.monitored_subject), jIcdAesKey, jIcdHmacKey);
+
+ err = JniReferences::GetInstance().AddToList(jInfo, jICDClientInfo);
+ VerifyOrReturnValue(err == CHIP_NO_ERROR, nullptr,
+ ChipLogError(Controller, "AddToList error!: %" CHIP_ERROR_FORMAT, err.Format()));
+ }
+
+ iter->Release();
+
+ return jInfo;
+}
+
JNI_METHOD(void, subscribe)
(JNIEnv * env, jclass clz, jlong handle, jlong callbackHandle, jlong devicePtr, jobject attributePathList, jobject eventPathList,
jobject dataVersionFilterList, jint minInterval, jint maxInterval, jboolean keepSubscriptions, jboolean isFabricFiltered,
diff --git a/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java b/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java
index ba3a7091e26e5f..454b0df3a8a5f4 100644
--- a/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java
+++ b/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java
@@ -606,9 +606,10 @@ public void onICDRegistrationInfoRequired() {
}
}
- public void onICDRegistrationComplete(long icdNodeId, long icdCounter) {
+ public void onICDRegistrationComplete(
+ int errorCode, long icdNodeId, long icdCounter, ICDDeviceInfo icdDeviceInfo) {
if (completionListener != null) {
- completionListener.onICDRegistrationComplete(icdNodeId, icdCounter);
+ completionListener.onICDRegistrationComplete(errorCode, icdNodeId, icdCounter, icdDeviceInfo);
}
}
@@ -712,6 +713,19 @@ public int getFabricIndex() {
return getFabricIndex(deviceControllerPtr);
}
+ public List getICDClientInfo() {
+ return getICDClientInfo(deviceControllerPtr, getFabricIndex(deviceControllerPtr));
+ }
+
+ /**
+ * Returns the ICD Client Information
+ *
+ * @param fabricIndex the fabric index to check
+ */
+ public List getICDClientInfo(int fabricIndex) {
+ return getICDClientInfo(deviceControllerPtr, fabricIndex);
+ }
+
/* Shuts down all active subscriptions. */
public void shutdownSubscriptions() {
shutdownSubscriptions(deviceControllerPtr, null, null, null);
@@ -1486,6 +1500,8 @@ private native void updateCommissioningNetworkCredentials(
private native void updateCommissioningICDRegistrationInfo(
long deviceControllerPtr, ICDRegistrationInfo icdRegistrationInfo);
+ private native List getICDClientInfo(long deviceControllerPtr, long fabricIndex);
+
private native int onNOCChainGeneration(long deviceControllerPtr, ControllerParams params);
private native int getFabricIndex(long deviceControllerPtr);
@@ -1605,6 +1621,7 @@ void onReadCommissioningInfo(
void onICDRegistrationInfoRequired();
/** Notifies when the registration flow for the ICD completes. */
- void onICDRegistrationComplete(long icdNodeId, long icdCounter);
+ void onICDRegistrationComplete(
+ int errorCode, long icdNodeId, long icdCounter, ICDDeviceInfo icdDeviceInfo);
}
}
diff --git a/src/controller/java/src/chip/devicecontroller/ICDClientInfo.java b/src/controller/java/src/chip/devicecontroller/ICDClientInfo.java
new file mode 100644
index 00000000000000..77df9179e115ee
--- /dev/null
+++ b/src/controller/java/src/chip/devicecontroller/ICDClientInfo.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2020-2023 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.
+ *
+ */
+package chip.devicecontroller;
+
+/** Class for holding ICD Client information. */
+public class ICDClientInfo {
+ private final long peerNodeId;
+ private final long startCounter;
+ private final long offset;
+ private final long monitoredSubject;
+ private final byte[] icdAesKey;
+ private final byte[] icdHmacKey;
+
+ public ICDClientInfo(
+ long peerNodeId,
+ long startCounter,
+ long offset,
+ long monitoredSubject,
+ byte[] icdAesKey,
+ byte[] icdHmacKey) {
+ this.peerNodeId = peerNodeId;
+ this.startCounter = startCounter;
+ this.offset = offset;
+ this.monitoredSubject = monitoredSubject;
+ this.icdAesKey = icdAesKey;
+ this.icdHmacKey = icdHmacKey;
+ }
+
+ /** Returns the check in peer node ID. */
+ public long getPeerNodeId() {
+ return peerNodeId;
+ }
+
+ /** Returns the Start Counter. */
+ public long getStartCounter() {
+ return startCounter;
+ }
+
+ /** Returns the Counter offset. */
+ public long getOffset() {
+ return offset;
+ }
+
+ /** Returns the monitored subject of the ICD. */
+ public long getMonitoredSubject() {
+ return monitoredSubject;
+ }
+
+ /** Returns the 16 bytes ICD AES key. */
+ public byte[] getIcdAesKey() {
+ return icdAesKey;
+ }
+
+ /** Returns the 16 bytes ICD HMAC key. */
+ public byte[] getIcdHmacKey() {
+ return icdHmacKey;
+ }
+
+ @Override
+ public String toString() {
+ return "ICDClientInfo{"
+ + "\n\tpeerNodeId="
+ + peerNodeId
+ + "\n\t, startCounter="
+ + startCounter
+ + "\n\t, offset="
+ + offset
+ + "\n\t, monitoredSubject="
+ + monitoredSubject
+ + "\n\t, icdAesKey='"
+ + bytesToHex(icdAesKey)
+ + '\''
+ + "\n\t, icdHmacKey='"
+ + bytesToHex(icdHmacKey)
+ + '\''
+ + "\n}";
+ }
+
+ private static String bytesToHex(byte[] bytes) {
+ StringBuilder hexString = new StringBuilder();
+ for (byte b : bytes) {
+ String hex = Integer.toHexString(0xFF & b);
+ if (hex.length() == 1) {
+ hexString.append('0');
+ }
+ hexString.append(hex);
+ }
+ return hexString.toString();
+ }
+}
diff --git a/src/controller/java/src/chip/devicecontroller/ICDDeviceInfo.java b/src/controller/java/src/chip/devicecontroller/ICDDeviceInfo.java
new file mode 100644
index 00000000000000..a6d4666df9270b
--- /dev/null
+++ b/src/controller/java/src/chip/devicecontroller/ICDDeviceInfo.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020-2023 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.
+ *
+ */
+package chip.devicecontroller;
+
+/** Class for holding ICD Device information. */
+public class ICDDeviceInfo {
+ private byte[] symmetricKey;
+
+ public ICDDeviceInfo(byte[] symmetricKey) {
+ this.symmetricKey = symmetricKey;
+ }
+
+ /** Returns the 16 bytes ICD symmetric key. */
+ public byte[] getSymmetricKey() {
+ return symmetricKey;
+ }
+}
diff --git a/src/controller/java/src/matter/controller/CompletionListenerAdapter.kt b/src/controller/java/src/matter/controller/CompletionListenerAdapter.kt
index f536fae311edaf..71352075e0e912 100644
--- a/src/controller/java/src/matter/controller/CompletionListenerAdapter.kt
+++ b/src/controller/java/src/matter/controller/CompletionListenerAdapter.kt
@@ -18,6 +18,7 @@
package matter.controller
import chip.devicecontroller.ChipDeviceController
+import chip.devicecontroller.ICDDeviceInfo
import java.util.logging.Level
import java.util.logging.Logger
@@ -51,8 +52,12 @@ class CompletionListenerAdapter(val listener: MatterController.CompletionListene
override fun onICDRegistrationInfoRequired() = listener.onICDRegistrationInfoRequired()
- override fun onICDRegistrationComplete(icdNodeId: Long, icdCounter: Long) =
- listener.onICDRegistrationComplete(icdNodeId, icdCounter)
+ override fun onICDRegistrationComplete(
+ errorCode: Int,
+ icdNodeId: Long,
+ icdCounter: Long,
+ icdDeviceInfo: ICDDeviceInfo
+ ) = listener.onICDRegistrationComplete(errorCode, icdNodeId, icdCounter, icdDeviceInfo)
override fun onError(error: Throwable) = listener.onError(error)
diff --git a/src/controller/java/src/matter/controller/MatterController.kt b/src/controller/java/src/matter/controller/MatterController.kt
index 1487446ba455f4..77b0664f76d7ec 100644
--- a/src/controller/java/src/matter/controller/MatterController.kt
+++ b/src/controller/java/src/matter/controller/MatterController.kt
@@ -17,6 +17,7 @@
*/
package matter.controller
+import chip.devicecontroller.ICDDeviceInfo
import java.io.Closeable
/** Controller interface for interacting with a CHIP device. */
@@ -65,7 +66,12 @@ interface MatterController : Closeable, InteractionClient {
fun onICDRegistrationInfoRequired()
/** Notifies when the registration flow for the ICD completes. */
- fun onICDRegistrationComplete(icdNodeId: Long, icdCounter: Long)
+ fun onICDRegistrationComplete(
+ errorCode: Int,
+ icdNodeId: Long,
+ icdCounter: Long,
+ icdDeviceInfo: ICDDeviceInfo
+ )
}
/**