diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/MultiAdminClientFragment.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/MultiAdminClientFragment.kt
index 240553514740ff..ff61638b9bac0b 100644
--- a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/MultiAdminClientFragment.kt
+++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/MultiAdminClientFragment.kt
@@ -9,16 +9,17 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import chip.devicecontroller.ChipClusters
import chip.devicecontroller.ChipDeviceController
+import chip.devicecontroller.OpenCommissioningCallback
import com.google.chip.chiptool.ChipClient
import com.google.chip.chiptool.GenericChipDeviceListener
import com.google.chip.chiptool.R
import kotlinx.android.synthetic.main.multi_admin_client_fragment.discriminatorEd
+import kotlinx.android.synthetic.main.multi_admin_client_fragment.timeoutEd
import kotlinx.android.synthetic.main.multi_admin_client_fragment.multiAdminClusterCommandStatus
import kotlinx.android.synthetic.main.multi_admin_client_fragment.setupPinCodeEd
import kotlinx.android.synthetic.main.multi_admin_client_fragment.view.basicCommissioningMethodBtn
import kotlinx.android.synthetic.main.multi_admin_client_fragment.view.enhancedCommissioningMethodBtn
import kotlinx.android.synthetic.main.multi_admin_client_fragment.view.revokeBtn
-import kotlinx.android.synthetic.main.on_off_client_fragment.*
import kotlinx.coroutines.*
class MultiAdminClientFragment : Fragment() {
@@ -53,8 +54,10 @@ class MultiAdminClientFragment : Fragment() {
// TODO: use the discriminator and setupPinCode that was used to commission the device
val testDiscriminator = "3840"
val testSetupPinCode = 20202021L
+ val testDuration = 180
discriminatorEd.setText(testDiscriminator)
setupPinCodeEd.setText(testSetupPinCode.toString())
+ timeoutEd.setText(testDuration.toString())
}
inner class ChipControllerCallback : GenericChipDeviceListener() {
@@ -78,23 +81,41 @@ class MultiAdminClientFragment : Fragment() {
}
private suspend fun sendBasicCommissioningCommandClick() {
- val testDuration = 100
- deviceController.openPairingWindow(
+ val testDuration = timeoutEd.text.toString().toInt()
+ deviceController.openPairingWindowCallback(
ChipClient.getConnectedDevicePointer(
requireContext(),
addressUpdateFragment.deviceId
- ), testDuration
+ ), testDuration,
+ object:OpenCommissioningCallback {
+ override fun onError(status: Int, deviceId: Long) {
+ showMessage("OpenBasicCommissioning Fail! \nDevice ID : $deviceId\nErrorCode : $status")
+ }
+
+ override fun onSuccess(deviceId: Long, manualPairingCode: String?, qrCode: String?) {
+ showMessage("OpenBasicCommissioning Success! \n Node ID: $deviceId")
+ }
+ }
)
}
private suspend fun sendEnhancedCommissioningCommandClick() {
- val testDuration = 100
+ val testDuration = timeoutEd.text.toString().toInt()
val testIteration = 1000
val devicePointer =
ChipClient.getConnectedDevicePointer(requireContext(), addressUpdateFragment.deviceId)
- deviceController.openPairingWindowWithPIN(
+ deviceController.openPairingWindowWithPINCallback(
devicePointer, testDuration, testIteration.toLong(),
- discriminatorEd.text.toString().toInt(), setupPinCodeEd.text.toString().toULong().toLong()
+ discriminatorEd.text.toString().toInt(), setupPinCodeEd.text.toString().toULong().toLong(),
+ object:OpenCommissioningCallback {
+ override fun onError(status: Int, deviceId: Long) {
+ showMessage("OpenCommissioning Fail! \nDevice ID : $deviceId\nErrorCode : $status")
+ }
+
+ override fun onSuccess(deviceId: Long, manualPairingCode: String?, qrCode: String?) {
+ showMessage("OpenCommissioning Success! \n Node ID: $deviceId\n\tManual : $manualPairingCode\n\tQRCode : $qrCode")
+ }
+ }
)
}
diff --git a/src/android/CHIPTool/app/src/main/res/layout/multi_admin_client_fragment.xml b/src/android/CHIPTool/app/src/main/res/layout/multi_admin_client_fragment.xml
index fb872bd9b8022f..95e5458f8164b0 100644
--- a/src/android/CHIPTool/app/src/main/res/layout/multi_admin_client_fragment.xml
+++ b/src/android/CHIPTool/app/src/main/res/layout/multi_admin_client_fragment.xml
@@ -14,6 +14,18 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
+
+
+ app:layout_constraintTop_toBottomOf="@id/timeoutEd"/>
Wildcard
Multi-admin cluster
+ Enter Commissioning timeout
Basic Commissioning Method
Enter Discriminator
Enter Setup PIN Code
diff --git a/src/controller/java/AndroidCommissioningWindowOpener.cpp b/src/controller/java/AndroidCommissioningWindowOpener.cpp
new file mode 100644
index 00000000000000..feae69f414967b
--- /dev/null
+++ b/src/controller/java/AndroidCommissioningWindowOpener.cpp
@@ -0,0 +1,178 @@
+/*
+ * 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 "AndroidCommissioningWindowOpener.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace chip::app::Clusters;
+using namespace chip::System::Clock;
+
+namespace chip {
+namespace Controller {
+
+AndroidCommissioningWindowOpener::AndroidCommissioningWindowOpener(DeviceController * controller, jobject jCallbackObject) :
+ CommissioningWindowOpener(controller), mOnOpenCommissioningWindowCallback(OnOpenCommissioningWindowResponse, this),
+ mOnOpenBasicCommissioningWindowCallback(OnOpenBasicCommissioningWindowResponse, this)
+{
+ JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
+ mJavaCallback = env->NewGlobalRef(jCallbackObject);
+
+ jclass callbackClass = env->GetObjectClass(jCallbackObject);
+
+ mOnSuccessMethod = env->GetMethodID(callbackClass, "onSuccess", "(JLjava/lang/String;Ljava/lang/String;)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();
+ }
+}
+
+AndroidCommissioningWindowOpener::~AndroidCommissioningWindowOpener()
+{
+ ChipLogError(Controller, "Delete AndroidCommissioningWindowOpener");
+ JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
+ env->DeleteGlobalRef(mJavaCallback);
+}
+
+CHIP_ERROR AndroidCommissioningWindowOpener::OpenBasicCommissioningWindow(DeviceController * controller, NodeId deviceId,
+ Seconds16 timeout, jobject jcallback)
+{
+ // Not using Platform::New because we want to keep our constructor private.
+ auto * opener = new AndroidCommissioningWindowOpener(controller, jcallback);
+ if (opener == nullptr)
+ {
+ return CHIP_ERROR_NO_MEMORY;
+ }
+
+ CHIP_ERROR err = opener->CommissioningWindowOpener::OpenBasicCommissioningWindow(
+ deviceId, timeout, &opener->mOnOpenBasicCommissioningWindowCallback);
+ if (err != CHIP_NO_ERROR)
+ {
+ delete opener;
+ }
+ // Else will clean up when the callback is called.
+ return err;
+}
+
+CHIP_ERROR AndroidCommissioningWindowOpener::OpenCommissioningWindow(DeviceController * controller, NodeId deviceId,
+ Seconds16 timeout, uint32_t iteration, uint16_t discriminator,
+ Optional setupPIN, Optional salt,
+ jobject jcallback, SetupPayload & payload,
+ bool readVIDPIDAttributes)
+{
+ // Not using Platform::New because we want to keep our constructor private.
+ auto * opener = new AndroidCommissioningWindowOpener(controller, jcallback);
+ if (opener == nullptr)
+ {
+ return CHIP_ERROR_NO_MEMORY;
+ }
+
+ CHIP_ERROR err = opener->CommissioningWindowOpener::OpenCommissioningWindow(
+ deviceId, timeout, iteration, discriminator, setupPIN, salt, &opener->mOnOpenCommissioningWindowCallback, payload,
+ readVIDPIDAttributes);
+ if (err != CHIP_NO_ERROR)
+ {
+ delete opener;
+ }
+ // Else will clean up when the callback is called.
+ return err;
+}
+
+void AndroidCommissioningWindowOpener::OnOpenCommissioningWindowResponse(void * context, NodeId deviceId, CHIP_ERROR status,
+ chip::SetupPayload payload)
+{
+ auto * self = static_cast(context);
+ JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
+
+ VerifyOrExit(self->mJavaCallback != nullptr, ChipLogError(Controller, "mJavaCallback is not allocated."));
+
+ if (status == CHIP_NO_ERROR)
+ {
+ std::string QRCode;
+ std::string manualPairingCode;
+
+ SuccessOrExit(ManualSetupPayloadGenerator(payload).payloadDecimalStringRepresentation(manualPairingCode));
+ SuccessOrExit(QRCodeSetupPayloadGenerator(payload).payloadBase38Representation(QRCode));
+
+ if (self->mOnSuccessMethod != nullptr)
+ {
+ UtfString jManualPairingCode(env, manualPairingCode.c_str());
+ UtfString jQRCode(env, QRCode.c_str());
+ env->CallVoidMethod(self->mJavaCallback, self->mOnSuccessMethod, static_cast(deviceId),
+ jManualPairingCode.jniValue(), jQRCode.jniValue());
+ }
+ }
+ else
+ {
+ if (self->mOnErrorMethod != nullptr)
+ {
+ env->CallVoidMethod(self->mJavaCallback, self->mOnErrorMethod, static_cast(status.GetValue()),
+ static_cast(deviceId));
+ }
+ }
+exit:
+ delete self;
+}
+
+void AndroidCommissioningWindowOpener::OnOpenBasicCommissioningWindowResponse(void * context, NodeId deviceId, CHIP_ERROR status)
+{
+ auto * self = static_cast(context);
+
+ if (self->mJavaCallback != nullptr)
+ {
+ JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
+ if (status == CHIP_NO_ERROR)
+ {
+ if (self->mOnSuccessMethod != nullptr)
+ {
+ UtfString jManualPairingCode(env, "");
+ UtfString jQRCode(env, "");
+ env->CallVoidMethod(self->mJavaCallback, self->mOnSuccessMethod, static_cast(deviceId),
+ jManualPairingCode.jniValue(), jQRCode.jniValue());
+ }
+ }
+ else
+ {
+ if (self->mOnErrorMethod != nullptr)
+ {
+ env->CallVoidMethod(self->mJavaCallback, self->mOnErrorMethod, static_cast(status.GetValue()),
+ static_cast(deviceId));
+ }
+ }
+ }
+
+ delete self;
+}
+
+} // namespace Controller
+} // namespace chip
diff --git a/src/controller/java/AndroidCommissioningWindowOpener.h b/src/controller/java/AndroidCommissioningWindowOpener.h
new file mode 100644
index 00000000000000..2d6a61f62d6743
--- /dev/null
+++ b/src/controller/java/AndroidCommissioningWindowOpener.h
@@ -0,0 +1,61 @@
+/*
+ * 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 {
+
+/**
+ * A helper class that can be used by consumers that don't care about the callback from the
+ * open-commissioning-window process and just want automatic cleanup of the CommissioningWindowOpener when done
+ * with it.
+ */
+class AndroidCommissioningWindowOpener : private CommissioningWindowOpener
+{
+public:
+ // Takes the same arguments as CommissioningWindowOpener::OpenBasicCommissioningWindow except without the
+ // callback.
+ static CHIP_ERROR OpenBasicCommissioningWindow(DeviceController * controller, NodeId deviceId, System::Clock::Seconds16 timeout,
+ jobject jcallback);
+ // Takes the same arguments as CommissioningWindowOpener::OpenCommissioningWindow except without the
+ // callback.
+ static CHIP_ERROR OpenCommissioningWindow(DeviceController * controller, NodeId deviceId, System::Clock::Seconds16 timeout,
+ uint32_t iteration, uint16_t discriminator, Optional setupPIN,
+ Optional salt, jobject jcallback, SetupPayload & payload,
+ bool readVIDPIDAttributes = false);
+
+private:
+ AndroidCommissioningWindowOpener(DeviceController * controller, jobject javaCallbackObject);
+ ~AndroidCommissioningWindowOpener();
+
+ static void OnOpenCommissioningWindowResponse(void * context, NodeId deviceId, CHIP_ERROR status, chip::SetupPayload payload);
+ static void OnOpenBasicCommissioningWindowResponse(void * context, NodeId deviceId, CHIP_ERROR status);
+
+ chip::Callback::Callback mOnOpenCommissioningWindowCallback;
+ chip::Callback::Callback mOnOpenBasicCommissioningWindowCallback;
+
+ 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 9300c848c32d6a..ed5a9fee167a16 100644
--- a/src/controller/java/BUILD.gn
+++ b/src/controller/java/BUILD.gn
@@ -26,6 +26,8 @@ shared_library("jni") {
"AndroidCallbacks.h",
"AndroidClusterExceptions.cpp",
"AndroidClusterExceptions.h",
+ "AndroidCommissioningWindowOpener.cpp",
+ "AndroidCommissioningWindowOpener.h",
"AndroidDeviceControllerWrapper.cpp",
"AndroidDeviceControllerWrapper.h",
"AndroidOperationalCredentialsIssuer.cpp",
@@ -89,6 +91,7 @@ android_library("java") {
"src/chip/devicecontroller/GetConnectedDeviceCallbackJni.java",
"src/chip/devicecontroller/NetworkCredentials.java",
"src/chip/devicecontroller/NetworkLocation.java",
+ "src/chip/devicecontroller/OpenCommissioningCallback.java",
"src/chip/devicecontroller/PaseVerifierParams.java",
"src/chip/devicecontroller/ReportCallback.java",
"src/chip/devicecontroller/ReportCallbackJni.java",
diff --git a/src/controller/java/CHIPDeviceController-JNI.cpp b/src/controller/java/CHIPDeviceController-JNI.cpp
index 0b387b562821df..34a7a117475ab3 100644
--- a/src/controller/java/CHIPDeviceController-JNI.cpp
+++ b/src/controller/java/CHIPDeviceController-JNI.cpp
@@ -22,6 +22,7 @@
*
*/
#include "AndroidCallbacks.h"
+#include "AndroidCommissioningWindowOpener.h"
#include "AndroidDeviceControllerWrapper.h"
#include
#include
@@ -678,6 +679,63 @@ JNI_METHOD(jboolean, openPairingWindowWithPIN)
return true;
}
+JNI_METHOD(jboolean, openPairingWindowCallback)
+(JNIEnv * env, jobject self, jlong handle, jlong devicePtr, jint duration, jobject jcallback)
+{
+ chip::DeviceLayer::StackLock lock;
+ CHIP_ERROR err = CHIP_NO_ERROR;
+
+ DeviceProxy * chipDevice = reinterpret_cast(devicePtr);
+ if (chipDevice == nullptr)
+ {
+ ChipLogProgress(Controller, "Could not cast device pointer to Device object");
+ return false;
+ }
+
+ AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle);
+
+ err = AndroidCommissioningWindowOpener::OpenBasicCommissioningWindow(wrapper->Controller(), chipDevice->GetDeviceId(),
+ System::Clock::Seconds16(duration), jcallback);
+
+ if (err != CHIP_NO_ERROR)
+ {
+ ChipLogError(Controller, "OpenPairingWindow failed: %" CHIP_ERROR_FORMAT, err.Format());
+ return false;
+ }
+
+ return true;
+}
+
+JNI_METHOD(jboolean, openPairingWindowWithPINCallback)
+(JNIEnv * env, jobject self, jlong handle, jlong devicePtr, jint duration, jlong iteration, jint discriminator, jlong setupPinCode,
+ jobject jcallback)
+{
+ chip::DeviceLayer::StackLock lock;
+ CHIP_ERROR err = CHIP_NO_ERROR;
+
+ DeviceProxy * chipDevice = reinterpret_cast(devicePtr);
+ if (chipDevice == nullptr)
+ {
+ ChipLogProgress(Controller, "Could not cast device pointer to Device object");
+ return false;
+ }
+
+ AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle);
+
+ chip::SetupPayload setupPayload;
+ err = AndroidCommissioningWindowOpener::OpenCommissioningWindow(
+ wrapper->Controller(), chipDevice->GetDeviceId(), System::Clock::Seconds16(duration), iteration, discriminator,
+ MakeOptional(static_cast(setupPinCode)), NullOptional, jcallback, setupPayload);
+
+ if (err != CHIP_NO_ERROR)
+ {
+ ChipLogError(Controller, "OpenPairingWindow failed: %" CHIP_ERROR_FORMAT, err.Format());
+ return false;
+ }
+
+ return true;
+}
+
JNI_METHOD(void, shutdownCommissioning)
(JNIEnv * env, jobject self, jlong handle)
{
diff --git a/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java b/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java
index a2da49e62f198d..9e89f1d531ad9e 100644
--- a/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java
+++ b/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java
@@ -314,6 +314,22 @@ public boolean openPairingWindowWithPIN(
deviceControllerPtr, devicePtr, duration, iteration, discriminator, setupPinCode);
}
+ public boolean openPairingWindowCallback(
+ long devicePtr, int duration, OpenCommissioningCallback callback) {
+ return openPairingWindowCallback(deviceControllerPtr, devicePtr, duration, callback);
+ }
+
+ public boolean openPairingWindowWithPINCallback(
+ long devicePtr,
+ int duration,
+ long iteration,
+ int discriminator,
+ long setupPinCode,
+ OpenCommissioningCallback callback) {
+ return openPairingWindowWithPINCallback(
+ deviceControllerPtr, devicePtr, duration, iteration, discriminator, setupPinCode, callback);
+ }
+
/* Shutdown all cluster attribute subscriptions for a given device */
public void shutdownSubscriptions(long devicePtr) {
shutdownSubscriptions(deviceControllerPtr, devicePtr);
@@ -462,6 +478,18 @@ private native boolean openPairingWindowWithPIN(
int discriminator,
long setupPinCode);
+ private native boolean openPairingWindowCallback(
+ long deviceControllerPtr, long devicePtr, int duration, OpenCommissioningCallback callback);
+
+ private native boolean openPairingWindowWithPINCallback(
+ long deviceControllerPtr,
+ long devicePtr,
+ int duration,
+ long iteration,
+ int discriminator,
+ long setupPinCode,
+ OpenCommissioningCallback callback);
+
private native byte[] getAttestationChallenge(long deviceControllerPtr, long devicePtr);
private native void shutdownSubscriptions(long deviceControllerPtr, long devicePtr);
diff --git a/src/controller/java/src/chip/devicecontroller/OpenCommissioningCallback.java b/src/controller/java/src/chip/devicecontroller/OpenCommissioningCallback.java
new file mode 100644
index 00000000000000..4b2b0a3bbd8d0a
--- /dev/null
+++ b/src/controller/java/src/chip/devicecontroller/OpenCommissioningCallback.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 OpenCommissioningCallback {
+ public void onError(int status, long deviceId);
+
+ public void onSuccess(long deviceId, String manualPairingCode, String qrCode);
+}