diff --git a/.github/workflows/java-tests.yaml b/.github/workflows/java-tests.yaml
index 504cb865a9cc0b..c7730f2c27b32d 100644
--- a/.github/workflows/java-tests.yaml
+++ b/.github/workflows/java-tests.yaml
@@ -104,6 +104,8 @@ jobs:
build \
"
- name: Run Discover Commissionables Test
+ # Generally completes in seconds
+ timeout-minutes: 2
run: |
scripts/run_in_python_env.sh out/venv \
'./scripts/tests/run_java_test.py \
@@ -115,6 +117,8 @@ jobs:
--factoryreset \
'
- name: Run Pairing Onnetwork Test
+ # Generally completes in seconds
+ timeout-minutes: 2
run: |
scripts/run_in_python_env.sh out/venv \
'./scripts/tests/run_java_test.py \
@@ -126,6 +130,8 @@ jobs:
--factoryreset \
'
- name: Run IM Invoke Test
+ # Generally completes in seconds
+ timeout-minutes: 2
run: |
scripts/run_in_python_env.sh out/venv \
'./scripts/tests/run_java_test.py \
@@ -137,6 +143,8 @@ jobs:
--factoryreset \
'
- name: Run IM Extendable Invoke Test
+ # Generally completes in seconds
+ timeout-minutes: 2
run: |
scripts/run_in_python_env.sh out/venv \
'./scripts/tests/run_java_test.py \
@@ -148,6 +156,8 @@ jobs:
--factoryreset \
'
- name: Run IM Read Test
+ # Generally completes in seconds
+ timeout-minutes: 2
run: |
scripts/run_in_python_env.sh out/venv \
'./scripts/tests/run_java_test.py \
@@ -159,6 +169,8 @@ jobs:
--factoryreset \
'
- name: Run IM Write Test
+ # Generally completes in seconds
+ timeout-minutes: 2
run: |
scripts/run_in_python_env.sh out/venv \
'./scripts/tests/run_java_test.py \
@@ -170,6 +182,8 @@ jobs:
--factoryreset \
'
- name: Run IM Subscribe Test
+ # Generally completes in seconds
+ timeout-minutes: 2
run: |
scripts/run_in_python_env.sh out/venv \
'./scripts/tests/run_java_test.py \
@@ -181,6 +195,8 @@ jobs:
--factoryreset \
'
- name: Run Pairing AlreadyDiscovered Test
+ # Generally completes in seconds
+ timeout-minutes: 2
run: |
scripts/run_in_python_env.sh out/venv \
'./scripts/tests/run_java_test.py \
@@ -191,19 +207,22 @@ jobs:
--tool-args "already-discovered --nodeid 1 --setup-pin-code 20202021 --address ::1 --port 5540 -t 1000" \
--factoryreset \
'
- # Disabled due to failure: https://github.com/project-chip/connectedhomeip/issues/27361
- # - name: Run Pairing Address-PaseOnly Test
- # run: |
- # scripts/run_in_python_env.sh out/venv \
- # './scripts/tests/run_java_test.py \
- # --app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app \
- # --app-args "--discriminator 3840 --interface-id -1" \
- # --tool-path out/linux-x64-java-matter-controller \
- # --tool-cluster "pairing" \
- # --tool-args "address-paseonly --nodeid 1 --setup-pin-code 20202021 --address ::1 --port 5540 -t 1000" \
- # --factoryreset \
- # '
+ - name: Run Pairing Address-PaseOnly Test
+ # Disabled due to failure: https://github.com/project-chip/connectedhomeip/issues/27361
+ if: false
+ run: |
+ scripts/run_in_python_env.sh out/venv \
+ './scripts/tests/run_java_test.py \
+ --app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app \
+ --app-args "--discriminator 3840 --interface-id -1" \
+ --tool-path out/linux-x64-java-matter-controller \
+ --tool-cluster "pairing" \
+ --tool-args "address-paseonly --nodeid 1 --setup-pin-code 20202021 --address ::1 --port 5540 -t 1000" \
+ --factoryreset \
+ '
- name: Run Pairing SetupQRCode Test
+ # Generally completes in seconds
+ timeout-minutes: 2
run: |
scripts/run_in_python_env.sh out/venv \
'./scripts/tests/run_java_test.py \
@@ -215,6 +234,8 @@ jobs:
--factoryreset \
'
- name: Run Pairing ManualCode Test
+ # Generally completes in seconds
+ timeout-minutes: 2
run: |
scripts/run_in_python_env.sh out/venv \
'./scripts/tests/run_java_test.py \
@@ -226,6 +247,8 @@ jobs:
--factoryreset \
'
- name: Run Pairing ICD Onnetwork Test
+ # Generally completes in seconds
+ timeout-minutes: 2
run: |
scripts/run_in_python_env.sh out/venv \
'./scripts/tests/run_java_test.py \
@@ -236,7 +259,23 @@ jobs:
--tool-args "onnetwork-long --nodeid 1 --setup-pin-code 20202021 --discriminator 3840 -t 1000" \
--factoryreset \
'
+ - name: Run Pairing Onnetwork and get diagnostic log Test
+ # TODO: test below is disabled because it seems flaky (crashes on pool not being empty on app exit)
+ # See: https://github.com/project-chip/connectedhomeip/issues/36734
+ if: false
+ run: |
+ scripts/run_in_python_env.sh out/venv \
+ './scripts/tests/run_java_test.py \
+ --app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app \
+ --app-args "--discriminator 3840 --interface-id -1 --crash_log ./crashLog.log --end_user_support_log ./enduser.log --network_diagnostics_log ./network.log" \
+ --tool-path out/linux-x64-java-matter-controller \
+ --tool-cluster "bdx" \
+ --tool-args "onnetwork-long-downloadLog --nodeid 1 --setup-pin-code 20202021 --discriminator 3840 -t 3000 --logType CrashLogs --fileName ./crashLog.log" \
+ --factoryreset \
+ '
- name: Run Pairing Onnetwork Test
+ # Generally completes in seconds
+ timeout-minutes: 2
run: |
scripts/run_in_python_env.sh out/venv \
'./scripts/tests/run_kotlin_test.py \
@@ -248,6 +287,8 @@ jobs:
--factoryreset \
'
- name: Run Kotlin IM Invoke Test
+ # Generally completes in seconds
+ timeout-minutes: 2
run: |
scripts/run_in_python_env.sh out/venv \
'./scripts/tests/run_kotlin_test.py \
@@ -259,6 +300,8 @@ jobs:
--factoryreset \
'
- name: Run Kotlin IM Read Test
+ # Generally completes in seconds
+ timeout-minutes: 2
run: |
scripts/run_in_python_env.sh out/venv \
'./scripts/tests/run_kotlin_test.py \
@@ -270,6 +313,8 @@ jobs:
--factoryreset \
'
- name: Run Kotlin IM Write Test
+ # Generally completes in seconds
+ timeout-minutes: 2
run: |
scripts/run_in_python_env.sh out/venv \
'./scripts/tests/run_kotlin_test.py \
@@ -281,6 +326,8 @@ jobs:
--factoryreset \
'
- name: Run Kotlin IM Subscribe Test
+ # Generally completes in seconds
+ timeout-minutes: 2
run: |
scripts/run_in_python_env.sh out/venv \
'./scripts/tests/run_kotlin_test.py \
diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
index ceee20818defd8..7e19267b3742af 100644
--- a/.github/workflows/tests.yaml
+++ b/.github/workflows/tests.yaml
@@ -496,6 +496,7 @@ jobs:
--target linux-x64-network-manager-ipv6only-no-ble-no-wifi-tsan-clang-test \
--target linux-x64-fabric-admin-rpc-ipv6only-no-ble-no-wifi-clang \
--target linux-x64-fabric-bridge-rpc-ipv6only-no-ble-no-wifi-clang \
+ --target linux-x64-fabric-sync-ipv6only-no-ble-no-wifi-clang \
--target linux-x64-light-data-model-no-unique-id-ipv6only-no-ble-no-wifi-clang \
--target linux-x64-python-bindings \
build \
@@ -513,6 +514,7 @@ jobs:
echo "NETWORK_MANAGEMENT_APP: out/linux-x64-network-manager-ipv6only-no-ble-no-wifi-tsan-clang-test/matter-network-manager-app" >> /tmp/test_env.yaml
echo "FABRIC_ADMIN_APP: out/linux-x64-fabric-admin-rpc-ipv6only-no-ble-no-wifi-clang/fabric-admin" >> /tmp/test_env.yaml
echo "FABRIC_BRIDGE_APP: out/linux-x64-fabric-bridge-rpc-ipv6only-no-ble-no-wifi-clang/fabric-bridge-app" >> /tmp/test_env.yaml
+ echo "FABRIC_SYNC_APP: out/linux-x64-fabric-sync-ipv6only-no-ble-no-wifi-clang/fabric-sync" >> /tmp/test_env.yaml
echo "LIGHTING_APP_NO_UNIQUE_ID: out/linux-x64-light-data-model-no-unique-id-ipv6only-no-ble-no-wifi-clang/chip-lighting-app" >> /tmp/test_env.yaml
echo "TRACE_APP: out/trace_data/app-{SCRIPT_BASE_NAME}" >> /tmp/test_env.yaml
echo "TRACE_TEST_JSON: out/trace_data/test-{SCRIPT_BASE_NAME}" >> /tmp/test_env.yaml
diff --git a/docs/testing/python.md b/docs/testing/python.md
index a7bec6d07e17c3..9bda585726d409 100644
--- a/docs/testing/python.md
+++ b/docs/testing/python.md
@@ -722,6 +722,11 @@ for that run, e.g.:
- Example: `"Manual pairing code: \\[\\d+\\]"`
+- `app-stdin-pipe`: Specifies the path to the named pipe that the test runner
+ might use to send input to the application.
+
+ - Example: `/tmp/app-fifo`
+
- `script-args`: Specifies the arguments to be passed to the test script.
- Example:
diff --git a/docs/upgrading.md b/docs/upgrading.md
index 517a83f6574dc9..e9e17239d50049 100644
--- a/docs/upgrading.md
+++ b/docs/upgrading.md
@@ -102,3 +102,8 @@ To preserve `codegen/zap` generated logic, use
`CodegenDataModelProviderInstance` (see changes in
[36558](https://github.com/project-chip/connectedhomeip/pull/36558) and
[36613](https://github.com/project-chip/connectedhomeip/pull/36613) ).
+
+To use default attribute persistence, you need to pass in a
+`PersistentStorageDelegate` to `CodegenDataModelProviderInstance`. See example
+changes in [36658](https://github.com/project-chip/connectedhomeip/pull/36658)
+).
diff --git a/examples/air-purifier-app/ameba/main/chipinterface.cpp b/examples/air-purifier-app/ameba/main/chipinterface.cpp
index 72563f987efdbe..368fa8eee05a8a 100644
--- a/examples/air-purifier-app/ameba/main/chipinterface.cpp
+++ b/examples/air-purifier-app/ameba/main/chipinterface.cpp
@@ -135,7 +135,7 @@ static void InitServer(intptr_t context)
// Init ZCL Data Model and CHIP App Server
static chip::CommonCaseDeviceServerInitParams initParams;
(void) initParams.InitializeStaticResourcesBeforeServerInit();
- initParams.dataModelProvider = CodegenDataModelProviderInstance();
+ initParams.dataModelProvider = CodegenDataModelProviderInstance(initParams.persistentStorageDelegate);
#if CONFIG_ENABLE_AMEBA_CRYPTO
ChipLogProgress(DeviceLayer, "platform crypto enabled!");
static chip::AmebaPersistentStorageOperationalKeystore sAmebaPersistentStorageOpKeystore;
diff --git a/examples/air-purifier-app/cc32xx/main/AppTask.cpp b/examples/air-purifier-app/cc32xx/main/AppTask.cpp
index e7527ef338f05a..a14ccbeff0a900 100644
--- a/examples/air-purifier-app/cc32xx/main/AppTask.cpp
+++ b/examples/air-purifier-app/cc32xx/main/AppTask.cpp
@@ -165,7 +165,7 @@ int AppTask::Init()
PLAT_LOG("Initialize Server");
static chip::CommonCaseDeviceServerInitParams initParams;
(void) initParams.InitializeStaticResourcesBeforeServerInit();
- initParams.dataModelProvider = CodegenDataModelProviderInstance();
+ initParams.dataModelProvider = CodegenDataModelProviderInstance(initParams.persistentStorageDelegate);
chip::Server::GetInstance().Init(initParams);
// Initialize device attestation config
diff --git a/examples/all-clusters-app/ameba/main/chipinterface.cpp b/examples/all-clusters-app/ameba/main/chipinterface.cpp
index 06ba9f01cd8256..7ba25ed011cd41 100644
--- a/examples/all-clusters-app/ameba/main/chipinterface.cpp
+++ b/examples/all-clusters-app/ameba/main/chipinterface.cpp
@@ -150,7 +150,7 @@ static void InitServer(intptr_t context)
initParams.appDelegate = &sAmebaObserver;
initParams.InitializeStaticResourcesBeforeServerInit();
- initParams.dataModelProvider = CodegenDataModelProviderInstance();
+ initParams.dataModelProvider = CodegenDataModelProviderInstance(initParams.persistentStorageDelegate);
#if CONFIG_ENABLE_AMEBA_CRYPTO
ChipLogProgress(DeviceLayer, "platform crypto enabled!");
diff --git a/examples/all-clusters-app/asr/BUILD.gn b/examples/all-clusters-app/asr/BUILD.gn
index e27884810cbd8f..c9764e954189b2 100644
--- a/examples/all-clusters-app/asr/BUILD.gn
+++ b/examples/all-clusters-app/asr/BUILD.gn
@@ -111,6 +111,7 @@ asr_executable("clusters_app") {
"${chip_root}/examples/all-clusters-app/all-clusters-common",
"${chip_root}/examples/common/QRCode",
"${chip_root}/examples/providers:device_info_provider",
+ "${chip_root}/src/app:attribute-persistence",
"${chip_root}/src/lib",
"${chip_root}/src/platform/logging:default",
"${chip_root}/src/setup_payload",
diff --git a/examples/all-clusters-app/infineon/psoc6/BUILD.gn b/examples/all-clusters-app/infineon/psoc6/BUILD.gn
index aeb1fa0b6feb4d..bc250abd5c6e90 100644
--- a/examples/all-clusters-app/infineon/psoc6/BUILD.gn
+++ b/examples/all-clusters-app/infineon/psoc6/BUILD.gn
@@ -148,6 +148,7 @@ psoc6_executable("clusters_app") {
"${chip_root}/examples/all-clusters-app/all-clusters-common",
"${chip_root}/examples/common/QRCode",
"${chip_root}/examples/providers:device_info_provider",
+ "${chip_root}/src/app:attribute-persistence",
"${chip_root}/src/lib",
"${chip_root}/src/platform/logging:default",
"${chip_root}/src/setup_payload",
diff --git a/examples/all-clusters-app/infineon/psoc6/src/AppTask.cpp b/examples/all-clusters-app/infineon/psoc6/src/AppTask.cpp
index 3b127091f13277..3f56626cd97b59 100644
--- a/examples/all-clusters-app/infineon/psoc6/src/AppTask.cpp
+++ b/examples/all-clusters-app/infineon/psoc6/src/AppTask.cpp
@@ -130,7 +130,7 @@ static void InitServer(intptr_t context)
// Init ZCL Data Model
static chip::CommonCaseDeviceServerInitParams initParams;
(void) initParams.InitializeStaticResourcesBeforeServerInit();
- initParams.dataModelProvider = app::CodegenDataModelProviderInstance();
+ initParams.dataModelProvider = app::CodegenDataModelProviderInstance(initParams.persistentStorageDelegate);
chip::Server::GetInstance().Init(initParams);
// We only have network commissioning on endpoint 0.
diff --git a/examples/all-clusters-app/linux/BUILD.gn b/examples/all-clusters-app/linux/BUILD.gn
index 0e16c64c41de13..64dcf06e90736b 100644
--- a/examples/all-clusters-app/linux/BUILD.gn
+++ b/examples/all-clusters-app/linux/BUILD.gn
@@ -92,6 +92,7 @@ source_set("chip-all-clusters-common") {
deps = [
"${chip_root}/examples/all-clusters-app/all-clusters-common",
"${chip_root}/examples/platform/linux:app-main",
+ "${chip_root}/src/app:attribute-persistence",
"${chip_root}/src/app/tests/suites/credentials:dac_provider",
"${chip_root}/src/lib",
"${chip_root}/third_party/jsoncpp",
diff --git a/examples/all-clusters-app/linux/fuzzing-main.cpp b/examples/all-clusters-app/linux/fuzzing-main.cpp
index 5056f08cce65f4..565e8198c26d25 100644
--- a/examples/all-clusters-app/linux/fuzzing-main.cpp
+++ b/examples/all-clusters-app/linux/fuzzing-main.cpp
@@ -57,7 +57,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t * aData, size_t aSize)
// ChipLinuxAppMainLoop blocks, and we don't want that here.
static chip::CommonCaseDeviceServerInitParams initParams;
(void) initParams.InitializeStaticResourcesBeforeServerInit();
- initParams.dataModelProvider = app::CodegenDataModelProviderInstance();
+ initParams.dataModelProvider = app::CodegenDataModelProviderInstance(initParams.persistentStorageDelegate);
VerifyOrDie(Server::GetInstance().Init(initParams) == CHIP_NO_ERROR);
ApplicationInit();
diff --git a/examples/all-clusters-app/mbed/main/AppTask.cpp b/examples/all-clusters-app/mbed/main/AppTask.cpp
index 98b8597ba43e5f..3a6686b517a78a 100644
--- a/examples/all-clusters-app/mbed/main/AppTask.cpp
+++ b/examples/all-clusters-app/mbed/main/AppTask.cpp
@@ -72,7 +72,7 @@ int AppTask::Init()
// Init ZCL Data Model and start server
static chip::CommonCaseDeviceServerInitParams initParams;
(void) initParams.InitializeStaticResourcesBeforeServerInit();
- initParams.dataModelProvider = app::CodegenDataModelProviderInstance();
+ initParams.dataModelProvider = app::CodegenDataModelProviderInstance(initParams.persistentStorageDelegate);
error = Server::GetInstance().Init(initParams);
if (error != CHIP_NO_ERROR)
{
diff --git a/examples/all-clusters-app/nrfconnect/main/AppTask.cpp b/examples/all-clusters-app/nrfconnect/main/AppTask.cpp
index 9dcd4238240111..44a656279118a4 100644
--- a/examples/all-clusters-app/nrfconnect/main/AppTask.cpp
+++ b/examples/all-clusters-app/nrfconnect/main/AppTask.cpp
@@ -227,7 +227,7 @@ CHIP_ERROR AppTask::Init()
initParams.operationalKeystore = &sPSAOperationalKeystore;
#endif
(void) initParams.InitializeStaticResourcesBeforeServerInit();
- initParams.dataModelProvider = CodegenDataModelProviderInstance();
+ initParams.dataModelProvider = CodegenDataModelProviderInstance(initParams.persistentStorageDelegate);
initParams.testEventTriggerDelegate = &sTestEventTriggerDelegate;
ReturnErrorOnFailure(chip::Server::GetInstance().Init(initParams));
AppFabricTableDelegate::Init();
diff --git a/examples/all-clusters-app/nxp/mw320/BUILD.gn b/examples/all-clusters-app/nxp/mw320/BUILD.gn
index 89ba7f249719cc..73ea8cc02023ab 100644
--- a/examples/all-clusters-app/nxp/mw320/BUILD.gn
+++ b/examples/all-clusters-app/nxp/mw320/BUILD.gn
@@ -64,6 +64,7 @@ mw320_executable("shell_mw320") {
]
deps = [
+ "${chip_root}/src/app:attribute-persistence",
"${chip_root}/src/platform:syscalls_stub",
"${chip_root}/src/platform/logging:default",
]
diff --git a/examples/all-clusters-app/nxp/mw320/main.cpp b/examples/all-clusters-app/nxp/mw320/main.cpp
index bf0d6a282e9373..fbf99510c3cbfa 100644
--- a/examples/all-clusters-app/nxp/mw320/main.cpp
+++ b/examples/all-clusters-app/nxp/mw320/main.cpp
@@ -1069,7 +1069,7 @@ static void run_chip_srv(System::Layer * aSystemLayer, void * aAppState)
static chip::CommonCaseDeviceServerInitParams initParams;
(void) initParams.InitializeStaticResourcesBeforeServerInit();
- initParams.dataModelProvider = CodegenDataModelProviderInstance();
+ initParams.dataModelProvider = CodegenDataModelProviderInstance(initParams.persistentStorageDelegate);
chip::Server::GetInstance().Init(initParams);
PRINTF("Done to call chip::Server() \r\n");
}
diff --git a/examples/all-clusters-app/tizen/BUILD.gn b/examples/all-clusters-app/tizen/BUILD.gn
index 332b03cbebaf00..59aac2228161d4 100644
--- a/examples/all-clusters-app/tizen/BUILD.gn
+++ b/examples/all-clusters-app/tizen/BUILD.gn
@@ -53,6 +53,7 @@ source_set("chip-all-clusters-common") {
deps = [
"${chip_root}/examples/all-clusters-app/all-clusters-common",
"${chip_root}/examples/platform/tizen:app-main",
+ "${chip_root}/src/app:attribute-persistence",
"${chip_root}/src/lib/shell:shell_core",
]
diff --git a/examples/all-clusters-minimal-app/ameba/main/chipinterface.cpp b/examples/all-clusters-minimal-app/ameba/main/chipinterface.cpp
index cec1c457e04f04..7e2dc227f69b56 100644
--- a/examples/all-clusters-minimal-app/ameba/main/chipinterface.cpp
+++ b/examples/all-clusters-minimal-app/ameba/main/chipinterface.cpp
@@ -159,7 +159,7 @@ static void InitServer(intptr_t context)
// Init ZCL Data Model and CHIP App Server
static chip::CommonCaseDeviceServerInitParams initParams;
initParams.InitializeStaticResourcesBeforeServerInit();
- initParams.dataModelProvider = CodegenDataModelProviderInstance();
+ initParams.dataModelProvider = CodegenDataModelProviderInstance(initParams.persistentStorageDelegate);
static AmebaObserver sAmebaObserver;
initParams.appDelegate = &sAmebaObserver;
diff --git a/examples/all-clusters-minimal-app/infineon/psoc6/src/AppTask.cpp b/examples/all-clusters-minimal-app/infineon/psoc6/src/AppTask.cpp
index 29ad68b74241f1..893f585717dc9f 100644
--- a/examples/all-clusters-minimal-app/infineon/psoc6/src/AppTask.cpp
+++ b/examples/all-clusters-minimal-app/infineon/psoc6/src/AppTask.cpp
@@ -128,7 +128,7 @@ static void InitServer(intptr_t context)
// Init ZCL Data Model
static chip::CommonCaseDeviceServerInitParams initParams;
(void) initParams.InitializeStaticResourcesBeforeServerInit();
- initParams.dataModelProvider = app::CodegenDataModelProviderInstance();
+ initParams.dataModelProvider = app::CodegenDataModelProviderInstance(initParams.persistentStorageDelegate);
chip::Server::GetInstance().Init(initParams);
// We only have network commissioning on endpoint 0.
diff --git a/examples/all-clusters-minimal-app/linux/fuzzing-main.cpp b/examples/all-clusters-minimal-app/linux/fuzzing-main.cpp
index ccd2ffa782b4dd..df61dc538ad10c 100644
--- a/examples/all-clusters-minimal-app/linux/fuzzing-main.cpp
+++ b/examples/all-clusters-minimal-app/linux/fuzzing-main.cpp
@@ -44,7 +44,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t * aData, size_t aSize)
// ChipLinuxAppMainLoop blocks, and we don't want that here.
static chip::CommonCaseDeviceServerInitParams initParams;
(void) initParams.InitializeStaticResourcesBeforeServerInit();
- initParams.dataModelProvider = app::CodegenDataModelProviderInstance();
+ initParams.dataModelProvider = app::CodegenDataModelProviderInstance(initParams.persistentStorageDelegate);
VerifyOrDie(Server::GetInstance().Init(initParams) == CHIP_NO_ERROR);
ApplicationInit();
diff --git a/examples/all-clusters-minimal-app/mbed/main/AppTask.cpp b/examples/all-clusters-minimal-app/mbed/main/AppTask.cpp
index e516aee49e6e74..16e1a665da4518 100644
--- a/examples/all-clusters-minimal-app/mbed/main/AppTask.cpp
+++ b/examples/all-clusters-minimal-app/mbed/main/AppTask.cpp
@@ -68,7 +68,7 @@ int AppTask::Init()
// Init ZCL Data Model and start server
static chip::CommonCaseDeviceServerInitParams initParams;
(void) initParams.InitializeStaticResourcesBeforeServerInit();
- initParams.dataModelProvider = app::CodegenDataModelProviderInstance();
+ initParams.dataModelProvider = app::CodegenDataModelProviderInstance(initParams.persistentStorageDelegate);
error = Server::GetInstance().Init(initParams);
if (error != CHIP_NO_ERROR)
{
diff --git a/examples/all-clusters-minimal-app/nrfconnect/main/AppTask.cpp b/examples/all-clusters-minimal-app/nrfconnect/main/AppTask.cpp
index 8d9771fa593204..7cad0ac8a13a00 100644
--- a/examples/all-clusters-minimal-app/nrfconnect/main/AppTask.cpp
+++ b/examples/all-clusters-minimal-app/nrfconnect/main/AppTask.cpp
@@ -174,7 +174,7 @@ CHIP_ERROR AppTask::Init()
initParams.operationalKeystore = &sPSAOperationalKeystore;
#endif
(void) initParams.InitializeStaticResourcesBeforeServerInit();
- initParams.dataModelProvider = app::CodegenDataModelProviderInstance();
+ initParams.dataModelProvider = app::CodegenDataModelProviderInstance(initParams.persistentStorageDelegate);
ReturnErrorOnFailure(chip::Server::GetInstance().Init(initParams));
AppFabricTableDelegate::Init();
diff --git a/examples/android/CHIPTool/app/src/main/AndroidManifest.xml b/examples/android/CHIPTool/app/src/main/AndroidManifest.xml
index 0526dc5a78160d..efd089817efe68 100644
--- a/examples/android/CHIPTool/app/src/main/AndroidManifest.xml
+++ b/examples/android/CHIPTool/app/src/main/AndroidManifest.xml
@@ -46,6 +46,15 @@
+
+
+
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 69bfdc109912c9..fb8141e72d33a8 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
@@ -72,6 +72,7 @@ class SelectActionFragment : Fragment() {
binding.provisionCustomFlowBtn.setOnClickListener { handleProvisionCustomFlowClicked() }
binding.wildcardBtn.setOnClickListener { handleWildcardClicked() }
binding.unpairDeviceBtn.setOnClickListener { handleUnpairDeviceClicked() }
+ binding.diagnosticLogBtn.setOnClickListener { handleDiagnosticLogClicked() }
binding.groupSettingBtn.setOnClickListener { handleGroupSettingClicked() }
binding.otaProviderBtn.setOnClickListener { handleOTAProviderClicked() }
binding.icdBtn.setOnClickListener { handleICDClicked() }
@@ -225,6 +226,10 @@ class SelectActionFragment : Fragment() {
showFragment(OtaProviderClientFragment.newInstance())
}
+ private fun handleDiagnosticLogClicked() {
+ showFragment(DiagnosticLogFragment.newInstance())
+ }
+
/** 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/DiagnosticLogFragment.kt b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/DiagnosticLogFragment.kt
new file mode 100644
index 00000000000000..ea7d5438d247c3
--- /dev/null
+++ b/examples/android/CHIPTool/app/src/main/java/com/google/chip/chiptool/clusterclient/DiagnosticLogFragment.kt
@@ -0,0 +1,172 @@
+package com.google.chip.chiptool.clusterclient
+
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import android.os.Environment
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ArrayAdapter
+import androidx.core.content.FileProvider
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.lifecycleScope
+import chip.devicecontroller.ChipDeviceController
+import chip.devicecontroller.DiagnosticLogType
+import chip.devicecontroller.DownloadLogCallback
+import com.google.chip.chiptool.ChipClient
+import com.google.chip.chiptool.R
+import com.google.chip.chiptool.databinding.DiagnosticLogFragmentBinding
+import java.io.File
+import java.io.FileOutputStream
+import java.io.IOException
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+class DiagnosticLogFragment : Fragment() {
+ private val deviceController: ChipDeviceController
+ get() = ChipClient.getDeviceController(requireContext())
+
+ private lateinit var scope: CoroutineScope
+
+ private lateinit var addressUpdateFragment: AddressUpdateFragment
+
+ private var _binding: DiagnosticLogFragmentBinding? = null
+ private val binding
+ get() = _binding!!
+
+ private val timeout: Long
+ get() = binding.timeoutEd.text.toString().toULongOrNull()?.toLong() ?: 0L
+
+ private val diagnosticLogTypeList = DiagnosticLogType.values()
+ private val diagnosticLogType: DiagnosticLogType
+ get() = diagnosticLogTypeList[binding.diagnosticTypeSp.selectedItemPosition]
+
+ private var mDownloadFile: File? = null
+ private var mDownloadFileOutputStream: FileOutputStream? = null
+
+ private var mReceiveFileLen = 0U
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ _binding = DiagnosticLogFragmentBinding.inflate(inflater, container, false)
+ scope = viewLifecycleOwner.lifecycleScope
+
+ addressUpdateFragment =
+ childFragmentManager.findFragmentById(R.id.addressUpdateFragment) as AddressUpdateFragment
+
+ binding.getDiagnosticLogBtn.setOnClickListener { scope.launch { getDiagnosticLogClick() } }
+
+ binding.diagnosticTypeSp.adapter =
+ ArrayAdapter(
+ requireContext(),
+ android.R.layout.simple_spinner_dropdown_item,
+ diagnosticLogTypeList
+ )
+
+ return binding.root
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ _binding = null
+ }
+
+ inner class ChipDownloadLogCallback : DownloadLogCallback {
+ override fun onError(fabricIndex: Int, nodeId: Long, errorCode: Long) {
+ Log.d(TAG, "onError: $fabricIndex, ${nodeId.toULong()}, $errorCode")
+ showMessage("Downloading Failed")
+ mDownloadFileOutputStream?.flush() ?: return
+ }
+
+ override fun onSuccess(fabricIndex: Int, nodeId: Long) {
+ Log.d(TAG, "onSuccess: $fabricIndex, ${nodeId.toULong()}")
+ mDownloadFileOutputStream?.flush() ?: return
+ showMessage("Downloading Completed")
+ mDownloadFile?.let { showNotification(it) } ?: return
+ }
+
+ override fun onTransferData(fabricIndex: Int, nodeId: Long, data: ByteArray): Boolean {
+ Log.d(TAG, "onTransferData : ${data.size}")
+ if (mDownloadFileOutputStream == null) {
+ Log.d(TAG, "mDownloadFileOutputStream or mDownloadFile is null")
+ return false
+ }
+ return addData(mDownloadFileOutputStream!!, data)
+ }
+
+ private fun addData(outputStream: FileOutputStream, data: ByteArray): Boolean {
+ try {
+ outputStream.write(data)
+ } catch (e: IOException) {
+ Log.d(TAG, "IOException", e)
+ return false
+ }
+ mReceiveFileLen += data.size.toUInt()
+ showMessage("Receive Data Size : $mReceiveFileLen")
+ return true
+ }
+ }
+
+ private fun getDiagnosticLogClick() {
+ mDownloadFile =
+ createLogFile(
+ deviceController.fabricIndex.toUInt(),
+ addressUpdateFragment.deviceId.toULong(),
+ diagnosticLogType
+ )
+ mDownloadFileOutputStream = FileOutputStream(mDownloadFile)
+ deviceController.downloadLogFromNode(
+ addressUpdateFragment.deviceId,
+ diagnosticLogType,
+ timeout,
+ ChipDownloadLogCallback()
+ )
+ }
+
+ private fun isExternalStorageWritable(): Boolean {
+ return Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED
+ }
+
+ private fun createLogFile(fabricIndex: UInt, nodeId: ULong, type: DiagnosticLogType): File? {
+ if (!isExternalStorageWritable()) {
+ return null
+ }
+ val now = System.currentTimeMillis()
+ val fileName = "${type}_${fabricIndex}_${nodeId}_$now.txt"
+ mReceiveFileLen = 0U
+ return File(requireContext().getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), fileName)
+ }
+
+ private fun showNotification(file: File) {
+ val intent =
+ Intent(Intent.ACTION_VIEW).apply {
+ setDataAndType(getFileUri(file), "text/plain")
+ addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ }
+
+ requireActivity().startActivity(intent)
+ }
+
+ private fun getFileUri(file: File): Uri {
+ return FileProvider.getUriForFile(
+ requireContext(),
+ "${requireContext().packageName}.provider",
+ file
+ )
+ }
+
+ private fun showMessage(msg: String) {
+ requireActivity().runOnUiThread { binding.diagnosticLogTv.text = msg }
+ }
+
+ companion object {
+ private const val TAG = "DiagnosticLogFragment"
+
+ fun newInstance(): DiagnosticLogFragment = DiagnosticLogFragment()
+ }
+}
diff --git a/examples/android/CHIPTool/app/src/main/res/layout/diagnostic_log_fragment.xml b/examples/android/CHIPTool/app/src/main/res/layout/diagnostic_log_fragment.xml
new file mode 100644
index 00000000000000..56b770a645b442
--- /dev/null
+++ b/examples/android/CHIPTool/app/src/main/res/layout/diagnostic_log_fragment.xml
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 5824e8061a4e03..db999600999741 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
@@ -112,6 +112,14 @@
android:layout_marginTop="8dp"
android:text="@string/unpair_device_btn_text" />
+
+