Skip to content

Commit

Permalink
Pass network credentials to pairDevice() (#13325)
Browse files Browse the repository at this point in the history
  • Loading branch information
austinh0 authored Jan 6, 2022
1 parent 4112be5 commit 48d299f
Show file tree
Hide file tree
Showing 11 changed files with 346 additions and 163 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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)
}
}
}

Expand Down Expand Up @@ -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())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<BluetoothDevice> = 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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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)
}
}
Expand All @@ -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)
}
}
Expand Down Expand Up @@ -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)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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?,
Expand All @@ -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() }
}
Expand All @@ -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
Expand All @@ -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() {
Expand Down Expand Up @@ -181,63 +122,17 @@ class EnterNetworkFragment : Fragment() {
return
}

val cluster = createNetworkCommissioningCluster()

val operationalDataset = makeThreadOperationalDataset(
channelStr.toString().toInt(),
panIdStr.toString().toInt(16),
xpanIdStr.hexToByteArray(),
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(
Expand Down Expand Up @@ -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()
}
Expand All @@ -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
Expand Down
Loading

0 comments on commit 48d299f

Please sign in to comment.