diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/CHIPToolActivity.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/CHIPToolActivity.kt index 1168686289d4f6..54f271f63fddf6 100644 --- a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/CHIPToolActivity.kt +++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/CHIPToolActivity.kt @@ -28,6 +28,7 @@ import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment +import chip.devicecontroller.NetworkCredentials import chip.setuppayload.SetupPayload import chip.setuppayload.SetupPayloadParser import chip.setuppayload.SetupPayloadParser.UnrecognizedQrCodeException @@ -40,6 +41,7 @@ import com.google.chip.chiptool.clusterclient.OnOffClientFragment import com.google.chip.chiptool.clusterclient.SensorClientFragment 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.setuppayloadscanner.BarcodeFragment import com.google.chip.chiptool.setuppayloadscanner.CHIPDeviceDetailsFragment @@ -48,14 +50,16 @@ import com.google.chip.chiptool.setuppayloadscanner.CHIPLedgerDetailsFragment import org.json.JSONObject class CHIPToolActivity : - AppCompatActivity(), - BarcodeFragment.Callback, - SelectActionFragment.Callback, - DeviceProvisioningFragment.Callback, - CHIPDeviceDetailsFragment.Callback, - CHIPLedgerDetailsFragment.Callback { + AppCompatActivity(), + BarcodeFragment.Callback, + SelectActionFragment.Callback, + DeviceProvisioningFragment.Callback, + EnterNetworkFragment.Callback, + CHIPDeviceDetailsFragment.Callback, + CHIPLedgerDetailsFragment.Callback { private var networkType: ProvisionNetworkType? = null + private var deviceInfo: CHIPDeviceInfo? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -87,10 +91,15 @@ class CHIPToolActivity : } override fun onCHIPDeviceInfoReceived(deviceInfo: CHIPDeviceInfo) { + this.deviceInfo = deviceInfo if (networkType == null) { showFragment(CHIPDeviceDetailsFragment.newInstance(deviceInfo)) } else { - showFragment(DeviceProvisioningFragment.newInstance(deviceInfo, networkType!!), false) + if (deviceInfo.ipAddress != null) { + showFragment(DeviceProvisioningFragment.newInstance(deviceInfo!!, null)) + } else { + showFragment(EnterNetworkFragment.newInstance(networkType!!), false) + } } } @@ -123,6 +132,10 @@ class CHIPToolActivity : showFragment(AddressCommissioningFragment.newInstance(), false) } + override fun onNetworkCredentialsEntered(networkCredentials: NetworkCredentials) { + showFragment(DeviceProvisioningFragment.newInstance(deviceInfo!!, networkCredentials)) + } + override fun handleClusterInteractionClicked() { showFragment(ClusterInteractionFragment.newInstance()) } diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/bluetooth/BluetoothManager.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/bluetooth/BluetoothManager.kt index a40e92f5ee4ffd..7d83ee731b7e2c 100644 --- a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/bluetooth/BluetoothManager.kt +++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/bluetooth/BluetoothManager.kt @@ -20,6 +20,7 @@ import java.util.UUID import kotlin.coroutines.resume import kotlinx.coroutines.CancellableContinuation import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.channels.ProducerScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.first @@ -60,7 +61,12 @@ class BluetoothManager : BleCallback { val device = result.device Log.i(TAG, "Bluetooth Device Scanned Addr: ${device.address}, Name ${device.name}") - offer(device) + val producerScope: ProducerScope = this@callbackFlow + if (producerScope.channel.isClosedForSend) { + Log.w(TAG, "Bluetooth device was scanned, but channel is already closed") + } else { + offer(device) + } } override fun onScanFailed(errorCode: Int) { diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/provisioning/AddressCommissioningFragment.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/provisioning/AddressCommissioningFragment.kt index dd7015fac13dd6..23232bd8b15fc1 100644 --- a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/provisioning/AddressCommissioningFragment.kt +++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/provisioning/AddressCommissioningFragment.kt @@ -6,7 +6,6 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment -import com.google.chip.chiptool.ChipClient import com.google.chip.chiptool.R import com.google.chip.chiptool.setuppayloadscanner.BarcodeFragment import com.google.chip.chiptool.setuppayloadscanner.CHIPDeviceInfo diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/provisioning/DeviceProvisioningFragment.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/provisioning/DeviceProvisioningFragment.kt index 5156ee8cd96586..f97647ce4ab18a 100644 --- a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/provisioning/DeviceProvisioningFragment.kt +++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/provisioning/DeviceProvisioningFragment.kt @@ -27,6 +27,7 @@ import android.view.ViewGroup import android.widget.Toast import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope +import chip.devicecontroller.NetworkCredentials import com.google.chip.chiptool.ChipClient import com.google.chip.chiptool.GenericChipDeviceListener import com.google.chip.chiptool.R @@ -45,10 +46,8 @@ class DeviceProvisioningFragment : Fragment() { private var gatt: BluetoothGatt? = null - private val networkType: ProvisionNetworkType - get() = requireNotNull( - ProvisionNetworkType.fromName(arguments?.getString(ARG_PROVISION_NETWORK_TYPE)) - ) + private val networkCredentials: NetworkCredentials? + get() = arguments?.getParcelable(ARG_NETWORK_CREDENTIALS) private lateinit var scope: CoroutineScope @@ -121,7 +120,7 @@ class DeviceProvisioningFragment : Fragment() { val deviceId = DeviceIdUtil.getNextAvailableId(requireContext()) val connId = bluetoothManager.connectionId - deviceController.pairDevice(gatt, connId, deviceId, deviceInfo.setupPinCode) + deviceController.pairDevice(gatt, connId, deviceId, deviceInfo.setupPinCode, networkCredentials) DeviceIdUtil.setNextAvailableId(requireContext(), deviceId + 1) } } @@ -145,20 +144,19 @@ class DeviceProvisioningFragment : Fragment() { Log.d(TAG, "Pairing status update: $status") } - override fun onPairingComplete(code: Int) { - Log.d(TAG, "onPairingComplete: $code") - - if (deviceInfo.ipAddress != null) { + override fun onCommissioningComplete(nodeId: Long, errorCode: Int) { + if (errorCode == STATUS_PAIRING_SUCCESS) { FragmentUtil.getHost(this@DeviceProvisioningFragment, Callback::class.java) ?.onCommissioningComplete(0) - return + } else { + showMessage(R.string.rendezvous_over_ble_pairing_failure_text) } + } - if (code == STATUS_PAIRING_SUCCESS) { - childFragmentManager.beginTransaction() - .add(R.id.fragment_container, EnterNetworkFragment.newInstance(networkType)) - .commit() - } else { + override fun onPairingComplete(code: Int) { + Log.d(TAG, "onPairingComplete: $code") + + if (code != STATUS_PAIRING_SUCCESS) { showMessage(R.string.rendezvous_over_ble_pairing_failure_text) } } @@ -189,17 +187,21 @@ class DeviceProvisioningFragment : Fragment() { companion object { private const val TAG = "DeviceProvisioningFragment" private const val ARG_DEVICE_INFO = "device_info" - private const val ARG_PROVISION_NETWORK_TYPE = "provision_network_type" + private const val ARG_NETWORK_CREDENTIALS = "network_credentials" private const val STATUS_PAIRING_SUCCESS = 0 + /** + * Return a new instance of [DeviceProvisioningFragment]. [networkCredentials] can be null for + * IP commissioning. + */ fun newInstance( deviceInfo: CHIPDeviceInfo, - networkType: ProvisionNetworkType + networkCredentials: NetworkCredentials?, ): DeviceProvisioningFragment { return DeviceProvisioningFragment().apply { arguments = Bundle(2).apply { putParcelable(ARG_DEVICE_INFO, deviceInfo) - putString(ARG_PROVISION_NETWORK_TYPE, networkType.name) + putParcelable(ARG_NETWORK_CREDENTIALS, networkCredentials) } } } diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/provisioning/EnterNetworkFragment.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/provisioning/EnterNetworkFragment.kt index 25179ab4922dea..df4e4c7d9e5919 100644 --- a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/provisioning/EnterNetworkFragment.kt +++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/provisioning/EnterNetworkFragment.kt @@ -18,16 +18,13 @@ 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 android.widget.Toast import androidx.fragment.app.Fragment -import chip.devicecontroller.ChipClusters.NetworkCommissioningCluster -import com.google.chip.chiptool.ChipClient +import chip.devicecontroller.NetworkCredentials import com.google.chip.chiptool.R -import com.google.chip.chiptool.util.DeviceIdUtil import com.google.chip.chiptool.util.FragmentUtil import kotlinx.android.synthetic.main.enter_thread_network_fragment.channelEd import kotlinx.android.synthetic.main.enter_thread_network_fragment.masterKeyEd @@ -46,6 +43,10 @@ class EnterNetworkFragment : Fragment() { ProvisionNetworkType.fromName(arguments?.getString(ARG_PROVISION_NETWORK_TYPE)) ) + interface Callback { + fun onNetworkCredentialsEntered(networkCredentials: NetworkCredentials) + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -56,10 +57,6 @@ class EnterNetworkFragment : Fragment() { ProvisionNetworkType.THREAD -> R.layout.enter_thread_network_fragment } - if (USE_HARDCODED_WIFI) { - saveHardcodedWifiNetwork() - } - return inflater.inflate(layoutRes, container, false).apply { saveNetworkBtn.setOnClickListener { onSaveNetworkClicked() } } @@ -73,10 +70,6 @@ class EnterNetworkFragment : Fragment() { } } - private fun saveHardcodedWifiNetwork() { - addAndEnableWifiNetwork(HARDCODED_WIFI_SSID, HARDCODED_WIFI_PASSWORD) - } - private fun saveWifiNetwork() { val ssid = ssidEd?.text val pwd = pwdEd?.text @@ -86,63 +79,11 @@ class EnterNetworkFragment : Fragment() { return } - addAndEnableWifiNetwork(ssid.toString(), pwd.toString()) - } - - private fun addAndEnableWifiNetwork(ssid: String, password: String) { - // Uses UTF-8 as default - val ssidBytes = ssid.toByteArray() - val pwdBytes = password.toByteArray() - - val cluster = createNetworkCommissioningCluster() - val connectNetworkCallback = object : - NetworkCommissioningCluster.ConnectNetworkResponseCallback { - override fun onSuccess(networkingStatus: Int, debugText: String, errorValue: Long) { - Log.v(TAG, "ConnectNetwork for $ssid succeeded, proceeding to OnOff") - - requireActivity().runOnUiThread { - Toast.makeText( - requireContext(), - R.string.rendezvous_over_ble_commissioning_success_text, - Toast.LENGTH_SHORT - ).show() - } - - FragmentUtil.getHost( - this@EnterNetworkFragment, - DeviceProvisioningFragment.Callback::class.java - )?.onCommissioningComplete(0) - } - - override fun onError(ex: Exception) { - Log.e(TAG, "ConnectNetwork for $ssid failed", ex) - // TODO: consolidate error codes - FragmentUtil.getHost( - this@EnterNetworkFragment, - DeviceProvisioningFragment.Callback::class.java - )?.onCommissioningComplete(-1) - } - } - - cluster.addOrUpdateWiFiNetwork(object : - NetworkCommissioningCluster.NetworkConfigResponseCallback { - override fun onSuccess(networkingStatus: Int, debugText: String) { - Log.v(TAG, "AddOrUpdateWiFiNetwork for $ssid succeeded") - cluster.connectNetwork( - connectNetworkCallback, - ssidBytes, - /* breadcrumb = */ 0L, - ) - } - - override fun onError(ex: Exception) { - Log.e(TAG, "AddOrUpdateWiFiNetwork for $ssid failed", ex) - FragmentUtil.getHost( - this@EnterNetworkFragment, - DeviceProvisioningFragment.Callback::class.java - )?.onCommissioningComplete(-1) - } - }, ssidBytes, pwdBytes, /* breadcrumb = */ 0L) + val networkCredentials = NetworkCredentials.forWifi( + NetworkCredentials.WifiCredentials(ssid.toString(), pwd.toString()) + ) + FragmentUtil.getHost(this, Callback::class.java) + ?.onNetworkCredentialsEntered(networkCredentials) } private fun saveThreadNetwork() { @@ -181,8 +122,6 @@ class EnterNetworkFragment : Fragment() { return } - val cluster = createNetworkCommissioningCluster() - val operationalDataset = makeThreadOperationalDataset( channelStr.toString().toInt(), panIdStr.toString().toInt(16), @@ -190,54 +129,10 @@ class EnterNetworkFragment : Fragment() { masterKeyStr.hexToByteArray() ) - val connectNetworkCallback = object : - NetworkCommissioningCluster.ConnectNetworkResponseCallback { - override fun onSuccess(networkingStatus: Int, debugText: String, errorValue: Long) { - Log.v(TAG, "ConnectNetwork for $panIdStr succeeded, proceeding to OnOff") - - requireActivity().runOnUiThread { - Toast.makeText( - requireContext(), - R.string.rendezvous_over_ble_commissioning_success_text, - Toast.LENGTH_SHORT - ).show() - } - - FragmentUtil.getHost( - this@EnterNetworkFragment, - DeviceProvisioningFragment.Callback::class.java - )?.onCommissioningComplete(0) - } - - override fun onError(ex: Exception) { - Log.e(TAG, "ConnectNetwork for $panIdStr failed", ex) - // TODO: consolidate error codes - FragmentUtil.getHost( - this@EnterNetworkFragment, - DeviceProvisioningFragment.Callback::class.java - )?.onCommissioningComplete(-1) - } - } - - cluster.addOrUpdateThreadNetwork(object : - NetworkCommissioningCluster.NetworkConfigResponseCallback { - override fun onSuccess(networkingStatus: Int, debugText: String) { - Log.v(TAG, "AddOrUpdateThreadNetwork for $panIdStr succeeded") - cluster.connectNetwork( - connectNetworkCallback, - xpanIdStr.hexToByteArray(), - /* breadcrumb = */ 0L - ) - } - - override fun onError(ex: Exception) { - Log.e(TAG, "AddOrUpdateThreadNetwork for $panIdStr failed", ex) - FragmentUtil.getHost( - this@EnterNetworkFragment, - DeviceProvisioningFragment.Callback::class.java - )?.onCommissioningComplete(-1) - } - }, operationalDataset, /* breadcrumb = */ 0L) + val networkCredentials = + NetworkCredentials.forThread(NetworkCredentials.ThreadCredentials(operationalDataset)) + FragmentUtil.getHost(this, Callback::class.java) + ?.onNetworkCredentialsEntered(networkCredentials) } private fun makeThreadOperationalDataset( @@ -271,12 +166,6 @@ class EnterNetworkFragment : Fragment() { return dataset } - private fun createNetworkCommissioningCluster(): NetworkCommissioningCluster { - val devicePtr = ChipClient.getDeviceController(requireContext()) - .getDeviceBeingCommissionedPointer(DeviceIdUtil.getLastDeviceId(requireContext())) - return NetworkCommissioningCluster(devicePtr, NETWORK_COMMISSIONING_CLUSTER_ENDPOINT) - } - private fun String.hexToByteArray(): ByteArray { return chunked(2).map { byteStr -> byteStr.toUByte(16).toByte() }.toByteArray() } @@ -286,11 +175,6 @@ class EnterNetworkFragment : Fragment() { private const val ARG_PROVISION_NETWORK_TYPE = "provision_network_type" private const val NETWORK_COMMISSIONING_CLUSTER_ENDPOINT = 0 - // TODO(#5035): remove hardcoded option when delayed commands work. - private const val USE_HARDCODED_WIFI = false - private const val HARDCODED_WIFI_SSID = "" - private const val HARDCODED_WIFI_PASSWORD = "" - private const val NUM_CHANNEL_BYTES = 3 private const val NUM_PANID_BYTES = 2 private const val NUM_XPANID_BYTES = 8 diff --git a/src/controller/java/AndroidDeviceControllerWrapper.cpp b/src/controller/java/AndroidDeviceControllerWrapper.cpp index 49acd3e06b92de..2d1352a533d59b 100644 --- a/src/controller/java/AndroidDeviceControllerWrapper.cpp +++ b/src/controller/java/AndroidDeviceControllerWrapper.cpp @@ -282,6 +282,76 @@ AndroidDeviceControllerWrapper * AndroidDeviceControllerWrapper::AllocateNew( return wrapper.release(); } +CHIP_ERROR AndroidDeviceControllerWrapper::ApplyNetworkCredentials(chip::Controller::CommissioningParameters & params, + jobject networkCredentials) +{ + chip::DeviceLayer::StackUnlock unlock; + CHIP_ERROR err = CHIP_NO_ERROR; + + // Retrieve WiFi or Thread credentials from the NetworkCredentials Java object, and set them in the commissioning params. + JNIEnv * env = chip::JniReferences::GetInstance().GetEnvForCurrentThread(); + jmethodID getWifiCredentials; + err = chip::JniReferences::GetInstance().FindMethod(env, networkCredentials, "getWifiCredentials", + "()Lchip/devicecontroller/NetworkCredentials$WifiCredentials;", + &getWifiCredentials); + VerifyOrReturnError(err == CHIP_NO_ERROR, err); + jobject wifiCredentialsJava = env->CallObjectMethod(networkCredentials, getWifiCredentials); + + jmethodID getThreadCredentials; + err = chip::JniReferences::GetInstance().FindMethod(env, networkCredentials, "getThreadCredentials", + "()Lchip/devicecontroller/NetworkCredentials$ThreadCredentials;", + &getThreadCredentials); + VerifyOrReturnError(err == CHIP_NO_ERROR, err); + jobject threadCredentialsJava = env->CallObjectMethod(networkCredentials, getThreadCredentials); + + if (wifiCredentialsJava != nullptr) + { + jmethodID getSsid; + jmethodID getPassword; + err = chip::JniReferences::GetInstance().FindMethod(env, wifiCredentialsJava, "getSsid", "()Ljava/lang/String;", &getSsid); + VerifyOrReturnError(err == CHIP_NO_ERROR, err); + err = chip::JniReferences::GetInstance().FindMethod(env, wifiCredentialsJava, "getPassword", "()Ljava/lang/String;", + &getPassword); + VerifyOrReturnError(err == CHIP_NO_ERROR, err); + ssidStr = static_cast(env->NewGlobalRef(env->CallObjectMethod(wifiCredentialsJava, getSsid))); + VerifyOrReturnError(ssidStr != nullptr && !env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN); + passwordStr = static_cast(env->NewGlobalRef(env->CallObjectMethod(wifiCredentialsJava, getPassword))); + VerifyOrReturnError(ssidStr != nullptr && !env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN); + + ssid = env->GetStringUTFChars(ssidStr, 0); + password = env->GetStringUTFChars(passwordStr, 0); + jsize ssidLength = env->GetStringUTFLength(ssidStr); + jsize passwordLength = env->GetStringUTFLength(passwordStr); + + params.SetWifiCredentials( + WifiCredentials(chip::ByteSpan(reinterpret_cast(ssid), static_cast(ssidLength)), + chip::ByteSpan(reinterpret_cast(password), static_cast(passwordLength)))); + } + else if (threadCredentialsJava != nullptr) + { + jmethodID getOperationalDataset; + err = chip::JniReferences::GetInstance().FindMethod(env, wifiCredentialsJava, "getOperationalDataset", "()[B", + &getOperationalDataset); + VerifyOrReturnError(err == CHIP_NO_ERROR, err); + operationalDatasetBytes = + static_cast(env->NewGlobalRef(env->CallObjectMethod(threadCredentialsJava, getOperationalDataset))); + VerifyOrReturnError(ssidStr != nullptr && !env->ExceptionCheck(), CHIP_JNI_ERROR_EXCEPTION_THROWN); + + operationalDataset = env->GetByteArrayElements(operationalDatasetBytes, nullptr); + jsize length = env->GetArrayLength(operationalDatasetBytes); + + params.SetThreadOperationalDataset( + chip::ByteSpan(reinterpret_cast(operationalDataset), static_cast(length))); + } + else + { + ChipLogError(Controller, "Both WiFi and Thread credentials were null in NetworkCredentials"); + return CHIP_ERROR_INCORRECT_STATE; + } + + return err; +} + void AndroidDeviceControllerWrapper::OnStatusUpdate(chip::Controller::DevicePairingDelegate::Status status) { chip::DeviceLayer::StackUnlock unlock; @@ -309,6 +379,22 @@ void AndroidDeviceControllerWrapper::OnCommissioningComplete(NodeId deviceId, CH &onCommissioningCompleteMethod); VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(Controller, "Error finding Java method: %" CHIP_ERROR_FORMAT, err.Format())); env->CallVoidMethod(mJavaObjectRef, onCommissioningCompleteMethod, static_cast(deviceId), error.AsInteger()); + + if (ssidStr != nullptr) + { + env->ReleaseStringUTFChars(ssidStr, ssid); + env->DeleteGlobalRef(ssidStr); + } + if (passwordStr != nullptr) + { + env->ReleaseStringUTFChars(passwordStr, password); + env->DeleteGlobalRef(passwordStr); + } + if (operationalDatasetBytes != nullptr) + { + env->ReleaseByteArrayElements(operationalDatasetBytes, operationalDataset, 0); + env->DeleteGlobalRef(operationalDatasetBytes); + } } CHIP_ERROR AndroidDeviceControllerWrapper::InitializeOperationalCredentialsIssuer() diff --git a/src/controller/java/AndroidDeviceControllerWrapper.h b/src/controller/java/AndroidDeviceControllerWrapper.h index 3ab37f7dd06ce5..02c26e1a1dcbe8 100644 --- a/src/controller/java/AndroidDeviceControllerWrapper.h +++ b/src/controller/java/AndroidDeviceControllerWrapper.h @@ -50,6 +50,11 @@ class AndroidDeviceControllerWrapper : public chip::Controller::DevicePairingDel void CallJavaMethod(const char * methodName, jint argument); CHIP_ERROR InitializeOperationalCredentialsIssuer(); + /** + * Convert network credentials from Java, and apply them to the commissioning parameters object. + */ + CHIP_ERROR ApplyNetworkCredentials(chip::Controller::CommissioningParameters & params, jobject networkCredentials); + // DevicePairingDelegate implementation void OnStatusUpdate(chip::Controller::DevicePairingDelegate::Status status) override; void OnPairingComplete(CHIP_ERROR error) override; @@ -114,6 +119,14 @@ class AndroidDeviceControllerWrapper : public chip::Controller::DevicePairingDel chip::FabricId mNextFabricId = 0; bool mNodeIdRequested = false; + // These fields allow us to release the string/byte array memory later. + jstring ssidStr; + jstring passwordStr; + const char * ssid; + const char * password; + jbyteArray operationalDatasetBytes; + jbyte * operationalDataset; + AndroidDeviceControllerWrapper(ChipDeviceControllerPtr controller) : mController(std::move(controller)) { chip::CalendarToChipEpochTime(2021, 06, 10, 0, 0, 0, mNow); diff --git a/src/controller/java/BUILD.gn b/src/controller/java/BUILD.gn index 911ebb12ad275c..697a49f1b433db 100644 --- a/src/controller/java/BUILD.gn +++ b/src/controller/java/BUILD.gn @@ -80,6 +80,7 @@ android_library("java") { "src/chip/devicecontroller/ChipDeviceController.java", "src/chip/devicecontroller/ChipDeviceControllerException.java", "src/chip/devicecontroller/GetConnectedDeviceCallbackJni.java", + "src/chip/devicecontroller/NetworkCredentials.java", "src/chip/devicecontroller/PaseVerifierParams.java", "zap-generated/chip/devicecontroller/ChipClusters.java", "zap-generated/chip/devicecontroller/ClusterInfoMapping.java", diff --git a/src/controller/java/CHIPDeviceController-JNI.cpp b/src/controller/java/CHIPDeviceController-JNI.cpp index f5bc93df478426..e5bbd179c1fb13 100644 --- a/src/controller/java/CHIPDeviceController-JNI.cpp +++ b/src/controller/java/CHIPDeviceController-JNI.cpp @@ -178,7 +178,8 @@ JNI_METHOD(jlong, newDeviceController)(JNIEnv * env, jobject self) } JNI_METHOD(void, pairDevice) -(JNIEnv * env, jobject self, jlong handle, jlong deviceId, jint connObj, jlong pinCode, jbyteArray csrNonce) +(JNIEnv * env, jobject self, jlong handle, jlong deviceId, jint connObj, jlong pinCode, jbyteArray csrNonce, + jobject networkCredentials) { chip::DeviceLayer::StackLock lock; CHIP_ERROR err = CHIP_NO_ERROR; @@ -192,7 +193,10 @@ JNI_METHOD(void, pairDevice) .SetConnectionObject(reinterpret_cast(connObj)) #endif .SetPeerAddress(Transport::PeerAddress::BLE()); + CommissioningParameters commissioningParams = CommissioningParameters(); + wrapper->ApplyNetworkCredentials(commissioningParams, networkCredentials); + if (csrNonce != nullptr) { JniByteArray jniCsrNonce(env, csrNonce); diff --git a/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java b/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java index 793048dd7b0cf3..f9491ece1994c4 100644 --- a/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java +++ b/src/controller/java/src/chip/devicecontroller/ChipDeviceController.java @@ -45,8 +45,13 @@ public void setCompletionListener(CompletionListener listener) { completionListener = listener; } - public void pairDevice(BluetoothGatt bleServer, int connId, long deviceId, long setupPincode) { - pairDevice(bleServer, connId, deviceId, setupPincode, null); + public void pairDevice( + BluetoothGatt bleServer, + int connId, + long deviceId, + long setupPincode, + NetworkCredentials networkCredentials) { + pairDevice(bleServer, connId, deviceId, setupPincode, null, networkCredentials); } /** @@ -64,7 +69,8 @@ public void pairDevice( int connId, long deviceId, long setupPincode, - @Nullable byte[] csrNonce) { + @Nullable byte[] csrNonce, + NetworkCredentials networkCredentials) { if (connectionId == 0) { connectionId = connId; @@ -76,7 +82,8 @@ public void pairDevice( Log.d(TAG, "Bluetooth connection added with ID: " + connectionId); Log.d(TAG, "Pairing device with ID: " + deviceId); - pairDevice(deviceControllerPtr, deviceId, connectionId, setupPincode, csrNonce); + pairDevice( + deviceControllerPtr, deviceId, connectionId, setupPincode, csrNonce, networkCredentials); } else { Log.e(TAG, "Bluetooth connection already in use."); completionListener.onError(new Exception("Bluetooth connection already in use.")); @@ -244,7 +251,8 @@ private native void pairDevice( long deviceId, int connectionId, long pinCode, - @Nullable byte[] csrNonce); + @Nullable byte[] csrNonce, + NetworkCredentials networkCredentials); private native void pairDeviceWithAddress( long deviceControllerPtr, diff --git a/src/controller/java/src/chip/devicecontroller/NetworkCredentials.java b/src/controller/java/src/chip/devicecontroller/NetworkCredentials.java new file mode 100644 index 00000000000000..6eeef7422c7cac --- /dev/null +++ b/src/controller/java/src/chip/devicecontroller/NetworkCredentials.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2020-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; + +import android.os.Parcel; +import android.os.Parcelable; +import androidx.annotation.Nullable; + +/** Class for holding WiFi or Thread credentials, but not both. */ +public class NetworkCredentials implements Parcelable { + @Nullable private WifiCredentials wifiCredentials; + @Nullable private ThreadCredentials threadCredentials; + + private NetworkCredentials( + @Nullable WifiCredentials wifiCredentials, @Nullable ThreadCredentials threadCredentials) { + this.wifiCredentials = wifiCredentials; + this.threadCredentials = threadCredentials; + } + + /** + * Return a NetworkCredentials object with the given WifiCredentials and null ThreadCredentials. + */ + public static NetworkCredentials forWifi(WifiCredentials wifiCredentials) { + return new NetworkCredentials(wifiCredentials, null); + } + + /** + * Return a NetworkCredentials object with the given ThreadCredentials and null WifiCredentials. + */ + public static NetworkCredentials forThread(ThreadCredentials threadCredentials) { + return new NetworkCredentials(null, threadCredentials); + } + + public WifiCredentials getWifiCredentials() { + return wifiCredentials; + } + + public ThreadCredentials getThreadCredentials() { + return threadCredentials; + } + + // Begin Parcelable implementation + + private NetworkCredentials(Parcel in) { + wifiCredentials = in.readParcelable(WifiCredentials.class.getClassLoader()); + threadCredentials = in.readParcelable(ThreadCredentials.class.getClassLoader()); + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeParcelable(wifiCredentials, 0); + out.writeParcelable(threadCredentials, 0); + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public NetworkCredentials createFromParcel(Parcel in) { + return new NetworkCredentials(in); + } + + public NetworkCredentials[] newArray(int size) { + return new NetworkCredentials[size]; + } + }; + + public static class WifiCredentials implements Parcelable { + private final String ssid; + private final String password; + + public WifiCredentials(String ssid, String password) { + this.ssid = ssid; + this.password = password; + } + + public String getSsid() { + return ssid; + } + + public String getPassword() { + return password; + } + + // Begin Parcelable implementation + + private WifiCredentials(Parcel in) { + ssid = in.readString(); + password = in.readString(); + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeString(ssid); + out.writeString(password); + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public WifiCredentials createFromParcel(Parcel in) { + return new WifiCredentials(in); + } + + public WifiCredentials[] newArray(int size) { + return new WifiCredentials[size]; + } + }; + } + + public static class ThreadCredentials implements Parcelable { + private final byte[] operationalDataset; + + public ThreadCredentials(byte[] operationalDataset) { + this.operationalDataset = operationalDataset; + } + + public byte[] getOperationalDataset() { + return operationalDataset; + } + + // Begin Parcelable implementation + + private ThreadCredentials(Parcel in) { + operationalDataset = new byte[in.readInt()]; + in.readByteArray(operationalDataset); + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeInt(operationalDataset.length); + out.writeByteArray(operationalDataset); + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public ThreadCredentials createFromParcel(Parcel in) { + return new ThreadCredentials(in); + } + + public ThreadCredentials[] newArray(int size) { + return new ThreadCredentials[size]; + } + }; + } +}