From b0b82005bdef1fe3936d40f20fd744ec85b5ee74 Mon Sep 17 00:00:00 2001
From: joonhaengHeo <85541460+joonhaengHeo@users.noreply.github.com>
Date: Sat, 7 Jan 2023 05:34:04 +0900
Subject: [PATCH] Implement unpairDevice API in android and chip-tool (#24127)
* Implement unpairDevice
* Check restyle
* Delete unused file
* Add default value
* Update from review
* Modify from reviewer comments
* Modify from comment
* Fix clang-tidy validation
* Change check state
---
.../google/chip/chiptool/CHIPToolActivity.kt | 5 +
.../chip/chiptool/SelectActionFragment.kt | 3 +
.../provisioning/UnpairDeviceFragment.kt | 60 ++++++
.../res/layout/select_action_fragment.xml | 8 +
.../res/layout/unpair_device_fragment.xml | 27 +++
.../app/src/main/res/values/strings.xml | 2 +
.../commands/pairing/PairingCommand.cpp | 22 ++-
.../commands/pairing/PairingCommand.h | 10 +-
src/controller/BUILD.gn | 2 +
src/controller/CHIPDeviceController.cpp | 7 +-
src/controller/CurrentFabricRemover.cpp | 184 ++++++++++++++++++
src/controller/CurrentFabricRemover.h | 108 ++++++++++
.../java/AndroidCurrentFabricRemover.cpp | 102 ++++++++++
.../java/AndroidCurrentFabricRemover.h | 48 +++++
src/controller/java/BUILD.gn | 3 +
.../java/CHIPDeviceController-JNI.cpp | 18 ++
.../ChipDeviceController.java | 7 +
.../UnpairDeviceCallback.java | 24 +++
18 files changed, 634 insertions(+), 6 deletions(-)
create mode 100644 examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/provisioning/UnpairDeviceFragment.kt
create mode 100644 examples/android/CHIPTool/app/src/main/res/layout/unpair_device_fragment.xml
create mode 100644 src/controller/CurrentFabricRemover.cpp
create mode 100644 src/controller/CurrentFabricRemover.h
create mode 100644 src/controller/java/AndroidCurrentFabricRemover.cpp
create mode 100644 src/controller/java/AndroidCurrentFabricRemover.h
create mode 100644 src/controller/java/src/chip/devicecontroller/UnpairDeviceCallback.java
diff --git a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/CHIPToolActivity.kt b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/CHIPToolActivity.kt
index ce37951915e283..e7005477deb898 100644
--- a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/CHIPToolActivity.kt
+++ b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/CHIPToolActivity.kt
@@ -44,6 +44,7 @@ import com.google.chip.chiptool.provisioning.AddressCommissioningFragment
import com.google.chip.chiptool.provisioning.DeviceProvisioningFragment
import com.google.chip.chiptool.provisioning.EnterNetworkFragment
import com.google.chip.chiptool.provisioning.ProvisionNetworkType
+import com.google.chip.chiptool.provisioning.UnpairDeviceFragment
import com.google.chip.chiptool.setuppayloadscanner.BarcodeFragment
import com.google.chip.chiptool.setuppayloadscanner.CHIPDeviceDetailsFragment
import com.google.chip.chiptool.setuppayloadscanner.CHIPDeviceInfo
@@ -191,6 +192,10 @@ class CHIPToolActivity :
showFragment(BarcodeFragment.newInstance())
}
+ override fun handleUnpairDeviceClicked() {
+ showFragment(UnpairDeviceFragment.newInstance())
+ }
+
private fun showFragment(fragment: Fragment, showOnBack: Boolean = true) {
val fragmentTransaction = supportFragmentManager
.beginTransaction()
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 263e979bb12b28..4816469c187f61 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
@@ -60,6 +60,7 @@ class SelectActionFragment : Fragment() {
clusterInteractionBtn.setOnClickListener { getCallback()?.handleClusterInteractionClicked() }
provisionCustomFlowBtn.setOnClickListener{ getCallback()?.handleCustomFlowClicked() }
wildcardBtn.setOnClickListener { getCallback()?.handleWildcardClicked() }
+ unpairDeviceBtn.setOnClickListener{ getCallback()?.handleUnpairDeviceClicked() }
}
}
@@ -157,6 +158,8 @@ class SelectActionFragment : Fragment() {
fun handleWildcardClicked()
/** Notifies listener of provision-custom-flow button click. */
fun handleCustomFlowClicked()
+ /** Notifies listener of unpair button click. */
+ fun handleUnpairDeviceClicked()
}
companion object {
diff --git a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/provisioning/UnpairDeviceFragment.kt b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/provisioning/UnpairDeviceFragment.kt
new file mode 100644
index 00000000000000..801d4fc1272836
--- /dev/null
+++ b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/provisioning/UnpairDeviceFragment.kt
@@ -0,0 +1,60 @@
+package com.google.chip.chiptool.provisioning
+
+import android.os.Bundle
+import android.util.Log
+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 chip.devicecontroller.UnpairDeviceCallback
+import com.google.chip.chiptool.ChipClient
+import com.google.chip.chiptool.R
+import com.google.chip.chiptool.clusterclient.AddressUpdateFragment
+import kotlinx.android.synthetic.main.unpair_device_fragment.view.unpairDeviceBtn
+import kotlinx.coroutines.*
+
+class UnpairDeviceFragment : Fragment() {
+ private val deviceController: ChipDeviceController
+ get() = ChipClient.getDeviceController(requireContext())
+
+ private lateinit var scope: CoroutineScope
+
+ private lateinit var addressUpdateFragment: AddressUpdateFragment
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ scope = viewLifecycleOwner.lifecycleScope
+
+ return inflater.inflate(R.layout.unpair_device_fragment, container, false).apply {
+ addressUpdateFragment =
+ childFragmentManager.findFragmentById(R.id.addressUpdateFragment) as AddressUpdateFragment
+
+ unpairDeviceBtn.setOnClickListener { scope.launch { unpairDeviceClick() } }
+ }
+ }
+
+ inner class ChipUnpairDeviceCallback : UnpairDeviceCallback {
+ override fun onError(status: Int, remoteDeviceId: Long) {
+ Log.d(TAG, "onError : $remoteDeviceId, $status")
+ }
+
+ override fun onSuccess(remoteDeviceId: Long) {
+ Log.d(TAG, "onSuccess : $remoteDeviceId")
+ }
+ }
+
+ private fun unpairDeviceClick() {
+ deviceController.unpairDeviceCallback(addressUpdateFragment.deviceId, ChipUnpairDeviceCallback())
+ }
+
+
+ companion object {
+ private const val TAG = "UnpairDeviceFragment"
+ fun newInstance(): UnpairDeviceFragment = UnpairDeviceFragment()
+ }
+}
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 3098a324158270..cfc6b90be5853b 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
@@ -104,6 +104,14 @@
android:layout_marginTop="8dp"
android:text="@string/cluster_interaction_tool" />
+
+
diff --git a/examples/android/CHIPTool/app/src/main/res/layout/unpair_device_fragment.xml b/examples/android/CHIPTool/app/src/main/res/layout/unpair_device_fragment.xml
new file mode 100644
index 00000000000000..1494e749b4f360
--- /dev/null
+++ b/examples/android/CHIPTool/app/src/main/res/layout/unpair_device_fragment.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
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 1035876b489175..4bc4263821bbaa 100644
--- a/examples/android/CHIPTool/app/src/main/res/values/strings.xml
+++ b/examples/android/CHIPTool/app/src/main/res/values/strings.xml
@@ -174,4 +174,6 @@
Select a cluster
Endpoint:
Connect
+
+ Unpair
diff --git a/examples/chip-tool/commands/pairing/PairingCommand.cpp b/examples/chip-tool/commands/pairing/PairingCommand.cpp
index adc560a812fe11..9c185024ad2075 100644
--- a/examples/chip-tool/commands/pairing/PairingCommand.cpp
+++ b/examples/chip-tool/commands/pairing/PairingCommand.cpp
@@ -161,9 +161,8 @@ CHIP_ERROR PairingCommand::PairWithMdns(NodeId remoteId)
CHIP_ERROR PairingCommand::Unpair(NodeId remoteId)
{
- CHIP_ERROR err = CurrentCommissioner().UnpairDevice(remoteId);
- SetCommandExitStatus(err);
- return err;
+ mCurrentFabricRemover = Platform::MakeUnique(&CurrentCommissioner());
+ return mCurrentFabricRemover->RemoveCurrentFabric(remoteId, &mCurrentFabricRemoveCallback);
}
void PairingCommand::OnStatusUpdate(DevicePairingDelegate::Status status)
@@ -265,3 +264,20 @@ void PairingCommand::OnDiscoveredDevice(const chip::Dnssd::DiscoveredNodeData &
SetCommandExitStatus(err);
}
}
+
+void PairingCommand::OnCurrentFabricRemove(void * context, NodeId nodeId, CHIP_ERROR err)
+{
+ PairingCommand * command = reinterpret_cast(context);
+ VerifyOrReturn(command != nullptr, ChipLogError(chipTool, "OnCurrentFabricRemove: context is null"));
+
+ if (err == CHIP_NO_ERROR)
+ {
+ ChipLogProgress(chipTool, "Device unpair completed with success: " ChipLogFormatX64, ChipLogValueX64(nodeId));
+ }
+ else
+ {
+ ChipLogProgress(chipTool, "Device unpair Failure: " ChipLogFormatX64 " %s", ChipLogValueX64(nodeId), ErrorStr(err));
+ }
+
+ command->SetCommandExitStatus(err);
+}
diff --git a/examples/chip-tool/commands/pairing/PairingCommand.h b/examples/chip-tool/commands/pairing/PairingCommand.h
index 3edd1cfd9e3f4e..4919ec89de8ad1 100644
--- a/examples/chip-tool/commands/pairing/PairingCommand.h
+++ b/examples/chip-tool/commands/pairing/PairingCommand.h
@@ -20,6 +20,7 @@
#include "../common/CHIPCommand.h"
#include
+#include
#include
#include
@@ -55,7 +56,8 @@ class PairingCommand : public CHIPCommand,
chip::Dnssd::DiscoveryFilterType filterType = chip::Dnssd::DiscoveryFilterType::kNone) :
CHIPCommand(commandName, credIssuerCmds),
mPairingMode(mode), mNetworkType(networkType),
- mFilterType(filterType), mRemoteAddr{ IPAddress::Any, chip::Inet::InterfaceId::Null() }
+ mFilterType(filterType), mRemoteAddr{ IPAddress::Any, chip::Inet::InterfaceId::Null() },
+ mCurrentFabricRemoveCallback(OnCurrentFabricRemove, this)
{
AddArgument("node-id", 0, UINT64_MAX, &mNodeId);
@@ -184,4 +186,10 @@ class PairingCommand : public CHIPCommand,
char * mOnboardingPayload;
uint64_t mDiscoveryFilterCode;
char * mDiscoveryFilterInstanceName;
+
+ // For unpair
+ chip::Platform::UniquePtr mCurrentFabricRemover;
+ chip::Callback::Callback mCurrentFabricRemoveCallback;
+
+ static void OnCurrentFabricRemove(void * context, NodeId remoteNodeId, CHIP_ERROR status);
};
diff --git a/src/controller/BUILD.gn b/src/controller/BUILD.gn
index eabc7062030d42..cdb5726c338d53 100644
--- a/src/controller/BUILD.gn
+++ b/src/controller/BUILD.gn
@@ -46,6 +46,8 @@ static_library("controller") {
"CommissioningDelegate.cpp",
"CommissioningWindowOpener.cpp",
"CommissioningWindowOpener.h",
+ "CurrentFabricRemover.cpp",
+ "CurrentFabricRemover.h",
"DeviceDiscoveryDelegate.h",
"DevicePairingDelegate.h",
"EmptyDataModelHandler.cpp",
diff --git a/src/controller/CHIPDeviceController.cpp b/src/controller/CHIPDeviceController.cpp
index 201cb22a34270c..46200403e0fcc7 100644
--- a/src/controller/CHIPDeviceController.cpp
+++ b/src/controller/CHIPDeviceController.cpp
@@ -45,6 +45,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -894,8 +895,10 @@ CHIP_ERROR DeviceCommissioner::StopPairing(NodeId remoteDeviceId)
CHIP_ERROR DeviceCommissioner::UnpairDevice(NodeId remoteDeviceId)
{
- // TODO: Send unpairing message to the remote device.
- return CHIP_NO_ERROR;
+ MATTER_TRACE_EVENT_SCOPE("UnpairDevice", "DeviceCommissioner");
+ VerifyOrReturnError(mState == State::Initialized, CHIP_ERROR_INCORRECT_STATE);
+
+ return AutoCurrentFabricRemover::RemoveCurrentFabric(this, remoteDeviceId);
}
void DeviceCommissioner::RendezvousCleanup(CHIP_ERROR status)
diff --git a/src/controller/CurrentFabricRemover.cpp b/src/controller/CurrentFabricRemover.cpp
new file mode 100644
index 00000000000000..d5aefe977609a0
--- /dev/null
+++ b/src/controller/CurrentFabricRemover.cpp
@@ -0,0 +1,184 @@
+/*
+ *
+ * Copyright (c) 2021 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
+
+#include
+#include
+
+using namespace chip::app::Clusters;
+
+namespace chip {
+namespace Controller {
+
+CHIP_ERROR CurrentFabricRemover::RemoveCurrentFabric(NodeId remoteNodeId, Callback::Callback * callback)
+{
+ mRemoteNodeId = remoteNodeId;
+ mCurrentFabricRemoveCallback = callback;
+ mNextStep = Step::kReadCurrentFabricIndex;
+
+ return mController->GetConnectedDevice(remoteNodeId, &mOnDeviceConnectedCallback, &mOnDeviceConnectionFailureCallback);
+}
+
+CHIP_ERROR CurrentFabricRemover::ReadCurrentFabricIndex(Messaging::ExchangeManager & exchangeMgr, SessionHandle & sessionHandle)
+{
+ using TypeInfo = chip::app::Clusters::OperationalCredentials::Attributes::CurrentFabricIndex::TypeInfo;
+ OperationalCredentialsCluster cluster(exchangeMgr, sessionHandle, kRootEndpointId);
+
+ return cluster.ReadAttribute(this, OnSuccessReadCurrentFabricIndex, OnReadAttributeFailure);
+}
+
+CHIP_ERROR CurrentFabricRemover::SendRemoveFabricIndex(Messaging::ExchangeManager & exchangeMgr, SessionHandle & sessionHandle)
+{
+ if (mFabricIndex == kUndefinedFabricIndex)
+ {
+ return CHIP_ERROR_INVALID_FABRIC_INDEX;
+ }
+
+ OperationalCredentials::Commands::RemoveFabric::Type request;
+ request.fabricIndex = mFabricIndex;
+
+ OperationalCredentialsCluster cluster(exchangeMgr, sessionHandle, 0);
+
+ return cluster.InvokeCommand(request, this, OnSuccessRemoveFabric, OnCommandFailure);
+}
+
+void CurrentFabricRemover::OnDeviceConnectedFn(void * context, Messaging::ExchangeManager & exchangeMgr,
+ SessionHandle & sessionHandle)
+{
+ CHIP_ERROR err = CHIP_NO_ERROR;
+ auto * self = static_cast(context);
+ VerifyOrReturn(self != nullptr, ChipLogProgress(Controller, "Device connected callback with null context. Ignoring"));
+
+ switch (self->mNextStep)
+ {
+ case Step::kReadCurrentFabricIndex: {
+ err = self->ReadCurrentFabricIndex(exchangeMgr, sessionHandle);
+ break;
+ }
+ case Step::kSendRemoveFabric: {
+ err = self->SendRemoveFabricIndex(exchangeMgr, sessionHandle);
+ break;
+ }
+ default:
+ err = CHIP_ERROR_INCORRECT_STATE;
+ break;
+ }
+
+ if (err != CHIP_NO_ERROR)
+ {
+ ChipLogError(Controller, "Current Fabric Remover failure : %" CHIP_ERROR_FORMAT, err.Format());
+ FinishRemoveCurrentFabric(context, err);
+ }
+}
+
+void CurrentFabricRemover::OnDeviceConnectionFailureFn(void * context, const ScopedNodeId & peerId, CHIP_ERROR err)
+{
+ ChipLogProgress(Controller, "OnDeviceConnectionFailureFn: %" CHIP_ERROR_FORMAT, err.Format());
+
+ auto * self = static_cast(context);
+ VerifyOrReturn(self != nullptr, ChipLogProgress(Controller, "Device connected failure callback with null context. Ignoring"));
+
+ FinishRemoveCurrentFabric(context, err);
+}
+
+void CurrentFabricRemover::OnSuccessReadCurrentFabricIndex(void * context, FabricIndex fabricIndex)
+{
+ auto * self = static_cast(context);
+ VerifyOrReturn(self != nullptr,
+ ChipLogProgress(Controller, "Success Read Current Fabric index callback with null context. Ignoring"));
+ self->mFabricIndex = fabricIndex;
+ self->mNextStep = Step::kSendRemoveFabric;
+ CHIP_ERROR err = self->mController->GetConnectedDevice(self->mRemoteNodeId, &self->mOnDeviceConnectedCallback,
+ &self->mOnDeviceConnectionFailureCallback);
+ if (err != CHIP_NO_ERROR)
+ {
+ FinishRemoveCurrentFabric(context, err);
+ }
+}
+
+void CurrentFabricRemover::OnReadAttributeFailure(void * context, CHIP_ERROR err)
+{
+ ChipLogProgress(Controller, "OnReadAttributeFailure %" CHIP_ERROR_FORMAT, err.Format());
+
+ auto * self = static_cast(context);
+ VerifyOrReturn(self != nullptr, ChipLogProgress(Controller, "Read Attribute failure callback with null context. Ignoring"));
+
+ FinishRemoveCurrentFabric(context, err);
+}
+
+void CurrentFabricRemover::OnSuccessRemoveFabric(void * context,
+ const OperationalCredentials::Commands::NOCResponse::DecodableType & data)
+{
+ auto * self = static_cast(context);
+ VerifyOrReturn(self != nullptr,
+ ChipLogProgress(Controller, "Success Remove Fabric command callback with null context. Ignoring"));
+
+ FinishRemoveCurrentFabric(context, CHIP_NO_ERROR);
+}
+
+void CurrentFabricRemover::OnCommandFailure(void * context, CHIP_ERROR err)
+{
+ ChipLogProgress(Controller, "OnCommandFailure %" CHIP_ERROR_FORMAT, err.Format());
+
+ auto * self = static_cast(context);
+ VerifyOrReturn(self != nullptr, ChipLogProgress(Controller, "Send command failure callback with null context. Ignoring"));
+
+ FinishRemoveCurrentFabric(context, err);
+}
+
+void CurrentFabricRemover::FinishRemoveCurrentFabric(void * context, CHIP_ERROR err)
+{
+ ChipLogError(Controller, "Remove Current Fabric Result : %" CHIP_ERROR_FORMAT, err.Format());
+ auto * self = static_cast(context);
+ self->mNextStep = Step::kAcceptRemoveFabricStart;
+ if (self->mCurrentFabricRemoveCallback != nullptr)
+ {
+ self->mCurrentFabricRemoveCallback->mCall(self->mCurrentFabricRemoveCallback->mContext, self->mRemoteNodeId, err);
+ }
+}
+
+AutoCurrentFabricRemover::AutoCurrentFabricRemover(DeviceController * controller) :
+ CurrentFabricRemover(controller), mOnRemoveCurrentFabricCallback(OnRemoveCurrentFabric, this)
+{}
+
+CHIP_ERROR AutoCurrentFabricRemover::RemoveCurrentFabric(DeviceController * controller, NodeId remoteNodeId)
+{
+ // Not using Platform::New because we want to keep our constructor private.
+ auto * remover = new (std::nothrow) AutoCurrentFabricRemover(controller);
+ if (remover == nullptr)
+ {
+ return CHIP_ERROR_NO_MEMORY;
+ }
+
+ CHIP_ERROR err = remover->CurrentFabricRemover::RemoveCurrentFabric(remoteNodeId, &remover->mOnRemoveCurrentFabricCallback);
+ if (err != CHIP_NO_ERROR)
+ {
+ delete remover;
+ }
+ // Else will clean up when the callback is called.
+ return err;
+}
+
+void AutoCurrentFabricRemover::OnRemoveCurrentFabric(void * context, NodeId remoteNodeId, CHIP_ERROR status)
+{
+ auto * self = static_cast(context);
+ delete self;
+}
+} // namespace Controller
+} // namespace chip
diff --git a/src/controller/CurrentFabricRemover.h b/src/controller/CurrentFabricRemover.h
new file mode 100644
index 00000000000000..8acef3cc56930e
--- /dev/null
+++ b/src/controller/CurrentFabricRemover.h
@@ -0,0 +1,108 @@
+/*
+ *
+ * Copyright (c) 2021 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
+#include
+#include
+
+namespace chip {
+namespace Controller {
+
+typedef void (*OnCurrentFabricRemove)(void * context, NodeId remoteNodeId, CHIP_ERROR status);
+
+/**
+ * A helper class to remove fabric given some parameters.
+ */
+class CurrentFabricRemover
+{
+public:
+ CurrentFabricRemover(DeviceController * controller) :
+ mController(controller), mOnDeviceConnectedCallback(&OnDeviceConnectedFn, this),
+ mOnDeviceConnectionFailureCallback(&OnDeviceConnectionFailureFn, this)
+ {}
+
+ enum class Step : uint8_t
+ {
+ // Ready to start removing a fabric.
+ kAcceptRemoveFabricStart = 0,
+ // Need to get Current Fabric Index.
+ kReadCurrentFabricIndex,
+ // Need to send Remove Fabric Command.
+ kSendRemoveFabric,
+ };
+
+ /*
+ * @brief
+ * Try to look up the device attached to our controller with the given
+ * remote node id and ask it to remove Fabric.
+ * If function returns an error, callback will never be be executed. Otherwise, callback will always be executed.
+ *
+ * @param[in] remoteNodeId The remote device Id
+ * @param[in] callback The callback to call once the remote fabric is completed or not.
+ */
+ CHIP_ERROR RemoveCurrentFabric(NodeId remoteNodeId, Callback::Callback * callback);
+
+private:
+ DeviceController * mController;
+
+ chip::Callback::Callback mOnDeviceConnectedCallback;
+ chip::Callback::Callback mOnDeviceConnectionFailureCallback;
+ chip::Callback::Callback * mCurrentFabricRemoveCallback;
+
+ NodeId mRemoteNodeId;
+ FabricIndex mFabricIndex = kUndefinedFabricIndex;
+ Step mNextStep = Step::kAcceptRemoveFabricStart;
+
+ CHIP_ERROR ReadCurrentFabricIndex(Messaging::ExchangeManager & exchangeMgr, SessionHandle & sessionHandle);
+ CHIP_ERROR SendRemoveFabricIndex(Messaging::ExchangeManager & exchangeMgr, SessionHandle & sessionHandle);
+
+ static void OnDeviceConnectedFn(void * context, Messaging::ExchangeManager & exchangeMgr, SessionHandle & sessionHandle);
+ static void OnDeviceConnectionFailureFn(void * context, const ScopedNodeId & peerId, CHIP_ERROR error);
+
+ static void OnSuccessReadCurrentFabricIndex(void * context, FabricIndex fabricIndex);
+ static void OnReadAttributeFailure(void * context, CHIP_ERROR error);
+
+ static void
+ OnSuccessRemoveFabric(void * context,
+ const chip::app::Clusters::OperationalCredentials::Commands::NOCResponse::DecodableType & data);
+ static void OnCommandFailure(void * context, CHIP_ERROR error);
+
+ static void FinishRemoveCurrentFabric(void * context, CHIP_ERROR err);
+};
+
+/**
+ * A helper class that can be used by consumers that don't care about the callback from the
+ * remove fabric process and just want automatic cleanup of the CurrentFabricRemover when done
+ * with it.
+ */
+class AutoCurrentFabricRemover : private CurrentFabricRemover
+{
+public:
+ // Takes the same arguments as CurrentFabricRemover::RemoveCurrentFabric except without the callback.
+ static CHIP_ERROR RemoveCurrentFabric(DeviceController * controller, NodeId remoteNodeId);
+
+private:
+ AutoCurrentFabricRemover(DeviceController * controller);
+ static void OnRemoveCurrentFabric(void * context, NodeId remoteNodeId, CHIP_ERROR status);
+ chip::Callback::Callback mOnRemoveCurrentFabricCallback;
+};
+
+} // namespace Controller
+} // namespace chip
diff --git a/src/controller/java/AndroidCurrentFabricRemover.cpp b/src/controller/java/AndroidCurrentFabricRemover.cpp
new file mode 100644
index 00000000000000..1029177ae3db31
--- /dev/null
+++ b/src/controller/java/AndroidCurrentFabricRemover.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#include "AndroidCurrentFabricRemover.h"
+
+#include
+#include
+
+namespace chip {
+namespace Controller {
+
+AndroidCurrentFabricRemover::AndroidCurrentFabricRemover(DeviceController * controller, jobject jCallbackObject) :
+ CurrentFabricRemover(controller), mOnRemoveCurrentFabricCallback(OnRemoveCurrentFabric, this)
+{
+ JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
+ mJavaCallback = env->NewGlobalRef(jCallbackObject);
+
+ jclass callbackClass = env->GetObjectClass(jCallbackObject);
+
+ mOnSuccessMethod = env->GetMethodID(callbackClass, "onSuccess", "(J)V");
+ if (mOnSuccessMethod == nullptr)
+ {
+ ChipLogError(Controller, "Failed to access callback 'onSuccess' method");
+ env->ExceptionClear();
+ }
+
+ mOnErrorMethod = env->GetMethodID(callbackClass, "onError", "(IJ)V");
+ if (mOnErrorMethod == nullptr)
+ {
+ ChipLogError(Controller, "Failed to access callback 'onError' method");
+ env->ExceptionClear();
+ }
+}
+
+AndroidCurrentFabricRemover::~AndroidCurrentFabricRemover()
+{
+ ChipLogError(Controller, "Delete AndroidCurrentFabricRemover");
+ JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
+ env->DeleteGlobalRef(mJavaCallback);
+}
+
+CHIP_ERROR AndroidCurrentFabricRemover::RemoveCurrentFabric(DeviceController * controller, NodeId remoteNodeId, jobject jcallback)
+{
+ // Not using Platform::New because we want to keep our constructor private.
+ auto * remover = new AndroidCurrentFabricRemover(controller, jcallback);
+ if (remover == nullptr)
+ {
+ return CHIP_ERROR_NO_MEMORY;
+ }
+
+ CHIP_ERROR err = remover->CurrentFabricRemover::RemoveCurrentFabric(remoteNodeId, &remover->mOnRemoveCurrentFabricCallback);
+ if (err != CHIP_NO_ERROR)
+ {
+ delete remover;
+ }
+ // Else will clean up when the callback is called.
+ return err;
+}
+
+void AndroidCurrentFabricRemover::OnRemoveCurrentFabric(void * context, NodeId remoteNodeId, CHIP_ERROR err)
+{
+ auto * self = static_cast(context);
+
+ if (self->mJavaCallback != nullptr)
+ {
+ JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
+ if (err == CHIP_NO_ERROR)
+ {
+ if (self->mOnSuccessMethod != nullptr)
+ {
+ env->CallVoidMethod(self->mJavaCallback, self->mOnSuccessMethod, static_cast(remoteNodeId));
+ }
+ }
+ else
+ {
+ if (self->mOnErrorMethod != nullptr)
+ {
+ env->CallVoidMethod(self->mJavaCallback, self->mOnErrorMethod, static_cast(err.GetValue()),
+ static_cast(remoteNodeId));
+ }
+ }
+ }
+
+ delete self;
+}
+
+} // namespace Controller
+} // namespace chip
diff --git a/src/controller/java/AndroidCurrentFabricRemover.h b/src/controller/java/AndroidCurrentFabricRemover.h
new file mode 100644
index 00000000000000..a363411549a7f7
--- /dev/null
+++ b/src/controller/java/AndroidCurrentFabricRemover.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#pragma once
+
+#include
+#include
+
+namespace chip {
+namespace Controller {
+
+/**
+ * AndroidCurrentFabricRemover class is a helper class that automatic cleanup in C++ class memory on Android Platform. Invoke the
+ * java callback object after all the operations are complete.
+ */
+class AndroidCurrentFabricRemover : private CurrentFabricRemover
+{
+public:
+ static CHIP_ERROR RemoveCurrentFabric(DeviceController * controller, NodeId remoteNodeId, jobject jcallback);
+
+private:
+ AndroidCurrentFabricRemover(DeviceController * controller, jobject javaCallbackObject);
+ ~AndroidCurrentFabricRemover();
+
+ static void OnRemoveCurrentFabric(void * context, NodeId remoteNodeId, CHIP_ERROR status);
+ chip::Callback::Callback mOnRemoveCurrentFabricCallback;
+
+ jobject mJavaCallback;
+ jmethodID mOnSuccessMethod = nullptr;
+ jmethodID mOnErrorMethod = nullptr;
+};
+
+} // Namespace Controller
+} // namespace chip
diff --git a/src/controller/java/BUILD.gn b/src/controller/java/BUILD.gn
index d7c68afd518800..979f69122bf3ab 100644
--- a/src/controller/java/BUILD.gn
+++ b/src/controller/java/BUILD.gn
@@ -32,6 +32,8 @@ shared_library("jni") {
"AndroidClusterExceptions.h",
"AndroidCommissioningWindowOpener.cpp",
"AndroidCommissioningWindowOpener.h",
+ "AndroidCurrentFabricRemover.cpp",
+ "AndroidCurrentFabricRemover.h",
"AndroidDeviceControllerWrapper.cpp",
"AndroidDeviceControllerWrapper.h",
"AndroidOperationalCredentialsIssuer.cpp",
@@ -120,6 +122,7 @@ android_library("java") {
"src/chip/devicecontroller/ReportEventCallbackJni.java",
"src/chip/devicecontroller/ResubscriptionAttemptCallback.java",
"src/chip/devicecontroller/SubscriptionEstablishedCallback.java",
+ "src/chip/devicecontroller/UnpairDeviceCallback.java",
"src/chip/devicecontroller/model/AttributeState.java",
"src/chip/devicecontroller/model/ChipAttributePath.java",
"src/chip/devicecontroller/model/ChipEventPath.java",
diff --git a/src/controller/java/CHIPDeviceController-JNI.cpp b/src/controller/java/CHIPDeviceController-JNI.cpp
index 85c88c48eacf38..5716efe361ac0e 100644
--- a/src/controller/java/CHIPDeviceController-JNI.cpp
+++ b/src/controller/java/CHIPDeviceController-JNI.cpp
@@ -23,6 +23,7 @@
*/
#include "AndroidCallbacks.h"
#include "AndroidCommissioningWindowOpener.h"
+#include "AndroidCurrentFabricRemover.h"
#include "AndroidDeviceControllerWrapper.h"
#include
#include
@@ -686,6 +687,23 @@ JNI_METHOD(void, unpairDevice)(JNIEnv * env, jobject self, jlong handle, jlong d
}
}
+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(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;
diff --git a/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java b/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java
index 7004fac4e177a9..bc1b66838efcf1 100644
--- a/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java
+++ b/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java
@@ -238,6 +238,10 @@ public void unpairDevice(long deviceId) {
unpairDevice(deviceControllerPtr, deviceId);
}
+ public void unpairDeviceCallback(long deviceId, UnpairDeviceCallback callback) {
+ unpairDeviceCallback(deviceControllerPtr, deviceId, callback);
+ }
+
/**
* Returns a pointer to a device currently being commissioned. This should be used before the
* device is operationally available.
@@ -643,6 +647,9 @@ private native void commissionDevice(
private native void unpairDevice(long deviceControllerPtr, long deviceId);
+ private native void unpairDeviceCallback(
+ long deviceControllerPtr, long deviceId, UnpairDeviceCallback callback);
+
private native long getDeviceBeingCommissionedPointer(long deviceControllerPtr, long nodeId);
private native void getConnectedDevicePointer(
diff --git a/src/controller/java/src/chip/devicecontroller/UnpairDeviceCallback.java b/src/controller/java/src/chip/devicecontroller/UnpairDeviceCallback.java
new file mode 100644
index 00000000000000..7e533b64338841
--- /dev/null
+++ b/src/controller/java/src/chip/devicecontroller/UnpairDeviceCallback.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 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.
+ *
+ */
+package chip.devicecontroller;
+
+public interface UnpairDeviceCallback {
+ public void onError(int status, long remoteDeviceId);
+
+ public void onSuccess(long remoteDeviceId);
+}