Skip to content

Commit 732d8ee

Browse files
committed
Merge branch 'bugfix/matter_controller' into 'master'
Bugfix: Added group cat id operate in access control list entries so that matter controller is able control end devices. See merge request app-frameworks/esp-rainmaker-android!83
2 parents 036881a + c84d494 commit 732d8ee

File tree

10 files changed

+198
-11
lines changed

10 files changed

+198
-11
lines changed

app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ android {
5555
applicationId "com.espressif.rainmaker"
5656
minSdkVersion 27
5757
targetSdkVersion 33
58-
versionCode 111
59-
versionName "3.2.1 - ${getGitHash()}"
58+
versionCode 112
59+
versionName "3.2.2 - ${getGitHash()}"
6060
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
6161

6262
buildConfigField "String", "GitHash", "\"${getGitHash()}\""

app/src/main/java/com/espressif/AppConstants.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,10 @@ class AppConstants {
441441
const val COMMAND_UPDATE_USER_NOC = 3L
442442
const val COMMAND_UPDATE_DEVICE_LIST = 4L
443443

444+
const val CAT_ID_PREFIX = "FFFFFFFD"
445+
446+
const val PRIVILEGE_ADMIN = 5
447+
const val PRIVILEGE_OPERATE = 3
444448

445449
enum class UpdateEventType {
446450
EVENT_DEVICE_ADDED,

app/src/main/java/com/espressif/EspApplication.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ public class EspApplication extends Application {
123123
private String deviceToken;
124124
private KeyStore keyStore = null;
125125

126-
public String mGroupId, mFabricId, mRootCa, mIpk;
126+
public String mGroupId, mFabricId, mRootCa, mIpk, groupCatIdOperate;
127127

128128
public enum AppState {
129129
NO_USER_LOGIN,
@@ -558,6 +558,7 @@ private void initChipController(String matterNodeId) {
558558
String fabricId = "";
559559
String ipk = "";
560560
String rootCa = "";
561+
String catIdOp = "";
561562

562563
if (!matterNodeId.equals(mNodeId)) {
563564
continue;
@@ -569,12 +570,13 @@ private void initChipController(String matterNodeId) {
569570
fabricId = g.getFabricDetails().getFabricId();
570571
rootCa = g.getFabricDetails().getRootCa();
571572
ipk = g.getFabricDetails().getIpk();
573+
catIdOp = g.getFabricDetails().getGroupCatIdOperate();
572574

573575
if (!chipClientMap.containsKey(matterNodeId)) {
574576
if (!TextUtils.isEmpty(fabricId) && !TextUtils.isEmpty(rootCa)
575577
&& !TextUtils.isEmpty(ipk) && !TextUtils.isEmpty(matterNodeId) && !TextUtils.isEmpty(matterNodeId)) {
576578
ChipClient chipClient = new ChipClient(this, g.getGroupId()
577-
, fabricId, rootCa, ipk);
579+
, fabricId, rootCa, ipk, catIdOp);
578580
chipClientMap.put(matterNodeId, chipClient);
579581
}
580582
}
@@ -611,11 +613,13 @@ private void initChipControllerForHomeGroup() {
611613
String fabricId = "";
612614
String ipk = "";
613615
String rootCa = "";
616+
String catIdOp = "";
614617

615618
if (g.getFabricDetails() != null) {
616619
fabricId = g.getFabricDetails().getFabricId();
617620
rootCa = g.getFabricDetails().getRootCa();
618621
ipk = g.getFabricDetails().getIpk();
622+
catIdOp = g.getFabricDetails().getGroupCatIdOperate();
619623

620624
if (TextUtils.isEmpty(matterNodeId)) {
621625
return;
@@ -625,7 +629,7 @@ private void initChipControllerForHomeGroup() {
625629
if (!TextUtils.isEmpty(fabricId) && !TextUtils.isEmpty(rootCa)
626630
&& !TextUtils.isEmpty(ipk) && !TextUtils.isEmpty(matterNodeId) && !TextUtils.isEmpty(matterNodeId)) {
627631
ChipClient chipClient = new ChipClient(this, g.getGroupId()
628-
, fabricId, rootCa, ipk);
632+
, fabricId, rootCa, ipk, catIdOp);
629633
Log.d(TAG, "In it chip controller for matterNodeId id : " + matterNodeId);
630634
chipClientMap.put(matterNodeId, chipClient);
631635
}

app/src/main/java/com/espressif/cloudapi/ApiManager.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4245,6 +4245,7 @@ public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response)
42454245
data.putString(AppConstants.KEY_FABRIC_ID, fabricId);
42464246
data.putString(AppConstants.KEY_ROOT_CA, fabricDetails.getRootCa());
42474247
data.putString(AppConstants.KEY_IPK, fabricDetails.getIpk());
4248+
data.putString(AppConstants.KEY_GROUP_CAT_ID_OPERATE, fabricDetails.getGroupCatIdOperate());
42484249
}
42494250
espApp.groupMap.put(groupId, fabricGroup);
42504251
listener.onSuccess(data);
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// Copyright 2024 Espressif Systems (Shanghai) PTE LTD
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.espressif.matter
16+
17+
import android.util.Log
18+
import chip.devicecontroller.ChipClusters
19+
import chip.devicecontroller.ChipStructs
20+
import chip.devicecontroller.ChipStructs.AccessControlClusterAccessControlEntryStruct
21+
import kotlinx.coroutines.GlobalScope
22+
import kotlinx.coroutines.future.future
23+
import java.util.concurrent.CompletableFuture
24+
import kotlin.coroutines.resume
25+
import kotlin.coroutines.resumeWithException
26+
import kotlin.coroutines.suspendCoroutine
27+
28+
class AccessControlClusterHelper constructor(private val chipClient: ChipClient) {
29+
30+
companion object {
31+
const val TAG = "AccessControlClusterHelper"
32+
}
33+
34+
suspend fun readAclAttribute(
35+
deviceId: Long,
36+
endpoint: Int
37+
): MutableList<ChipStructs.AccessControlClusterAccessControlEntryStruct>? {
38+
Log.d(TAG, "readAclAttribute : $deviceId")
39+
val connectedDevicePtr =
40+
try {
41+
chipClient.getConnectedDevicePointer(deviceId)
42+
} catch (e: IllegalStateException) {
43+
Log.e(TAG, "Can't get connectedDevicePointer.")
44+
return null
45+
}
46+
return suspendCoroutine { continuation ->
47+
getAccessControlClusterForDevice(connectedDevicePtr, endpoint)
48+
.readAclAttribute(
49+
object : ChipClusters.AccessControlCluster.AclAttributeCallback {
50+
51+
override fun onSuccess(valueList: MutableList<ChipStructs.AccessControlClusterAccessControlEntryStruct>?) {
52+
Log.d(TAG, "readAclAttribute success: [$valueList]")
53+
continuation.resume(valueList)
54+
}
55+
56+
override fun onError(ex: Exception) {
57+
Log.e(TAG, "readAclAttribute command failure")
58+
continuation.resumeWithException(ex)
59+
}
60+
})
61+
}
62+
}
63+
64+
fun readAclAttributeAsync(
65+
deviceId: Long,
66+
endpoint: Int
67+
): CompletableFuture<MutableList<ChipStructs.AccessControlClusterAccessControlEntryStruct>?> =
68+
GlobalScope.future { readAclAttribute(deviceId, endpoint) }
69+
70+
suspend fun writeAclAttribute(
71+
deviceId: Long,
72+
endpoint: Int,
73+
entries: ArrayList<AccessControlClusterAccessControlEntryStruct>
74+
) {
75+
Log.d(TAG, "writeAclAttribute : $deviceId")
76+
val connectedDevicePtr =
77+
try {
78+
chipClient.getConnectedDevicePointer(deviceId)
79+
} catch (e: IllegalStateException) {
80+
Log.e(TAG, "Can't get connectedDevicePointer.")
81+
return
82+
}
83+
84+
return suspendCoroutine { continuation ->
85+
getAccessControlClusterForDevice(connectedDevicePtr, endpoint)
86+
.writeAclAttribute(
87+
object : ChipClusters.DefaultClusterCallback {
88+
89+
override fun onSuccess() {
90+
Log.d(TAG, "writeAclAttribute success")
91+
continuation.resume(Unit)
92+
}
93+
94+
override fun onError(ex: Exception) {
95+
Log.e(TAG, "writeAclAttribute command failure")
96+
continuation.resumeWithException(ex)
97+
}
98+
}, entries
99+
)
100+
}
101+
}
102+
103+
fun writeAclAttributeAsync(
104+
deviceId: Long,
105+
endpoint: Int,
106+
level: ArrayList<ChipStructs.AccessControlClusterAccessControlEntryStruct>
107+
): CompletableFuture<Unit> =
108+
GlobalScope.future { writeAclAttribute(deviceId, endpoint, level) }
109+
110+
private fun getAccessControlClusterForDevice(
111+
devicePtr: Long,
112+
endpoint: Int
113+
): ChipClusters.AccessControlCluster {
114+
return ChipClusters.AccessControlCluster(devicePtr, endpoint)
115+
}
116+
}

app/src/main/java/com/espressif/matter/AppCommissioningService.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,12 @@ class AppCommissioningService : Service(), CommissioningService.Callback {
6464
espApp.mGroupId,
6565
espApp.mFabricId,
6666
espApp.mRootCa,
67-
espApp.mIpk
67+
espApp.mIpk,
68+
espApp.groupCatIdOperate
6869
)
6970
} else {
7071
Log.d(TAG, "Commissioning service created without RM parameters")
71-
chipClient = ChipClient(applicationContext, "", "", "", "")
72+
chipClient = ChipClient(applicationContext, "", "", "", "", "")
7273
}
7374
commissioningServiceDelegate = CommissioningService.Builder(this).setCallback(this).build()
7475
}

app/src/main/java/com/espressif/matter/ChipClient.kt

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import chip.tlv.AnonymousTag
2727
import chip.tlv.TlvWriter
2828
import com.espressif.AppConstants
2929
import com.espressif.cloudapi.ApiManager
30+
import com.espressif.ui.Utils
3031
import com.google.gson.JsonArray
3132
import com.google.gson.JsonObject
3233
import dagger.hilt.android.qualifiers.ApplicationContext
@@ -55,7 +56,8 @@ class ChipClient constructor(
5556
private val groupId: String,
5657
private val fabricId: String,
5758
private val rootCa: String,
58-
private val ipk: String
59+
private val ipk: String,
60+
private val groupCatIdOperate: String
5961
) {
6062

6163
companion object {
@@ -600,8 +602,54 @@ class ChipClient constructor(
600602
var description: String? =
601603
ApiManager.getInstance(context)
602604
.confirmMatterNode(body, groupId)
603-
Log.d(TAG, "Confirming matter node : $description")
604-
605+
Log.d(TAG, "Confirming matter node, response : $description")
606+
607+
var aclClusterHelper = AccessControlClusterHelper(this@ChipClient)
608+
var aclAttr: MutableList<ChipStructs.AccessControlClusterAccessControlEntryStruct>? =
609+
null
610+
611+
Log.d(TAG, "Reading ACL Attributes")
612+
aclAttr = aclClusterHelper.readAclAttributeAsync(
613+
deviceNodeId,
614+
AppConstants.ENDPOINT_0
615+
).get()
616+
Log.d(TAG, "ACL attributes : $aclAttr")
617+
618+
var entries: java.util.ArrayList<ChipStructs.AccessControlClusterAccessControlEntryStruct> =
619+
java.util.ArrayList<ChipStructs.AccessControlClusterAccessControlEntryStruct>()
620+
621+
val it = aclAttr?.listIterator()
622+
var fabricIndex = 0
623+
var authMode = 0
624+
if (it != null) {
625+
for (entry in it) {
626+
entries.add(entry)
627+
if (entry.privilege == AppConstants.PRIVILEGE_ADMIN) {
628+
fabricIndex = entry.fabricIndex
629+
authMode = entry.authMode
630+
}
631+
}
632+
}
633+
634+
var subjects: ArrayList<Any> = ArrayList<Any>()
635+
subjects.add(Utils.getCatId(groupCatIdOperate))
636+
637+
var entry =
638+
ChipStructs.AccessControlClusterAccessControlEntryStruct(
639+
AppConstants.PRIVILEGE_OPERATE,
640+
authMode, subjects,
641+
null,
642+
fabricIndex
643+
)
644+
645+
entries.add(entry)
646+
647+
aclClusterHelper.writeAclAttributeAsync(
648+
deviceNodeId,
649+
AppConstants.ENDPOINT_0,
650+
entries
651+
).get()
652+
605653
continuation.resume(Unit)
606654
}
607655
}

app/src/main/java/com/espressif/matter/ChipClientHelper.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ class ChipClientHelper constructor(private val espApp: EspApplication) {
5353
var fabricId = ""
5454
var ipk = ""
5555
var rootCa = ""
56+
var groupCatIdOperate = ""
5657
if (matterNodeId != mNodeId) {
5758
continue
5859
}
@@ -64,14 +65,15 @@ class ChipClientHelper constructor(private val espApp: EspApplication) {
6465
fabricId = g.fabricDetails.fabricId
6566
rootCa = g.fabricDetails.rootCa
6667
ipk = g.fabricDetails.ipk
68+
groupCatIdOperate = g.fabricDetails.groupCatIdOperate
6769
if (!espApp.chipClientMap.containsKey(matterNodeId)) {
6870
if (!TextUtils.isEmpty(fabricId) && !TextUtils.isEmpty(rootCa)
6971
&& !TextUtils.isEmpty(ipk) && !TextUtils.isEmpty(matterNodeId) && !TextUtils.isEmpty(
7072
matterNodeId
7173
)
7274
) {
7375
val chipClient = ChipClient(
74-
espApp, g.groupId, fabricId, rootCa, ipk
76+
espApp, g.groupId, fabricId, rootCa, ipk, groupCatIdOperate
7577
)
7678
espApp.chipClientMap.put(matterNodeId, chipClient)
7779
}

app/src/main/java/com/espressif/matter/GroupSelectionAdapter.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ class GroupSelectionAdapter(val context: Activity, val groups: MutableList<Group
6060
if (clickedGroup.fabricDetails != null) {
6161
espApp.mRootCa = clickedGroup.fabricDetails.rootCa
6262
espApp.mIpk = clickedGroup.fabricDetails.ipk
63+
espApp.groupCatIdOperate = clickedGroup.fabricDetails.groupCatIdOperate
6364
startCommissioningFlow()
6465
}
6566
} else {
@@ -76,6 +77,8 @@ class GroupSelectionAdapter(val context: Activity, val groups: MutableList<Group
7677
espApp.mFabricId = data.getString(AppConstants.KEY_FABRIC_ID, "")
7778
espApp.mRootCa = data.getString(AppConstants.KEY_ROOT_CA, "")
7879
espApp.mIpk = data.getString(AppConstants.KEY_IPK, "")
80+
espApp.groupCatIdOperate =
81+
data.getString(AppConstants.KEY_GROUP_CAT_ID_OPERATE, "")
7982
}
8083
startCommissioningFlow()
8184
}

app/src/main/java/com/espressif/ui/Utils.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
import org.json.JSONException;
4141
import org.json.JSONObject;
4242

43+
import java.math.BigDecimal;
44+
import java.math.BigInteger;
4345
import java.util.ArrayList;
4446
import java.util.Calendar;
4547
import java.util.Iterator;
@@ -500,4 +502,10 @@ public static void setDeviceIcon(ImageView ivDevice, String deviceType) {
500502
}
501503
}
502504
}
505+
506+
public static Object getCatId(String catIdOperate) {
507+
catIdOperate = AppConstants.CAT_ID_PREFIX + catIdOperate;
508+
BigDecimal catId = new BigDecimal(new BigInteger(catIdOperate, 16));
509+
return catId.longValue();
510+
}
503511
}

0 commit comments

Comments
 (0)