From e079364d2f18aae6c4718cd7074ea62fae3f0360 Mon Sep 17 00:00:00 2001 From: joonhaengHeo <85541460+joonhaengHeo@users.noreply.github.com> Date: Thu, 9 May 2024 16:19:44 +0900 Subject: [PATCH] [Android][ICD]Implement Android ICD sending using buffer (#33377) * Implement Android ICD Queue sending * Kotlin detect config --- .../clusterclient/AddressUpdateFragment.kt | 27 ++++- .../clusterclient/WildcardFragment.kt | 112 +++++++++++++++--- kotlin-detect-config.yaml | 1 + 3 files changed, 119 insertions(+), 21 deletions(-) diff --git a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/AddressUpdateFragment.kt b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/AddressUpdateFragment.kt index 1fa6fadcff9ed3..43eeb2b892c51c 100644 --- a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/AddressUpdateFragment.kt +++ b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/AddressUpdateFragment.kt @@ -21,7 +21,6 @@ import com.google.chip.chiptool.ChipClient import com.google.chip.chiptool.databinding.AddressUpdateFragmentBinding import com.google.chip.chiptool.util.DeviceIdUtil import kotlin.coroutines.resume -import kotlin.coroutines.resumeWithException import kotlin.coroutines.suspendCoroutine import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -55,6 +54,8 @@ class AddressUpdateFragment : ICDCheckInCallback, Fragment() { private val handler = Handler(Looper.getMainLooper()) + private var externalICDCheckInMessageCallback: ICDCheckInMessageCallback? = null + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -128,11 +129,12 @@ class AddressUpdateFragment : ICDCheckInCallback, Fragment() { } val cluster = ChipClusters.IcdManagementCluster(devicePtr, 0) - val duration = suspendCoroutine { cont -> + val retDuration = suspendCoroutine { cont -> cluster.stayActiveRequest( object : ChipClusters.IcdManagementCluster.StayActiveResponseCallback { override fun onError(error: Exception) { - cont.resumeWithException(error) + Log.d(TAG, "onError", error) + cont.resume(0L) } override fun onSuccess(promisedActiveDuration: Long) { @@ -143,12 +145,11 @@ class AddressUpdateFragment : ICDCheckInCallback, Fragment() { ) } isSendingStayActiveCommand = false - return duration + return retDuration } private fun updateUIForICDInteractionSwitch(isEnabled: Boolean): Boolean { - val isICD = - deviceController.icdClientInfo.firstOrNull { info -> info.peerNodeId == deviceId } != null + val isICD = isICDDevice() if (isEnabled && !isICD) { binding.icdInteractionSwitch.isChecked = false return false @@ -188,7 +189,13 @@ class AddressUpdateFragment : ICDCheckInCallback, Fragment() { return binding.deviceIdEd.text.toString().toULong(16) } + fun isICDDevice(): Boolean { + return deviceController.icdClientInfo.firstOrNull { info -> info.peerNodeId == deviceId } != + null + } + override fun notifyCheckInMessage(info: ICDClientInfo) { + externalICDCheckInMessageCallback?.notifyCheckInMessage() if (info.peerNodeId != icdDeviceId) { return } @@ -206,6 +213,14 @@ class AddressUpdateFragment : ICDCheckInCallback, Fragment() { } } + fun setNotifyCheckInMessageCallback(callback: ICDCheckInMessageCallback?) { + externalICDCheckInMessageCallback = callback + } + + interface ICDCheckInMessageCallback { + fun notifyCheckInMessage() + } + private fun turnOnActiveMode() { requireActivity().runOnUiThread { binding.icdProgressBar.max = (icdTotalRemainStayActiveTimeMs / 1000).toInt() diff --git a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/WildcardFragment.kt b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/WildcardFragment.kt index 2ffbec50c746a2..4324ba535d886a 100644 --- a/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/WildcardFragment.kt +++ b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/WildcardFragment.kt @@ -45,7 +45,7 @@ import matter.tlv.AnonymousTag import matter.tlv.TlvReader import matter.tlv.TlvWriter -class WildcardFragment : Fragment() { +class WildcardFragment : Fragment(), AddressUpdateFragment.ICDCheckInMessageCallback { private var _binding: WildcardFragmentBinding? = null private val binding get() = _binding!! @@ -63,6 +63,23 @@ class WildcardFragment : Fragment() { private val invokePath = ArrayList() private val subscribeIdList = ArrayList() + data class ReadICDConfig(val isFabricFiltered: Boolean, val eventMin: Long?) + + data class SubscribeICDConfig( + val minInterval: Int, + val maxInterval: Int, + val keepSubscriptions: Boolean, + val isFabricFiltered: Boolean, + val eventMin: Long? + ) + + data class WriteInvokeICDConfig(val timedRequestTimeoutMs: Int, val imTimeoutMs: Int) + + private var readICDConfig: ReadICDConfig? = null + private var subscribeICDConfig: SubscribeICDConfig? = null + private var writeICDConfig: WriteInvokeICDConfig? = null + private var invokeICDConfig: WriteInvokeICDConfig? = null + private val reportCallback = object : ReportCallback { override fun onError( @@ -173,6 +190,45 @@ class WildcardFragment : Fragment() { return binding.root } + override fun onResume() { + super.onResume() + addressUpdateFragment.setNotifyCheckInMessageCallback(this) + } + + override fun onPause() { + addressUpdateFragment.setNotifyCheckInMessageCallback(null) + super.onPause() + } + + override fun notifyCheckInMessage() { + Log.d(TAG, "notifyCheckInMessage") + if (attributePath.isNotEmpty() || eventPath.isNotEmpty()) { + if (binding.readRadioBtn.isChecked && readICDConfig != null) { + scope.launch { read(readICDConfig!!.isFabricFiltered, readICDConfig!!.eventMin) } + } else if (binding.subscribeRadioBtn.isChecked && subscribeICDConfig != null) { + scope.launch { + subscribe( + subscribeICDConfig!!.minInterval, + subscribeICDConfig!!.maxInterval, + subscribeICDConfig!!.keepSubscriptions, + subscribeICDConfig!!.isFabricFiltered, + subscribeICDConfig!!.eventMin + ) + } + } + } else if ( + binding.writeRadioBtn.isChecked && writePath.isNotEmpty() && writeICDConfig != null + ) { + scope.launch { write(writeICDConfig!!.timedRequestTimeoutMs, writeICDConfig!!.imTimeoutMs) } + } else if ( + binding.invokeRadioBtn.isChecked && invokePath.isNotEmpty() && invokeICDConfig != null + ) { + scope.launch { + invoke(invokeICDConfig!!.timedRequestTimeoutMs, invokeICDConfig!!.imTimeoutMs) + } + } + } + private fun setVisibilityEachView(radioBtnId: Int) { val readBtnOn = (radioBtnId == R.id.readRadioBtn) val subscribeBtnOn = (radioBtnId == R.id.subscribeRadioBtn) @@ -520,7 +576,12 @@ class WildcardFragment : Fragment() { if (eventPath.isNotEmpty() && eventMinEd.text.isNotBlank()) { eventMin = eventMinEd.text.toString().toULong().toLong() } - read(isFabricFilteredEd.selectedItem.toString().toBoolean(), eventMin) + if (addressUpdateFragment.isICDDevice()) { + readICDConfig = + ReadICDConfig(isFabricFilteredEd.selectedItem.toString().toBoolean(), eventMin) + } else { + read(isFabricFilteredEd.selectedItem.toString().toBoolean(), eventMin) + } requireActivity().runOnUiThread { dialog.dismiss() } } } @@ -537,18 +598,23 @@ class WildcardFragment : Fragment() { dialogView.findViewById(R.id.timedRequestTimeoutEd).text.toString() val imTimeout = dialogView.findViewById(R.id.imTimeoutEd).text.toString() scope.launch { - write( + val timedRequestTimeoutInt = if (timedRequestTimeoutMs.isEmpty()) { 0 } else { timedRequestTimeoutMs.toInt() - }, + } + val imTimeoutInt = if (imTimeout.isEmpty()) { 0 } else { imTimeout.toInt() } - ) + if (addressUpdateFragment.isICDDevice()) { + writeICDConfig = WriteInvokeICDConfig(timedRequestTimeoutInt, imTimeoutInt) + } else { + write(timedRequestTimeoutInt, imTimeoutInt) + } requireActivity().runOnUiThread { dialog.dismiss() } } } @@ -588,13 +654,24 @@ class WildcardFragment : Fragment() { if (eventPath.isNotEmpty() && eventMinEd.text.isNotBlank()) { eventMin = eventMinEd.text.toString().toULong().toLong() } - subscribe( - minIntervalEd.text.toString().toInt(), - maxIntervalEd.text.toString().toInt(), - keepSubscriptionsSp.selectedItem.toString().toBoolean(), - isFabricFilteredSp.selectedItem.toString().toBoolean(), - eventMin, - ) + if (addressUpdateFragment.isICDDevice()) { + subscribeICDConfig = + SubscribeICDConfig( + minIntervalEd.text.toString().toInt(), + maxIntervalEd.text.toString().toInt(), + keepSubscriptionsSp.selectedItem.toString().toBoolean(), + isFabricFilteredSp.selectedItem.toString().toBoolean(), + eventMin + ) + } else { + subscribe( + minIntervalEd.text.toString().toInt(), + maxIntervalEd.text.toString().toInt(), + keepSubscriptionsSp.selectedItem.toString().toBoolean(), + isFabricFilteredSp.selectedItem.toString().toBoolean(), + eventMin + ) + } } else { Log.e(TAG, "minInterval or maxInterval is empty!") } @@ -614,18 +691,23 @@ class WildcardFragment : Fragment() { dialogView.findViewById(R.id.timedRequestTimeoutEd).text.toString() val imTimeout = dialogView.findViewById(R.id.imTimeoutEd).text.toString() scope.launch { - invoke( + val timedRequestTimeoutInt = if (timedRequestTimeoutMs.isEmpty()) { 0 } else { timedRequestTimeoutMs.toInt() - }, + } + val imTimeoutInt = if (imTimeout.isEmpty()) { 0 } else { imTimeout.toInt() } - ) + if (addressUpdateFragment.isICDDevice()) { + invokeICDConfig = WriteInvokeICDConfig(timedRequestTimeoutInt, imTimeoutInt) + } else { + invoke(timedRequestTimeoutInt, imTimeoutInt) + } requireActivity().runOnUiThread { dialog.dismiss() } } } diff --git a/kotlin-detect-config.yaml b/kotlin-detect-config.yaml index f039b94ee0deae..8470d1687dbe14 100644 --- a/kotlin-detect-config.yaml +++ b/kotlin-detect-config.yaml @@ -343,6 +343,7 @@ complexity: - "**/src/controller/java/tests/matter/tlv/TlvReaderTest.kt" LargeClass: excludes: + - "**/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/WildcardFragment.kt" - "**/src/controller/java/generated/java/**/*" - "**/src/controller/java/tests/matter/tlv/TlvReadWriteTest.kt" - "**/src/controller/java/tests/matter/jsontlv/JsonToTlvToJsonTest.kt"