From e3b7658367a2281b0146dffb306cf89f14c7d7bf Mon Sep 17 00:00:00 2001 From: Austin Hsieh Date: Wed, 19 Jan 2022 13:26:08 -0800 Subject: [PATCH] Implement wildcard read/subscribe for Android --- .../light-switch-app.matter | 1 - .../google/chip/chiptool/CHIPToolActivity.kt | 5 + .../chip/chiptool/SelectActionFragment.kt | 17 +- .../clusterclient/WildcardFragment.kt | 125 + .../res/layout/select_action_fragment.xml | 9 + .../src/main/res/layout/wildcard_fragment.xml | 125 + .../app/src/main/res/values/strings.xml | 11 +- src/controller/java/AndroidCallbacks-JNI.cpp | 14 + src/controller/java/AndroidCallbacks.cpp | 170 +- src/controller/java/AndroidCallbacks.h | 40 + .../java/AndroidClusterExceptions.cpp | 19 +- .../java/AndroidClusterExceptions.h | 5 + src/controller/java/BUILD.gn | 7 + .../java/CHIPAttributeTLVValueDecoder.h | 30 + .../java/CHIPDeviceController-JNI.cpp | 189 + .../ChipDeviceController.java | 40 + .../chip/devicecontroller/ReportCallback.java | 28 + .../devicecontroller/ReportCallbackJni.java | 56 + .../SubscriptionEstablishedCallback.java | 22 + .../model/ChipAttributePath.java | 81 + .../devicecontroller/model/ChipPathId.java | 72 + .../CHIPAttributeTLVValueDecoder-src.zapt | 72 + .../java/templates/ChipClusters-java.zapt | 1 + .../java/templates/partials/decode_value.zapt | 20 +- src/controller/java/templates/templates.json | 5 + .../CHIPAttributeTLVValueDecoder.cpp | 13201 ++++++++++++++++ .../zap-generated/CHIPInvokeCallbacks.cpp | 99 +- .../java/zap-generated/CHIPReadCallbacks.cpp | 440 +- src/lib/support/JniReferences.cpp | 27 + src/lib/support/JniReferences.h | 5 + 30 files changed, 14757 insertions(+), 179 deletions(-) create mode 100644 src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/WildcardFragment.kt create mode 100644 src/android/CHIPTool/app/src/main/res/layout/wildcard_fragment.xml create mode 100644 src/controller/java/CHIPAttributeTLVValueDecoder.h create mode 100644 src/controller/java/src/chip/devicecontroller/ReportCallback.java create mode 100644 src/controller/java/src/chip/devicecontroller/ReportCallbackJni.java create mode 100644 src/controller/java/src/chip/devicecontroller/SubscriptionEstablishedCallback.java create mode 100644 src/controller/java/src/chip/devicecontroller/model/ChipAttributePath.java create mode 100644 src/controller/java/src/chip/devicecontroller/model/ChipPathId.java create mode 100644 src/controller/java/templates/CHIPAttributeTLVValueDecoder-src.zapt create mode 100644 src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp diff --git a/examples/light-switch-app/light-switch-common/light-switch-app.matter b/examples/light-switch-app/light-switch-common/light-switch-app.matter index 785d948a46b03a..e0410a398bb083 100644 --- a/examples/light-switch-app/light-switch-common/light-switch-app.matter +++ b/examples/light-switch-app/light-switch-common/light-switch-app.matter @@ -1524,4 +1524,3 @@ endpoint 1 { binding cluster OnOff; binding cluster Scenes; } - 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 6fe40c6ad35458..7f96d1ad292b89 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 @@ -39,6 +39,7 @@ import com.google.chip.chiptool.clusterclient.OpCredClientFragment import com.google.chip.chiptool.clusterclient.BasicClientFragment import com.google.chip.chiptool.clusterclient.OnOffClientFragment import com.google.chip.chiptool.clusterclient.SensorClientFragment +import com.google.chip.chiptool.clusterclient.WildcardFragment import com.google.chip.chiptool.provisioning.AddressCommissioningFragment import com.google.chip.chiptool.provisioning.DeviceProvisioningFragment import com.google.chip.chiptool.provisioning.EnterNetworkFragment @@ -140,6 +141,10 @@ class CHIPToolActivity : showFragment(ClusterInteractionFragment.newInstance()) } + override fun handleWildcardClicked() { + showFragment(WildcardFragment.newInstance()) + } + override fun handleOnOffClicked() { showFragment(OnOffClientFragment.newInstance()) } diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/SelectActionFragment.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/SelectActionFragment.kt index 72acb167303017..a0fd93bbe976ae 100644 --- a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/SelectActionFragment.kt +++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/SelectActionFragment.kt @@ -51,13 +51,14 @@ class SelectActionFragment : Fragment() { setOnClickListener { getCallback()?.onProvisionThreadCredentialsClicked() } } onOffClusterBtn.setOnClickListener { getCallback()?.handleOnOffClicked() } - sensorClustersBtn.setOnClickListener{ getCallback()?.handleSensorClicked() } - multiAdminClusterBtn.setOnClickListener{ getCallback()?.handleMultiAdminClicked() } - opCredClustersBtn.setOnClickListener{ getCallback()?.handleOpCredClicked() } - basicClusterBtn.setOnClickListener{ getCallback()?.handleBasicClicked() } + sensorClustersBtn.setOnClickListener { getCallback()?.handleSensorClicked() } + multiAdminClusterBtn.setOnClickListener { getCallback()?.handleMultiAdminClicked() } + opCredClustersBtn.setOnClickListener { getCallback()?.handleOpCredClicked() } + basicClusterBtn.setOnClickListener { getCallback()?.handleBasicClicked() } attestationTestBtn.setOnClickListener { getCallback()?.handleAttestationTestClicked() } clusterInteractionBtn.setOnClickListener { getCallback()?.handleClusterInteractionClicked() } - provisionCustomFlowBtn.setOnClickListener{ getCallback()?.handleCustomFlowClicked() } + provisionCustomFlowBtn.setOnClickListener{ getCallback()?.handleCustomFlowClicked() } + wildcardBtn.setOnClickListener { getCallback()?.handleWildcardClicked() } } } @@ -122,10 +123,12 @@ class SelectActionFragment : Fragment() { fun handleBasicClicked() /** Notifies listener of attestation command button clicked. */ fun handleAttestationTestClicked() - /** Notifies listener of a click to manually input the CHIP device address.. */ + /** Notifies listener of a click to manually input the CHIP device address. */ fun onShowDeviceAddressInput() - /** Notifies listener of cluster interaction button click.. */ + /** Notifies listener of cluster interaction button click. */ fun handleClusterInteractionClicked() + /** Notifies listener of wildcard button click. */ + fun handleWildcardClicked() /** Notifies listener of provision-custom-flow button click. */ fun handleCustomFlowClicked() } diff --git a/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/WildcardFragment.kt b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/WildcardFragment.kt new file mode 100644 index 00000000000000..2ad27d37ddf9e6 --- /dev/null +++ b/src/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/WildcardFragment.kt @@ -0,0 +1,125 @@ +package com.google.chip.chiptool.clusterclient + +import android.app.AlertDialog +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Button +import android.widget.EditText +import androidx.appcompat.widget.MenuItemHoverListener +import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope +import chip.devicecontroller.ChipDeviceController +import chip.devicecontroller.ReportCallback +import chip.devicecontroller.SubscriptionEstablishedCallback +import chip.devicecontroller.model.ChipAttributePath +import chip.devicecontroller.model.ChipPathId +import com.google.chip.chiptool.ChipClient +import com.google.chip.chiptool.R +import kotlinx.android.synthetic.main.wildcard_fragment.attributeIdEd +import kotlinx.android.synthetic.main.wildcard_fragment.clusterIdEd +import kotlinx.android.synthetic.main.wildcard_fragment.endpointIdEd +import kotlinx.android.synthetic.main.wildcard_fragment.outputTv +import kotlinx.android.synthetic.main.wildcard_fragment.view.readBtn +import kotlinx.android.synthetic.main.wildcard_fragment.view.subscribeBtn +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +class WildcardFragment : Fragment() { + private val deviceController: ChipDeviceController + get() = ChipClient.getDeviceController(requireContext()) + + private lateinit var scope: CoroutineScope + + private lateinit var addressUpdateFragment: AddressUpdateFragment + + private val reportCallback = object : ReportCallback { + override fun onError(attributePath: ChipAttributePath, ex: Exception) { + Log.i(TAG, "Report error for $attributePath: $ex") + } + + override fun onReport(values: Map) { + Log.i(TAG, "Received report with ${values.size} values") + val builder = StringBuilder() + values.forEach { builder.append("${it.key}: ${it.value}\n") } + val output = builder.toString() + + Log.i(TAG, output) + requireActivity().runOnUiThread { outputTv.text = output } + } + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View { + scope = viewLifecycleOwner.lifecycleScope + return inflater.inflate(R.layout.wildcard_fragment, container, false).apply { + subscribeBtn.setOnClickListener { scope.launch { showSubscribeDialog() } } + readBtn.setOnClickListener { scope.launch { read() } } + + addressUpdateFragment = + childFragmentManager.findFragmentById(R.id.addressUpdateFragment) as AddressUpdateFragment + } + } + + private suspend fun subscribe(minInterval: Int, maxInterval: Int) { + val subscriptionEstablishedCallback = + SubscriptionEstablishedCallback { Log.i(TAG, "Subscription to device established") } + + val endpointId = getChipPathIdForText(endpointIdEd.text.toString()) + val clusterId = getChipPathIdForText(clusterIdEd.text.toString()) + val attributeId = getChipPathIdForText(attributeIdEd.text.toString()) + val attributePath = ChipAttributePath.newInstance(endpointId, clusterId, attributeId) + + deviceController.subscribeToPath(subscriptionEstablishedCallback, + reportCallback, + ChipClient.getConnectedDevicePointer(requireContext(), + addressUpdateFragment.deviceId), + attributePath, + minInterval, + maxInterval) + } + + private suspend fun read() { + val endpointId = getChipPathIdForText(endpointIdEd.text.toString()) + val clusterId = getChipPathIdForText(clusterIdEd.text.toString()) + val attributeId = getChipPathIdForText(attributeIdEd.text.toString()) + val attributePath = ChipAttributePath.newInstance(endpointId, clusterId, attributeId) + + deviceController.readPath(reportCallback, + ChipClient.getConnectedDevicePointer(requireContext(), + addressUpdateFragment.deviceId), + attributePath) + } + + private fun showSubscribeDialog() { + val dialogView = requireActivity().layoutInflater.inflate(R.layout.subscribe_dialog, null) + val dialog = AlertDialog.Builder(requireContext()).apply { + setView(dialogView) + }.create() + + val minIntervalEd = dialogView.findViewById(R.id.minIntervalEd) + val maxIntervalEd = dialogView.findViewById(R.id.maxIntervalEd) + dialogView.findViewById