From ec5ff8d7b857e35ecceaeb85cfabedb20166ac6e Mon Sep 17 00:00:00 2001 From: joonhaengHeo <85541460+joonhaengHeo@users.noreply.github.com> Date: Wed, 6 Dec 2023 00:30:15 +0900 Subject: [PATCH] [Android] Implement OTA Provider exception case (#30777) * Implement Android OTA Provider exception case * Restyled by google-java-format * Restyled by clang-format * Add TooManyFunctions exception --------- Co-authored-by: Restyled.io --- .../OtaProviderClientFragment.kt | 395 +++++++++++++++++- .../res/layout/add_access_control_dialog.xml | 17 +- .../layout/ota_provider_client_fragment.xml | 158 ++++++- .../write_default_otaproviders_dialog.xml | 60 +++ .../app/src/main/res/values/strings.xml | 15 + kotlin-detect-config.yaml | 1 + .../java/OTAProviderDelegateBridge.cpp | 69 ++- .../devicecontroller/OTAProviderDelegate.java | 53 ++- 8 files changed, 726 insertions(+), 42 deletions(-) create mode 100644 examples/android/CHIPTool/app/src/main/res/layout/write_default_otaproviders_dialog.xml diff --git a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/OtaProviderClientFragment.kt b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/OtaProviderClientFragment.kt index da7d6791372195..87735856fa4844 100644 --- a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/OtaProviderClientFragment.kt +++ b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/OtaProviderClientFragment.kt @@ -1,6 +1,7 @@ package com.google.chip.chiptool.clusterclient import android.app.Activity +import android.app.AlertDialog import android.content.Intent import android.net.Uri import android.os.Bundle @@ -9,22 +10,42 @@ import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.AdapterView +import android.widget.ArrayAdapter +import android.widget.Button +import android.widget.EditText +import android.widget.Spinner +import android.widget.Toast import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import chip.devicecontroller.ChipClusters import chip.devicecontroller.ChipClusters.DefaultClusterCallback import chip.devicecontroller.ChipDeviceController +import chip.devicecontroller.ClusterIDMapping import chip.devicecontroller.OTAProviderDelegate +import chip.devicecontroller.OTAProviderDelegate.QueryImageResponseStatusEnum +import chip.devicecontroller.ReportCallback +import chip.devicecontroller.WriteAttributesCallback +import chip.devicecontroller.cluster.structs.AccessControlClusterAccessControlEntryStruct +import chip.devicecontroller.cluster.structs.OtaSoftwareUpdateRequestorClusterProviderLocation +import chip.devicecontroller.model.AttributeWriteRequest +import chip.devicecontroller.model.ChipAttributePath +import chip.devicecontroller.model.ChipEventPath +import chip.devicecontroller.model.NodeState import com.google.chip.chiptool.ChipClient import com.google.chip.chiptool.GenericChipDeviceListener import com.google.chip.chiptool.R import com.google.chip.chiptool.databinding.OtaProviderClientFragmentBinding +import com.google.chip.chiptool.util.toAny import java.io.BufferedInputStream import java.io.IOException import java.io.InputStream import java.util.Optional import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch +import matter.tlv.AnonymousTag +import matter.tlv.TlvReader +import matter.tlv.TlvWriter class OtaProviderClientFragment : Fragment() { private val deviceController: ChipDeviceController @@ -45,6 +66,8 @@ class OtaProviderClientFragment : Fragment() { private val binding get() = _binding!! + private val attributeList = ClusterIDMapping.OtaSoftwareUpdateRequestor.Attribute.values() + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -59,16 +82,312 @@ class OtaProviderClientFragment : Fragment() { childFragmentManager.findFragmentById(R.id.addressUpdateFragment) as AddressUpdateFragment binding.selectFirmwareFileBtn.setOnClickListener { selectFirmwareFileBtnClick() } + binding.updateOTAStatusBtn.setOnClickListener { updateOTAStatusBtnClick() } binding.announceOTAProviderBtn.setOnClickListener { scope.launch { sendAnnounceOTAProviderBtnClick() } } + binding.writeAclBtn.setOnClickListener { scope.launch { sendAclBtnClick() } } + + binding.readAttributeBtn.setOnClickListener { scope.launch { readAttributeBtnClick() } } + + binding.writeAttributeBtn.setOnClickListener { scope.launch { writeAttributeBtnClick() } } + + setQueryImageSpinnerListener() + + val attributeNames = attributeList.map { it.name } + + binding.attributeSp.adapter = + ArrayAdapter(requireContext(), android.R.layout.simple_spinner_dropdown_item, attributeNames) + binding.vendorIdEd.setText(ChipClient.VENDOR_ID.toString()) + binding.delayActionTimeEd.setText("0") deviceController.startOTAProvider(otaProviderCallback) return binding.root } + private suspend fun sendAclBtnClick() { + val endpointId = 0 + val clusterId = ClusterIDMapping.AccessControl.ID + val attributeId = ClusterIDMapping.AccessControl.Attribute.Acl.id + + val attributePath = ChipAttributePath.newInstance(endpointId, clusterId, attributeId) + deviceController.readAttributePath( + object : ReportCallback { + override fun onError( + attributePath: ChipAttributePath?, + eventPath: ChipEventPath?, + e: Exception + ) { + Log.d(TAG, "onError : ", e) + showMessage("Error : $e") + } + + override fun onReport(nodeState: NodeState?) { + Log.d(TAG, "onResponse") + val tlv = + nodeState + ?.getEndpointState(endpointId) + ?.getClusterState(clusterId) + ?.getAttributeState(attributeId) + ?.tlv + requireActivity().runOnUiThread { showAddAccessControlDialog(tlv) } + } + }, + ChipClient.getConnectedDevicePointer(requireContext(), addressUpdateFragment.deviceId), + listOf(attributePath), + 0 + ) + } + + private fun showAddAccessControlDialog(tlv: ByteArray?) { + if (tlv == null) { + Log.d(TAG, "Access Control read fail") + showMessage("Access Control read fail") + return + } + + val dialogView = + requireActivity().layoutInflater.inflate(R.layout.add_access_control_dialog, null) + val groupIdEd = dialogView.findViewById(R.id.groupIdEd) + groupIdEd.visibility = View.GONE + val nodeIdEd = dialogView.findViewById(R.id.nodeIdEd) + nodeIdEd.visibility = View.VISIBLE + val accessControlEntrySp = dialogView.findViewById(R.id.accessControlEntrySp) + accessControlEntrySp.adapter = + ArrayAdapter( + requireContext(), + android.R.layout.simple_spinner_dropdown_item, + GroupSettingFragment.Companion.AccessControlEntry.values() + ) + + val dialog = AlertDialog.Builder(requireContext()).apply { setView(dialogView) }.create() + dialogView.findViewById