Skip to content

Commit

Permalink
Android OTA Provider code
Browse files Browse the repository at this point in the history
  • Loading branch information
joonhaengHeo committed Oct 4, 2023
1 parent 33052cc commit bac7a66
Show file tree
Hide file tree
Showing 15 changed files with 490 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class SelectActionFragment : Fragment() {
binding.provisionCustomFlowBtn.setOnClickListener { handleProvisionCustomFlowClicked() }
binding.wildcardBtn.setOnClickListener { handleWildcardClicked() }
binding.unpairDeviceBtn.setOnClickListener { handleUnpairDeviceClicked() }
binding.otaProviderBtn.setOnClickListener { handleOTAProviderClicked() }

return binding.root
}
Expand Down Expand Up @@ -216,6 +217,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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<androidx.fragment.app.FragmentContainerView
android:id="@+id/addressUpdateFragment"
android:name="com.google.chip.chiptool.clusterclient.AddressUpdateFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true" />

<GridLayout
android:id="@+id/buttonGrid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/addressUpdateFragment"
android:columnCount="3"
android:rowCount="2">
<TextView
android:id="@+id/announceOTAProviderBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="16dp"
android:layout_margin="8dp"
android:layout_gravity="center"
android:layout_columnWeight="1"
android:layout_column="0"
android:layout_row="0"
android:background="@android:color/darker_gray"
android:text="@string/ota_provider_announce_ota_provider_text"
android:textSize="16sp"/>

</GridLayout>
<TextView
android:id="@+id/commandStatusTv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/buttonGrid"
android:padding="16dp"
android:minLines="4"
android:singleLine="false"
android:textSize="20sp" />

<TextView
android:id="@+id/reportStatusTv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/commandStatusTv"
android:padding="16dp"
android:minLines="4"
android:singleLine="false"
android:textSize="20sp" />

</RelativeLayout>
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,14 @@
android:layout_marginTop="8dp"
android:text="@string/unpair_device_btn_text" />

<Button
android:id="@+id/otaProviderBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="@string/ota_provider_btn_text" />

</LinearLayout>

</ScrollView>
3 changes: 3 additions & 0 deletions examples/android/CHIPTool/app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -219,4 +219,7 @@
<string name="wifi_connect_btn_text">Connect</string>

<string name="unpair_device_btn_text">Unpair</string>

<string name="ota_provider_btn_text">OTA Provider</string>
<string name="ota_provider_announce_ota_provider_text">Announce OTAProvider</string>
</resources>
2 changes: 2 additions & 0 deletions scripts/build/builders/android.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ def AppGnArgs(self):
gn_args["chip_config_network_layer_ble"] = False
elif self == AndroidApp.VIRTUAL_DEVICE_APP:
gn_args["chip_config_network_layer_ble"] = True
elif self == AndroidApp.CHIP_TOOL:
gn_args["chip_build_controller_dynamic_server"] = True
return gn_args

def ExampleName(self):
Expand Down
24 changes: 24 additions & 0 deletions src/controller/java/AndroidDeviceControllerWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,30 @@ CHIP_ERROR AndroidDeviceControllerWrapper::UpdateAttestationTrustStoreBridge(job
return err;
}

CHIP_ERROR AndroidDeviceControllerWrapper::InitializeOTAProviderBridge(jobject otaProviderDelegate)
{
CHIP_ERROR err = CHIP_NO_ERROR;

OTAProviderDelegateBridge * otaProviderBridge = new OTAProviderDelegateBridge(otaProviderDelegate);
auto systemState = DeviceControllerFactory::GetInstance().GetSystemState();

VerifyOrExit(otaProviderBridge != nullptr, err = CHIP_ERROR_NO_MEMORY);

err = otaProviderBridge->Init(systemState->SystemLayer(), systemState->ExchangeMgr());

mOtaProviderBridge = otaProviderBridge;
exit:
if (err != CHIP_NO_ERROR)
{
if (otaProviderBridge != nullptr)
{
delete otaProviderBridge;
}
}

return err;
}

void AndroidDeviceControllerWrapper::OnStatusUpdate(chip::Controller::DevicePairingDelegate::Status status)
{
chip::DeviceLayer::StackUnlock unlock;
Expand Down
4 changes: 4 additions & 0 deletions src/controller/java/AndroidDeviceControllerWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#include "AndroidOperationalCredentialsIssuer.h"
#include "AttestationTrustStoreBridge.h"
#include "DeviceAttestationDelegateBridge.h"
#include "OTAProviderDelegateBridge.h"

/**
* This class contains all relevant information for the JNI view of CHIPDeviceController
Expand Down Expand Up @@ -189,6 +190,8 @@ class AndroidDeviceControllerWrapper : public chip::Controller::DevicePairingDel

CHIP_ERROR UpdateAttestationTrustStoreBridge(jobject attestationTrustStoreDelegate);

CHIP_ERROR InitializeOTAProviderBridge(jobject otaProviderDelegate);

private:
using ChipDeviceControllerPtr = std::unique_ptr<chip::Controller::DeviceCommissioner>;

Expand Down Expand Up @@ -230,6 +233,7 @@ class AndroidDeviceControllerWrapper : public chip::Controller::DevicePairingDel
DeviceAttestationDelegateBridge * mDeviceAttestationDelegateBridge = nullptr;
AttestationTrustStoreBridge * mAttestationTrustStoreBridge = nullptr;
chip::Credentials::DeviceAttestationVerifier * mDeviceAttestationVerifier = nullptr;
OTAProviderDelegateBridge *mOtaProviderBridge = nullptr;

AndroidDeviceControllerWrapper(ChipDeviceControllerPtr controller,
#ifdef JAVA_MATTER_CONTROLLER_TEST
Expand Down
7 changes: 6 additions & 1 deletion src/controller/java/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ shared_library("jni") {
]

deps = [
"${chip_root}/src/controller/data_model",
# "${chip_root}/src/controller/data_model",
"${chip_root}/src/credentials:default_attestation_verifier",
"${chip_root}/src/inet",
"${chip_root}/src/lib",
Expand Down Expand Up @@ -95,6 +95,10 @@ shared_library("jni") {
}

if (chip_build_controller_dynamic_server) {
sources += [
"OTAProviderDelegateBridge.cpp",
"OTAProviderDelegateBridge.h",
]
defines += [ "CHIP_DEVICE_CONFIG_DYNAMIC_SERVER" ]
}

Expand Down Expand Up @@ -407,6 +411,7 @@ android_library("java") {
"src/chip/devicecontroller/KeypairDelegate.java",
"src/chip/devicecontroller/NetworkCredentials.java",
"src/chip/devicecontroller/NetworkLocation.java",
"src/chip/devicecontroller/OTAProviderDelegate.java",
"src/chip/devicecontroller/OpenCommissioningCallback.java",
"src/chip/devicecontroller/OperationalKeyConfig.java",
"src/chip/devicecontroller/PaseVerifierParams.java",
Expand Down
25 changes: 25 additions & 0 deletions src/controller/java/CHIPDeviceController-JNI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,31 @@ JNI_METHOD(void, setAttestationTrustStoreDelegate)
}
}

JNI_METHOD(void, setOTAProviderDelegate)(JNIEnv * env, jobject self, jlong handle, jobject otaProviderDelegate)
{
chip::DeviceLayer::StackLock lock;
CHIP_ERROR err = CHIP_NO_ERROR;
AndroidDeviceControllerWrapper * wrapper = AndroidDeviceControllerWrapper::FromJNIHandle(handle);

VerifyOrReturn(wrapper != nullptr, ChipLogError(Controller, "wrapper is null"));

ChipLogProgress(Controller, "setOTAProviderDelegate() called");

if (otaProviderDelegate != nullptr)
{
jobject otaProviderDelegateRef = env->NewGlobalRef(otaProviderDelegate);
err = wrapper->InitializeOTAProviderBridge(otaProviderDelegateRef);
SuccessOrExit(err);
}

exit:
if (err != CHIP_NO_ERROR)
{
ChipLogError(Controller, "Failed to set OTA Provider delegate.");
JniReferences::GetInstance().ThrowError(env, sChipDeviceControllerExceptionCls, err);
}
}

JNI_METHOD(void, commissionDevice)
(JNIEnv * env, jobject self, jlong handle, jlong deviceId, jbyteArray csrNonce, jobject networkCredentials)
{
Expand Down
Loading

0 comments on commit bac7a66

Please sign in to comment.