From 26db8c5e0b3f34dabac91e4a9ac86cb1ff4fc9fc Mon Sep 17 00:00:00 2001 From: Joonhaeng Heo Date: Wed, 4 Oct 2023 14:19:36 +0900 Subject: [PATCH] Android OTA Provider code --- .../com/google/chip/chiptool/ChipClient.kt | 2 +- .../chip/chiptool/SelectActionFragment.kt | 5 + .../OtaProviderClientFragment.kt | 163 ++++++++++++++++++ .../layout/ota_provider_client_fragment.xml | 56 ++++++ .../res/layout/select_action_fragment.xml | 7 + .../app/src/main/res/values/strings.xml | 3 + scripts/build/builders/android.py | 2 + .../java/AndroidDeviceControllerWrapper.cpp | 24 +++ .../java/AndroidDeviceControllerWrapper.h | 4 + src/controller/java/BUILD.gn | 7 +- .../java/CHIPDeviceController-JNI.cpp | 25 +++ .../java/OTAProviderDelegateBridge.cpp | 91 ++++++++++ .../java/OTAProviderDelegateBridge.h | 57 ++++++ .../ChipDeviceController.java | 10 ++ .../devicecontroller/OTAProviderDelegate.java | 35 ++++ 15 files changed, 489 insertions(+), 2 deletions(-) create mode 100644 examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/OtaProviderClientFragment.kt create mode 100644 examples/android/CHIPTool/app/src/main/res/layout/ota_provider_client_fragment.xml create mode 100644 src/controller/java/OTAProviderDelegateBridge.cpp create mode 100644 src/controller/java/OTAProviderDelegateBridge.h create mode 100644 src/controller/java/src/chip/devicecontroller/OTAProviderDelegate.java diff --git a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/ChipClient.kt b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/ChipClient.kt index d57f63a69a9bee..77c59d75d00c36 100644 --- a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/ChipClient.kt +++ b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/ChipClient.kt @@ -41,7 +41,7 @@ object ChipClient { private lateinit var chipDeviceController: ChipDeviceController private lateinit var androidPlatform: AndroidChipPlatform /* 0xFFF4 is a test vendor ID, replace with your assigned company ID */ - private const val VENDOR_ID = 0xFFF4 + const val VENDOR_ID = 0xFFF4 fun getDeviceController(context: Context): ChipDeviceController { getAndroidChipPlatform(context) diff --git a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/SelectActionFragment.kt b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/SelectActionFragment.kt index 571552b86f06b2..dc082d5dc5b73c 100644 --- a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/SelectActionFragment.kt +++ b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/SelectActionFragment.kt @@ -73,6 +73,7 @@ class SelectActionFragment : Fragment() { binding.wildcardBtn.setOnClickListener { handleWildcardClicked() } binding.unpairDeviceBtn.setOnClickListener { handleUnpairDeviceClicked() } binding.groupSettingBtn.setOnClickListener { handleGroupSettingClicked() } + binding.otaProviderBtn.setOnClickListener { handleOTAProviderClicked() } return binding.root } @@ -218,6 +219,10 @@ class SelectActionFragment : Fragment() { showFragment(UnpairDeviceFragment.newInstance()) } + private fun handleOTAProviderClicked() { + showFragment(OtaProviderClientFragment.newInstance(), false) + } + /** Notifies listener of provision-WiFi-credentials button click. */ private fun handleProvisionWiFiCredentialsClicked() { getCallback()?.SetNetworkType(ProvisionNetworkType.WIFI) 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 new file mode 100644 index 00000000000000..b120e361bc9898 --- /dev/null +++ b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/OtaProviderClientFragment.kt @@ -0,0 +1,163 @@ +package com.google.chip.chiptool.clusterclient + +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope +import chip.devicecontroller.ChipDeviceController +import chip.devicecontroller.ClusterIDMapping +import chip.devicecontroller.InvokeCallback +import chip.devicecontroller.OTAProviderDelegate +import chip.devicecontroller.model.InvokeElement +import chip.tlv.AnonymousTag +import chip.tlv.ContextSpecificTag +import chip.tlv.TlvWriter +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 kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +class OtaProviderClientFragment : Fragment() { + private val deviceController: ChipDeviceController + get() = ChipClient.getDeviceController(requireContext()) + + private lateinit var scope: CoroutineScope + + private lateinit var addressUpdateFragment: AddressUpdateFragment + + private var _binding: OtaProviderClientFragmentBinding? = null + private val binding + get() = _binding!! + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = OtaProviderClientFragmentBinding.inflate(inflater, container, false) + scope = viewLifecycleOwner.lifecycleScope + + deviceController.setCompletionListener(ChipControllerCallback()) + + addressUpdateFragment = + childFragmentManager.findFragmentById(R.id.addressUpdateFragment) as AddressUpdateFragment + + binding.announceOTAProviderBtn.setOnClickListener { scope.launch { sendAnnounceOTAProviderBtnClick() } } + + deviceController.setOTAProviderDelegate(object: OTAProviderDelegate { + override fun handleQueryImage() { + TODO("Not yet implemented") + } + + override fun handleApplyUpdateRequest() { + TODO("Not yet implemented") + } + + override fun handleNotifyUpdateApplied() { + TODO("Not yet implemented") + } + + override fun handleBDXTransferSessionBegin(nodeId: Long, fileDesignator: String?, offset: Int) { + TODO("Not yet implemented") + } + + override fun handleBDXTransferSessionEnd(nodeId: Long) { + TODO("Not yet implemented") + } + + override fun setOTAFilePath(path: String?) { + TODO("Not yet implemented") + } + + override fun selectOTACandidate(requestorVendorID: Int, requestorProductID: Int, requestorSoftwareVersion: Int): Boolean { + TODO("Not yet implemented") + } + + }) + return binding.root + } + + private suspend fun sendAnnounceOTAProviderBtnClick() { + val endpointId = 0 + val clusterId = ClusterIDMapping.OtaSoftwareUpdateRequestor.ID + val commandId = ClusterIDMapping.OtaSoftwareUpdateRequestor.Command.AnnounceOTAProvider + + val tlvWriter = TlvWriter().apply { + startStructure(AnonymousTag) + put(ContextSpecificTag(ClusterIDMapping.OtaSoftwareUpdateRequestor.AnnounceOTAProviderCommandField.ProviderNodeID.id), deviceController.controllerNodeId.toULong()) + put(ContextSpecificTag(ClusterIDMapping.OtaSoftwareUpdateRequestor.AnnounceOTAProviderCommandField.VendorID.id), ChipClient.VENDOR_ID.toUInt()) + put(ContextSpecificTag(ClusterIDMapping.OtaSoftwareUpdateRequestor.AnnounceOTAProviderCommandField.AnnouncementReason.id), 0U) + put(ContextSpecificTag(ClusterIDMapping.OtaSoftwareUpdateRequestor.AnnounceOTAProviderCommandField.Endpoint.id), 0U) + endStructure() + } + + val invokeElement = InvokeElement.newInstance(endpointId, clusterId, commandId.id, tlvWriter.getEncoded(), null) + + deviceController.invoke( + object : InvokeCallback { + override fun onError(ex: Exception?) { + showMessage("${commandId.name} command failure $ex") + Log.e(TAG, "${commandId.name} command failure", ex) + } + + override fun onResponse(invokeElement: InvokeElement?, successCode: Long) { + Log.d(TAG, "onResponse : $invokeElement, Code : $successCode") + showMessage("${commandId.name} command success") + } + }, + getConnectedDevicePointer(), + invokeElement, + 0, + 0 + ) + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + inner class ChipControllerCallback : GenericChipDeviceListener() { + override fun onConnectDeviceComplete() {} + + override fun onCommissioningComplete(nodeId: Long, errorCode: Int) { + Log.d(TAG, "onCommissioningComplete for nodeId $nodeId: $errorCode") + showMessage("Address update complete for nodeId $nodeId with code $errorCode") + } + + override fun onNotifyChipConnectionClosed() { + Log.d(TAG, "onNotifyChipConnectionClosed") + } + + override fun onCloseBleComplete() { + Log.d(TAG, "onCloseBleComplete") + } + + override fun onError(error: Throwable?) { + Log.d(TAG, "onError: $error") + } + } + + private suspend fun getConnectedDevicePointer(): Long { + return ChipClient.getConnectedDevicePointer(requireContext(), addressUpdateFragment.deviceId) + } + + private fun showMessage(msg: String) { + requireActivity().runOnUiThread { binding.commandStatusTv.text = msg } + } + + private fun showReportMessage(msg: String) { + requireActivity().runOnUiThread { binding.reportStatusTv.text = msg } + } + + companion object { + private const val TAG = "OtaProviderClientFragment" + + fun newInstance(): OtaProviderClientFragment = OtaProviderClientFragment() + } +} \ No newline at end of file diff --git a/examples/android/CHIPTool/app/src/main/res/layout/ota_provider_client_fragment.xml b/examples/android/CHIPTool/app/src/main/res/layout/ota_provider_client_fragment.xml new file mode 100644 index 00000000000000..10515161e611a0 --- /dev/null +++ b/examples/android/CHIPTool/app/src/main/res/layout/ota_provider_client_fragment.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + diff --git a/examples/android/CHIPTool/app/src/main/res/layout/select_action_fragment.xml b/examples/android/CHIPTool/app/src/main/res/layout/select_action_fragment.xml index bc79784b383b7a..2ec36b43128be4 100644 --- a/examples/android/CHIPTool/app/src/main/res/layout/select_action_fragment.xml +++ b/examples/android/CHIPTool/app/src/main/res/layout/select_action_fragment.xml @@ -120,6 +120,13 @@ android:layout_marginTop="8dp" android:text="@string/group_setting_btn_text" /> +