Skip to content

Commit 177d875

Browse files
[Android] Add initial batch command support (project-chip#32326)
1 parent ed8f37d commit 177d875

24 files changed

+1242
-3
lines changed

.github/workflows/java-tests.yaml

+11
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,17 @@ jobs:
132132
--tool-cluster "im" \
133133
--tool-args "onnetwork-long-im-invoke --nodeid 1 --setup-pin-code 20202021 --discriminator 3840 -t 1000" \
134134
--factoryreset \
135+
'
136+
- name: Run IM Batch Invoke Test
137+
run: |
138+
scripts/run_in_python_env.sh out/venv \
139+
'./scripts/tests/run_java_test.py \
140+
--app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app \
141+
--app-args "--discriminator 3840 --interface-id -1" \
142+
--tool-path out/linux-x64-java-matter-controller \
143+
--tool-cluster "im" \
144+
--tool-args "onnetwork-long-im-batch-invoke --nodeid 1 --setup-pin-code 20202021 --discriminator 3840 -t 1000" \
145+
--factoryreset \
135146
'
136147
- name: Run IM Read Test
137148
run: |

examples/java-matter-controller/BUILD.gn

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ kotlin_binary("java-matter-controller") {
5757
"java/src/com/matter/controller/commands/pairing/PairOnNetworkFabricCommand.kt",
5858
"java/src/com/matter/controller/commands/pairing/PairOnNetworkInstanceNameCommand.kt",
5959
"java/src/com/matter/controller/commands/pairing/PairOnNetworkLongCommand.kt",
60+
"java/src/com/matter/controller/commands/pairing/PairOnNetworkLongImExtendableInvokeCommand.kt",
6061
"java/src/com/matter/controller/commands/pairing/PairOnNetworkLongImInvokeCommand.kt",
6162
"java/src/com/matter/controller/commands/pairing/PairOnNetworkLongImReadCommand.kt",
6263
"java/src/com/matter/controller/commands/pairing/PairOnNetworkLongImSubscribeCommand.kt",

examples/java-matter-controller/java/src/com/matter/controller/Main.kt

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ private fun getImCommands(
6767
PairOnNetworkLongImSubscribeCommand(controller, credentialsIssuer),
6868
PairOnNetworkLongImWriteCommand(controller, credentialsIssuer),
6969
PairOnNetworkLongImInvokeCommand(controller, credentialsIssuer),
70+
PairOnNetworkLongImExtendableInvokeCommand(controller, credentialsIssuer),
7071
)
7172
}
7273

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
/*
2+
* Copyright (c) 2024 Project CHIP Authors
3+
* All rights reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
package com.matter.controller.commands.pairing
19+
20+
import chip.devicecontroller.ChipDeviceController
21+
import chip.devicecontroller.ExtendableInvokeCallback
22+
import chip.devicecontroller.GetConnectedDeviceCallbackJni.GetConnectedDeviceCallback
23+
import chip.devicecontroller.model.InvokeElement
24+
import chip.devicecontroller.model.InvokeResponseData
25+
import chip.devicecontroller.model.NoInvokeResponseData
26+
import chip.devicecontroller.model.Status
27+
import com.matter.controller.commands.common.CredentialsIssuer
28+
import java.util.logging.Level
29+
import java.util.logging.Logger
30+
import kotlin.UShort
31+
import matter.tlv.AnonymousTag
32+
import matter.tlv.ContextSpecificTag
33+
import matter.tlv.TlvWriter
34+
35+
class PairOnNetworkLongImExtendableInvokeCommand(
36+
controller: ChipDeviceController,
37+
credsIssue: CredentialsIssuer?
38+
) :
39+
PairingCommand(
40+
controller,
41+
"onnetwork-long-im-batch-invoke",
42+
credsIssue,
43+
PairingModeType.ON_NETWORK,
44+
PairingNetworkType.NONE,
45+
DiscoveryFilterType.LONG_DISCRIMINATOR
46+
) {
47+
private var devicePointer: Long = 0
48+
49+
private fun setDevicePointer(devicePointer: Long) {
50+
this.devicePointer = devicePointer
51+
}
52+
53+
private inner class InternalInvokeCallback : ExtendableInvokeCallback {
54+
private var responseCount = 0
55+
56+
override fun onError(e: Exception) {
57+
logger.log(Level.INFO, "Batch Invoke receive onError" + e.message)
58+
setFailure("invoke failure")
59+
}
60+
61+
override fun onResponse(invokeResponseData: InvokeResponseData) {
62+
logger.log(Level.INFO, "Batch Invoke receive OnResponse on $invokeResponseData")
63+
val clusterId = invokeResponseData.getClusterId().getId()
64+
val commandId = invokeResponseData.getCommandId().getId()
65+
val tlvData = invokeResponseData.getTlvByteArray()
66+
val jsonData = invokeResponseData.getJsonString()
67+
val status = invokeResponseData.getStatus()
68+
69+
if (clusterId == CLUSTER_ID_IDENTIFY && commandId == IDENTIFY_COMMAND) {
70+
if (tlvData != null || jsonData != null) {
71+
setFailure("invoke failure with problematic payload")
72+
}
73+
if (
74+
status != null && status.status != Status.Code.Success && status.clusterStatus.isPresent()
75+
) {
76+
setFailure("invoke failure with incorrect status")
77+
}
78+
}
79+
80+
if (clusterId == CLUSTER_ID_TEST && commandId == TEST_ADD_ARGUMENT_RSP_COMMAND) {
81+
if (tlvData == null || jsonData == null) {
82+
setFailure("invoke failure with problematic payload")
83+
}
84+
85+
if (!jsonData.equals("""{"0:UINT":2}""")) {
86+
setFailure("invoke failure with problematic json")
87+
}
88+
89+
if (status != null) {
90+
setFailure("invoke failure with incorrect status")
91+
}
92+
}
93+
responseCount++
94+
}
95+
96+
override fun onNoResponse(noInvokeResponseData: NoInvokeResponseData) {
97+
logger.log(Level.INFO, "Batch Invoke receive onNoResponse on $noInvokeResponseData")
98+
}
99+
100+
override fun onDone() {
101+
if (responseCount == TEST_COMMONDS_NUM) {
102+
setSuccess()
103+
} else {
104+
setFailure("invoke failure")
105+
}
106+
}
107+
}
108+
109+
private inner class InternalGetConnectedDeviceCallback : GetConnectedDeviceCallback {
110+
override fun onDeviceConnected(devicePointer: Long) {
111+
setDevicePointer(devicePointer)
112+
logger.log(Level.INFO, "onDeviceConnected")
113+
}
114+
115+
override fun onConnectionFailure(nodeId: Long, error: Exception) {
116+
logger.log(Level.INFO, "onConnectionFailure")
117+
}
118+
}
119+
120+
override fun runCommand() {
121+
val number: UShort = 1u
122+
val tlvWriter1 = TlvWriter()
123+
tlvWriter1.startStructure(AnonymousTag)
124+
tlvWriter1.put(ContextSpecificTag(0), number)
125+
tlvWriter1.endStructure()
126+
127+
val element1: InvokeElement =
128+
InvokeElement.newInstance(
129+
/* endpointId= */ 0,
130+
CLUSTER_ID_IDENTIFY,
131+
IDENTIFY_COMMAND,
132+
tlvWriter1.getEncoded(),
133+
null
134+
)
135+
136+
val tlvWriter2 = TlvWriter()
137+
tlvWriter2.startStructure(AnonymousTag)
138+
tlvWriter2.put(ContextSpecificTag(0), number)
139+
tlvWriter2.put(ContextSpecificTag(1), number)
140+
tlvWriter2.endStructure()
141+
142+
val element2: InvokeElement =
143+
InvokeElement.newInstance(
144+
/* endpointId= */ 1,
145+
CLUSTER_ID_TEST,
146+
TEST_ADD_ARGUMENT_COMMAND,
147+
tlvWriter2.getEncoded(),
148+
null
149+
)
150+
151+
val invokeList = listOf(element1, element2)
152+
currentCommissioner()
153+
.pairDeviceWithAddress(
154+
getNodeId(),
155+
getRemoteAddr().address.hostAddress,
156+
MATTER_PORT,
157+
getDiscriminator(),
158+
getSetupPINCode(),
159+
null
160+
)
161+
currentCommissioner().setCompletionListener(this)
162+
waitCompleteMs(getTimeoutMillis())
163+
currentCommissioner()
164+
.getConnectedDevicePointer(getNodeId(), InternalGetConnectedDeviceCallback())
165+
clear()
166+
currentCommissioner()
167+
.extendableInvoke(InternalInvokeCallback(), devicePointer, invokeList, 0, 0)
168+
waitCompleteMs(getTimeoutMillis())
169+
}
170+
171+
companion object {
172+
private val logger =
173+
Logger.getLogger(PairOnNetworkLongImExtendableInvokeCommand::class.java.name)
174+
175+
private const val MATTER_PORT = 5540
176+
private const val CLUSTER_ID_IDENTIFY = 0x0003L
177+
private const val IDENTIFY_COMMAND = 0L
178+
private const val CLUSTER_ID_TEST = 0xFFF1FC05L
179+
private const val TEST_ADD_ARGUMENT_COMMAND = 0X04L
180+
private const val TEST_ADD_ARGUMENT_RSP_COMMAND = 0X01L
181+
private const val TEST_COMMONDS_NUM = 2
182+
}
183+
}

kotlin-detect-config.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ style:
104104
- "**/examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairOnNetworkInstanceNameCommand.kt"
105105
- "**/examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairOnNetworkLongCommand.kt"
106106
- "**/examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairOnNetworkLongImInvokeCommand.kt"
107+
- "**/examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairOnNetworkLongImExtendableInvokeCommand.kt"
107108
- "**/examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairOnNetworkLongImWriteCommand.kt"
108109
- "**/examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairOnNetworkShortCommand.kt"
109110
- "**/examples/java-matter-controller/java/src/com/matter/controller/commands/pairing/PairOnNetworkVendorCommand.kt"

scripts/tests/java/im_test.py

+13
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ def TestCmdOnnetworkLongImInvoke(self, nodeid, setuppin, discriminator, timeout)
7171
DumpProgramOutputToQueue(self.thread_list, Fore.GREEN + "JAVA " + Style.RESET_ALL, java_process, self.queue)
7272
return java_process.wait()
7373

74+
def TestCmdOnnetworkLongImExtendableInvoke(self, nodeid, setuppin, discriminator, timeout):
75+
java_command = self.command + ['im', 'onnetwork-long-im-batch-invoke', nodeid, setuppin, discriminator, timeout]
76+
logging.info(f"Execute: {java_command}")
77+
java_process = subprocess.Popen(
78+
java_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
79+
DumpProgramOutputToQueue(self.thread_list, Fore.GREEN + "JAVA " + Style.RESET_ALL, java_process, self.queue)
80+
return java_process.wait()
81+
7482
def TestCmdOnnetworkLongImWrite(self, nodeid, setuppin, discriminator, timeout):
7583
java_command = self.command + ['im', 'onnetwork-long-im-write', nodeid, setuppin, discriminator, timeout]
7684
logging.info(f"Execute: {java_command}")
@@ -101,6 +109,11 @@ def RunTest(self):
101109
code = self.TestCmdOnnetworkLongImInvoke(self.nodeid, self.setup_pin_code, self.discriminator, self.timeout)
102110
if code != 0:
103111
raise Exception(f"Testing pairing onnetwork-long-im-invoke failed with error {code}")
112+
elif self.command_name == 'onnetwork-long-im-batch-invoke':
113+
logging.info("Testing pairing onnetwork-long-im-batch-invoke")
114+
code = self.TestCmdOnnetworkLongImExtendableInvoke(self.nodeid, self.setup_pin_code, self.discriminator, self.timeout)
115+
if code != 0:
116+
raise Exception(f"Testing pairing onnetwork-long-im-batch-invoke failed with error {code}")
104117
elif self.command_name == 'onnetwork-long-im-write':
105118
logging.info("Testing pairing onnetwork-long-im-write")
106119
code = self.TestCmdOnnetworkLongImWrite(self.nodeid, self.setup_pin_code, self.discriminator, self.timeout)

src/controller/java/AndroidCallbacks-JNI.cpp

+11
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,14 @@ JNI_METHOD(void, InvokeCallbackJni, deleteCallback)(JNIEnv * env, jobject self,
6969
{
7070
deleteInvokeCallback(env, self, callbackHandle);
7171
}
72+
73+
JNI_METHOD(jlong, ExtendableInvokeCallbackJni, newCallback)
74+
(JNIEnv * env, jobject self)
75+
{
76+
return newExtendableInvokeCallback(env, self);
77+
}
78+
79+
JNI_METHOD(void, ExtendableInvokeCallbackJni, deleteCallback)(JNIEnv * env, jobject self, jlong callbackHandle)
80+
{
81+
deleteExtendableInvokeCallback(env, self, callbackHandle);
82+
}

0 commit comments

Comments
 (0)