virtual-device-app: Add DoorLock/PowerSource Manager for DoorLock (#28784)

Signed-off-by: Jaehoon You <jaehoon.you@samsung.com>
Signed-off-by: Charles Kim <chulspro.kim@samsung.com>
diff --git a/examples/virtual-device-app/android/BUILD.gn b/examples/virtual-device-app/android/BUILD.gn
index 61b29e9..8ff5677 100644
--- a/examples/virtual-device-app/android/BUILD.gn
+++ b/examples/virtual-device-app/android/BUILD.gn
@@ -30,10 +30,14 @@
     "java/ColorControlManager.cpp",
     "java/ColorControlManager.h",
     "java/DeviceApp-JNI.cpp",
+    "java/DoorLockManager.cpp",
+    "java/DoorLockManager.h",
     "java/JNIDACProvider.cpp",
     "java/JNIDACProvider.h",
     "java/OnOffManager.cpp",
     "java/OnOffManager.h",
+    "java/PowerSourceManager.cpp",
+    "java/PowerSourceManager.h",
   ]
 
   deps = [
@@ -72,7 +76,9 @@
     "java/src/com/matter/virtual/device/app/DeviceApp.java",
     "java/src/com/matter/virtual/device/app/DeviceAppCallback.java",
     "java/src/com/matter/virtual/device/app/DeviceEventType.java",
+    "java/src/com/matter/virtual/device/app/DoorLockManager.java",
     "java/src/com/matter/virtual/device/app/OnOffManager.java",
+    "java/src/com/matter/virtual/device/app/PowerSourceManager.java",
   ]
 
   javac_flags = [ "-Xlint:deprecation" ]
diff --git a/examples/virtual-device-app/android/java/ClusterChangeAttribute.cpp b/examples/virtual-device-app/android/java/ClusterChangeAttribute.cpp
index 721f468..edb373b 100644
--- a/examples/virtual-device-app/android/java/ClusterChangeAttribute.cpp
+++ b/examples/virtual-device-app/android/java/ClusterChangeAttribute.cpp
@@ -17,6 +17,7 @@
  */
 
 #include "ColorControlManager.h"
+#include "DoorLockManager.h"
 #include "OnOffManager.h"
 #include <app-common/zap-generated/attributes/Accessors.h>
 #include <app-common/zap-generated/ids/Attributes.h>
@@ -89,6 +90,19 @@
     }
 }
 
+static void DoorLockClusterAttributeChangeCallback(const app::ConcreteAttributePath & attributePath, uint16_t size, uint8_t * value)
+{
+    if (attributePath.mAttributeId == DoorLock::Attributes::LockState::Id)
+    {
+        uint8_t lockState = *value;
+
+        ChipLogProgress(Zcl, "Received lock state command endpoint %d value = %d", static_cast<int>(attributePath.mEndpointId),
+                        lockState);
+
+        DoorLockManager().PostLockStateChanged(attributePath.mEndpointId, lockState);
+    }
+}
+
 void MatterPostAttributeChangeCallback(const app::ConcreteAttributePath & attributePath, uint8_t type, uint16_t size,
                                        uint8_t * value)
 {
@@ -103,6 +117,9 @@
     case ColorControl::Id:
         ColorControlClusterAttributeChangeCallback(attributePath, size, value);
         break;
+    case DoorLock::Id:
+        DoorLockClusterAttributeChangeCallback(attributePath, size, value);
+        break;
 
     default:
         break;
diff --git a/examples/virtual-device-app/android/java/DeviceApp-JNI.cpp b/examples/virtual-device-app/android/java/DeviceApp-JNI.cpp
index 02998bb..cb80a1d 100644
--- a/examples/virtual-device-app/android/java/DeviceApp-JNI.cpp
+++ b/examples/virtual-device-app/android/java/DeviceApp-JNI.cpp
@@ -21,7 +21,9 @@
 #include "JNIDACProvider.h"
 
 #include "ColorControlManager.h"
+#include "DoorLockManager.h"
 #include "OnOffManager.h"
+#include "PowerSourceManager.h"
 #include "credentials/DeviceAttestationCredsProvider.h"
 #include <app/app-platform/ContentAppPlatform.h>
 #include <app/server/Dnssd.h>
@@ -166,3 +168,67 @@
 {
     ColorControlManager::NewManager(endpoint, manager);
 }
+
+/*
+ * Door Lock Manager
+ */
+JNI_METHOD(void, setDoorLockManager)(JNIEnv *, jobject, jint endpoint, jobject manager)
+{
+    DoorLockManager::NewManager(endpoint, manager);
+}
+
+JNI_METHOD(jboolean, setLockType)(JNIEnv *, jobject, jint endpoint, jint value)
+{
+    return DeviceLayer::SystemLayer().ScheduleLambda([endpoint, value] { DoorLockManager::SetLockType(endpoint, value); }) ==
+        CHIP_NO_ERROR;
+}
+
+JNI_METHOD(jboolean, setLockState)(JNIEnv *, jobject, jint endpoint, jint value)
+{
+    return DeviceLayer::SystemLayer().ScheduleLambda([endpoint, value] { DoorLockManager::SetLockState(endpoint, value); }) ==
+        CHIP_NO_ERROR;
+}
+
+JNI_METHOD(jboolean, setActuatorEnabled)(JNIEnv *, jobject, jint endpoint, jboolean value)
+{
+    return DeviceLayer::SystemLayer().ScheduleLambda([endpoint, value] { DoorLockManager::SetActuatorEnabled(endpoint, value); }) ==
+        CHIP_NO_ERROR;
+}
+
+JNI_METHOD(jboolean, setAutoRelockTime)(JNIEnv *, jobject, jint endpoint, jint value)
+{
+    return DeviceLayer::SystemLayer().ScheduleLambda([endpoint, value] { DoorLockManager::SetAutoRelockTime(endpoint, value); }) ==
+        CHIP_NO_ERROR;
+}
+
+JNI_METHOD(jboolean, setOperatingMode)(JNIEnv *, jobject, jint endpoint, jint value)
+{
+    return DeviceLayer::SystemLayer().ScheduleLambda([endpoint, value] { DoorLockManager::SetOperatingMode(endpoint, value); }) ==
+        CHIP_NO_ERROR;
+}
+
+JNI_METHOD(jboolean, setSupportedOperatingModes)(JNIEnv *, jobject, jint endpoint, jint value)
+{
+    return DeviceLayer::SystemLayer().ScheduleLambda(
+               [endpoint, value] { DoorLockManager::SetSupportedOperatingModes(endpoint, value); }) == CHIP_NO_ERROR;
+}
+
+JNI_METHOD(jboolean, sendLockAlarmEvent)(JNIEnv *, jobject, jint endpoint)
+{
+    return DeviceLayer::SystemLayer().ScheduleLambda([endpoint] { DoorLockManager::SendLockAlarmEvent(endpoint); }) ==
+        CHIP_NO_ERROR;
+}
+
+/*
+ * Power Source Manager
+ */
+JNI_METHOD(void, setPowerSourceManager)(JNIEnv *, jobject, jint endpoint, jobject manager)
+{
+    PowerSourceManager::NewManager(endpoint, manager);
+}
+
+JNI_METHOD(jboolean, setBatPercentRemaining)(JNIEnv *, jobject, jint endpoint, jint value)
+{
+    return DeviceLayer::SystemLayer().ScheduleLambda(
+               [endpoint, value] { PowerSourceManager::SetBatPercentRemaining(endpoint, value); }) == CHIP_NO_ERROR;
+}
diff --git a/examples/virtual-device-app/android/java/DoorLockManager.cpp b/examples/virtual-device-app/android/java/DoorLockManager.cpp
new file mode 100644
index 0000000..9a2c574
--- /dev/null
+++ b/examples/virtual-device-app/android/java/DoorLockManager.cpp
@@ -0,0 +1,195 @@
+/**
+ *
+ *    Copyright (c) 2023 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+#include "DoorLockManager.h"
+#include "DeviceApp-JNI.h"
+#include <app-common/zap-generated/attributes/Accessors.h>
+#include <app-common/zap-generated/ids/Clusters.h>
+#include <app/ConcreteAttributePath.h>
+#include <app/clusters/door-lock-server/door-lock-server.h>
+#include <app/reporting/reporting.h>
+#include <app/util/af.h>
+#include <jni.h>
+#include <lib/support/CHIPJNIError.h>
+#include <lib/support/JniReferences.h>
+#include <lib/support/JniTypeWrappers.h>
+#include <lib/support/ZclString.h>
+#include <platform/PlatformManager.h>
+#include <vector>
+
+using namespace chip;
+using namespace chip::app::Clusters;
+using namespace chip::app::Clusters::DoorLock;
+using namespace chip::DeviceLayer;
+
+static constexpr size_t kDoorLockManagerTableSize =
+    EMBER_AF_DOOR_LOCK_CLUSTER_SERVER_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT;
+
+namespace {
+
+DoorLockManager * gDoorLockManagerTable[kDoorLockManagerTableSize] = { nullptr };
+
+}
+
+void emberAfDoorLockClusterInitCallback(EndpointId endpoint)
+{
+    ChipLogProgress(Zcl, "Device App::DoorLock::PostClusterInit");
+    DeviceAppJNIMgr().PostClusterInit(chip::app::Clusters::DoorLock::Id, endpoint);
+    DoorLockServer::Instance().InitServer(endpoint);
+    EmberAfStatus status = DoorLock::Attributes::FeatureMap::Set(endpoint, 0);
+    if (status != EMBER_ZCL_STATUS_SUCCESS)
+    {
+        ChipLogProgress(Zcl, "Device App::DoorLock::emberAfDoorLockClusterInitCallback()::Updating feature map %x", status);
+    }
+}
+
+bool emberAfPluginDoorLockOnDoorLockCommand(EndpointId endpointId, const Nullable<chip::FabricIndex> & fabricIdx,
+                                            const Nullable<chip::NodeId> & nodeId, const Optional<ByteSpan> & pinCode,
+                                            OperationErrorEnum & err)
+{
+    ChipLogProgress(Zcl, "Device App::DoorLock::emberAfPluginDoorLockOnDoorLockCommand");
+    return DoorLockServer::Instance().SetLockState(endpointId, DlLockState::kLocked);
+}
+
+bool emberAfPluginDoorLockOnDoorUnlockCommand(EndpointId endpointId, const Nullable<chip::FabricIndex> & fabricIdx,
+                                              const Nullable<chip::NodeId> & nodeId, const Optional<ByteSpan> & pinCode,
+                                              OperationErrorEnum & err)
+{
+    ChipLogProgress(Zcl, "Device App::DoorLock::emberAfPluginDoorLockOnDoorUnlockCommand");
+    return DoorLockServer::Instance().SetLockState(endpointId, DlLockState::kUnlocked);
+}
+
+void DoorLockManager::NewManager(jint endpoint, jobject manager)
+{
+    ChipLogProgress(Zcl, "Device App: DoorLockManager::NewManager");
+    uint16_t ep = emberAfGetClusterServerEndpointIndex(static_cast<chip::EndpointId>(endpoint), app::Clusters::DoorLock::Id,
+                                                       EMBER_AF_DOOR_LOCK_CLUSTER_SERVER_ENDPOINT_COUNT);
+    VerifyOrReturn(ep < kDoorLockManagerTableSize,
+                   ChipLogError(Zcl, "Device App::DoorLock::NewManager: endpoint %d not found", endpoint));
+
+    VerifyOrReturn(gDoorLockManagerTable[ep] == nullptr,
+                   ChipLogError(Zcl, "Device App::DoorLock::NewManager: endpoint %d already has a manager", endpoint));
+    DoorLockManager * mgr = new DoorLockManager();
+    CHIP_ERROR err        = mgr->InitializeWithObjects(manager);
+    if (err != CHIP_NO_ERROR)
+    {
+        ChipLogError(Zcl, "Device App::DoorLock::NewManager: failed to initialize manager for endpoint %d", endpoint);
+        delete mgr;
+    }
+    else
+    {
+        gDoorLockManagerTable[ep] = mgr;
+    }
+}
+
+DoorLockManager * GetDoorLockManager(EndpointId endpoint)
+{
+    uint16_t ep = emberAfGetClusterServerEndpointIndex(endpoint, app::Clusters::DoorLock::Id,
+                                                       EMBER_AF_DOOR_LOCK_CLUSTER_SERVER_ENDPOINT_COUNT);
+    return ((ep >= kDoorLockManagerTableSize) ? nullptr : gDoorLockManagerTable[ep]);
+}
+
+jboolean DoorLockManager::SetLockType(jint endpoint, jint value)
+{
+    EmberAfStatus status = app::Clusters::DoorLock::Attributes::LockType::Set(
+        static_cast<chip::EndpointId>(endpoint), static_cast<app::Clusters::DoorLock::DlLockType>(value));
+    return status == EMBER_ZCL_STATUS_SUCCESS;
+}
+
+jboolean DoorLockManager::SetLockState(jint endpoint, jint value)
+{
+    return DoorLockServer::Instance().SetLockState(static_cast<chip::EndpointId>(endpoint),
+                                                   static_cast<app::Clusters::DoorLock::DlLockState>(value));
+}
+
+jboolean DoorLockManager::SetActuatorEnabled(jint endpoint, jboolean value)
+{
+    return DoorLockServer::Instance().SetActuatorEnabled(static_cast<chip::EndpointId>(endpoint), value);
+}
+
+jboolean DoorLockManager::SetAutoRelockTime(jint endpoint, jint value)
+{
+    return DoorLockServer::Instance().SetAutoRelockTime(static_cast<chip::EndpointId>(endpoint), static_cast<uint32_t>(value));
+}
+
+jboolean DoorLockManager::SetOperatingMode(jint endpoint, jint value)
+{
+    EmberAfStatus status = app::Clusters::DoorLock::Attributes::OperatingMode::Set(
+        static_cast<chip::EndpointId>(endpoint), static_cast<app::Clusters::DoorLock::OperatingModeEnum>(value));
+    return status == EMBER_ZCL_STATUS_SUCCESS;
+}
+
+jboolean DoorLockManager::SetSupportedOperatingModes(jint endpoint, jint value)
+{
+    EmberAfStatus status = app::Clusters::DoorLock::Attributes::SupportedOperatingModes::Set(
+        static_cast<chip::EndpointId>(endpoint), static_cast<app::Clusters::DoorLock::DlSupportedOperatingModes>(value));
+    return status == EMBER_ZCL_STATUS_SUCCESS;
+}
+
+jboolean DoorLockManager::SendLockAlarmEvent(jint endpoint)
+{
+    return DoorLockServer::Instance().SendLockAlarmEvent(static_cast<chip::EndpointId>(endpoint), AlarmCodeEnum::kDoorForcedOpen);
+}
+
+void DoorLockManager::PostLockStateChanged(chip::EndpointId endpoint, int value)
+{
+    ChipLogProgress(Zcl, "Device App: DoorLockManager::PostLockStateChanged:%d", value);
+    DoorLockManager * mgr = GetDoorLockManager(endpoint);
+    VerifyOrReturn(mgr != nullptr, ChipLogError(Zcl, "DoorLockManager null"));
+
+    mgr->HandleLockStateChanged(static_cast<int>(endpoint), value);
+}
+
+CHIP_ERROR DoorLockManager::InitializeWithObjects(jobject managerObject)
+{
+    JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
+    VerifyOrReturnLogError(env != nullptr, CHIP_ERROR_INCORRECT_STATE);
+
+    mDoorLockManagerObject = env->NewGlobalRef(managerObject);
+    VerifyOrReturnLogError(mDoorLockManagerObject != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
+
+    jclass DoorLockManagerClass = env->GetObjectClass(managerObject);
+    VerifyOrReturnLogError(DoorLockManagerClass != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
+
+    mHandleLockStateChangedMethod = env->GetMethodID(DoorLockManagerClass, "handleLockStateChanged", "(I)V");
+    if (mHandleLockStateChangedMethod == nullptr)
+    {
+        ChipLogError(Zcl, "Failed to access DoorLockManager 'handleLockStateChanged' method");
+        env->ExceptionClear();
+        return CHIP_ERROR_INVALID_ARGUMENT;
+    }
+
+    return CHIP_NO_ERROR;
+}
+
+void DoorLockManager::HandleLockStateChanged(jint endpoint, jint value)
+{
+    ChipLogProgress(Zcl, "DoorLockManager::HandleLockStateChanged:%d", value);
+
+    JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
+    VerifyOrReturn(env != NULL, ChipLogProgress(Zcl, "env null"));
+    VerifyOrReturn(mDoorLockManagerObject != nullptr, ChipLogProgress(Zcl, "mDoorLockManagerObject null"));
+    VerifyOrReturn(mHandleLockStateChangedMethod != nullptr, ChipLogProgress(Zcl, "mHandleLockStateChangedMethod null"));
+
+    env->ExceptionClear();
+    env->CallVoidMethod(mDoorLockManagerObject, mHandleLockStateChangedMethod, value);
+    if (env->ExceptionCheck())
+    {
+        ChipLogError(AppServer, "Java exception in DoorLockManager::HandleLockStateChanged");
+        env->ExceptionDescribe();
+        env->ExceptionClear();
+    }
+}
diff --git a/examples/virtual-device-app/android/java/DoorLockManager.h b/examples/virtual-device-app/android/java/DoorLockManager.h
new file mode 100644
index 0000000..d6dac14
--- /dev/null
+++ b/examples/virtual-device-app/android/java/DoorLockManager.h
@@ -0,0 +1,67 @@
+/*
+ *
+ *    Copyright (c) 2023 Project CHIP Authors
+ *    All rights reserved.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#pragma once
+
+#include <app-common/zap-generated/cluster-objects.h>
+#include <app/clusters/door-lock-server/door-lock-server.h>
+#include <app/util/attribute-storage.h>
+#include <jni.h>
+
+/**
+ * @brief Handles interfacing between java code and C++ code for the purposes of DoorLock clusters.
+ */
+class DoorLockManager
+{
+public:
+    // installed a bridege for a DoorLock cluster endpoint and java object
+    static void NewManager(jint endpoint, jobject manager);
+
+    // helps for java to set attributes::LockType of DoorLock cluster
+    static jboolean SetLockType(jint endpoint, jint value);
+
+    // helps for java to set attributes::LockState of DoorLock cluster
+    static jboolean SetLockState(jint endpoint, jint value);
+
+    // helps for java to set attributes::ActuatorEnabled of DoorLock cluster
+    static jboolean SetActuatorEnabled(jint endpoint, jboolean value);
+
+    // helps for java to set attributes::AutoRelockTime of DoorLock cluster
+    static jboolean SetAutoRelockTime(jint endpoint, jint value);
+
+    // helps for java to set attributes::OperatingMode of DoorLock cluster
+    static jboolean SetOperatingMode(jint endpoint, jint value);
+
+    // helps for java to set attributes::SupportedOperatingMode of DoorLock cluster
+    static jboolean SetSupportedOperatingModes(jint endpoint, jint value);
+
+    // helps for java to send LockAlarmEvent of DoorLock cluster
+    static jboolean SendLockAlarmEvent(jint endpoint);
+
+    // posts a LockStateChanged event to suitable DoorLockManager
+    static void PostLockStateChanged(chip::EndpointId endpoint, int value);
+
+    // handles `Changed` callbacks by calling the java `void HandleLockStateChanged()` method
+    void HandleLockStateChanged(int endpoint, int value);
+
+private:
+    // init with java objects
+    CHIP_ERROR InitializeWithObjects(jobject managerObject);
+    jobject mDoorLockManagerObject          = nullptr;
+    jmethodID mHandleLockStateChangedMethod = nullptr;
+};
diff --git a/examples/virtual-device-app/android/java/PowerSourceManager.cpp b/examples/virtual-device-app/android/java/PowerSourceManager.cpp
new file mode 100644
index 0000000..0d56b58
--- /dev/null
+++ b/examples/virtual-device-app/android/java/PowerSourceManager.cpp
@@ -0,0 +1,107 @@
+/**
+ *
+ *    Copyright (c) 2023 Project CHIP Authors
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+#include "PowerSourceManager.h"
+#include "DeviceApp-JNI.h"
+#include <app-common/zap-generated/attributes/Accessors.h>
+#include <app-common/zap-generated/ids/Attributes.h>
+#include <app-common/zap-generated/ids/Clusters.h>
+#include <app/ConcreteAttributePath.h>
+#include <app/clusters/door-lock-server/door-lock-server.h>
+#include <app/reporting/reporting.h>
+#include <app/util/af.h>
+#include <jni.h>
+#include <lib/support/CHIPJNIError.h>
+#include <lib/support/JniReferences.h>
+#include <lib/support/JniTypeWrappers.h>
+#include <lib/support/ZclString.h>
+#include <platform/PlatformManager.h>
+
+using namespace chip;
+using namespace chip::app::Clusters;
+using namespace chip::app::Clusters::PowerSource;
+
+static constexpr size_t kPowerSourceManagerTableSize =
+    EMBER_AF_POWER_SOURCE_CLUSTER_SERVER_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT;
+
+namespace {
+
+PowerSourceManager * gPowerSourceManagerTable[kPowerSourceManagerTableSize] = { nullptr };
+
+}
+
+void emberAfPowerSourceClusterInitCallback(EndpointId endpoint)
+{
+    ChipLogProgress(Zcl, "Device App::PowerSource::PostClusterInit");
+    DeviceAppJNIMgr().PostClusterInit(chip::app::Clusters::PowerSource::Id, endpoint);
+}
+
+void PowerSourceManager::NewManager(jint endpoint, jobject manager)
+{
+    ChipLogProgress(Zcl, "Device App: PowerSourceManager::NewManager");
+    uint16_t ep = emberAfGetClusterServerEndpointIndex(static_cast<chip::EndpointId>(endpoint), app::Clusters::PowerSource::Id,
+                                                       EMBER_AF_POWER_SOURCE_CLUSTER_SERVER_ENDPOINT_COUNT);
+    VerifyOrReturn(ep < kPowerSourceManagerTableSize,
+                   ChipLogError(Zcl, "Device App::PowerSource::NewManager: endpoint %d not found", endpoint));
+
+    VerifyOrReturn(gPowerSourceManagerTable[ep] == nullptr,
+                   ChipLogError(Zcl, "Device App::PowerSource::NewManager: endpoint %d already has a manager", endpoint));
+    PowerSourceManager * mgr = new PowerSourceManager();
+    CHIP_ERROR err           = mgr->InitializeWithObjects(manager);
+    if (err != CHIP_NO_ERROR)
+    {
+        ChipLogError(Zcl, "Device App::PowerSource::NewManager: failed to initialize manager for endpoint %d", endpoint);
+        delete mgr;
+    }
+    else
+    {
+        gPowerSourceManagerTable[ep] = mgr;
+    }
+}
+
+PowerSourceManager * GetPowerSourceManager(EndpointId endpoint)
+{
+    uint16_t ep = emberAfGetClusterServerEndpointIndex(endpoint, app::Clusters::PowerSource::Id,
+                                                       EMBER_AF_POWER_SOURCE_CLUSTER_SERVER_ENDPOINT_COUNT);
+    return ((ep >= kPowerSourceManagerTableSize) ? nullptr : gPowerSourceManagerTable[ep]);
+}
+
+jboolean PowerSourceManager::SetBatPercentRemaining(jint endpoint, jint value)
+{
+    using namespace chip::app::Clusters;
+    using namespace chip::DeviceLayer;
+    EmberAfStatus status = EMBER_ZCL_STATUS_SUCCESS;
+
+    status =
+        PowerSource::Attributes::BatPercentRemaining::Set(static_cast<chip::EndpointId>(endpoint), static_cast<uint8_t>(value * 2));
+
+    ChipLogDetail(Zcl, "Device App::PowerSource::SetBatPercentRemaining: endpoint:%d, percent:%d", endpoint, value);
+    return status == EMBER_ZCL_STATUS_SUCCESS;
+}
+
+CHIP_ERROR PowerSourceManager::InitializeWithObjects(jobject managerObject)
+{
+    JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread();
+    VerifyOrReturnLogError(env != nullptr, CHIP_ERROR_INCORRECT_STATE);
+
+    mPowerSourceManagerObject = env->NewGlobalRef(managerObject);
+    VerifyOrReturnLogError(mPowerSourceManagerObject != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
+
+    jclass PowerSourceManagerClass = env->GetObjectClass(managerObject);
+    VerifyOrReturnLogError(PowerSourceManagerClass != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
+
+    return CHIP_NO_ERROR;
+}
diff --git a/examples/virtual-device-app/android/java/PowerSourceManager.h b/examples/virtual-device-app/android/java/PowerSourceManager.h
new file mode 100644
index 0000000..54b2c68
--- /dev/null
+++ b/examples/virtual-device-app/android/java/PowerSourceManager.h
@@ -0,0 +1,39 @@
+/*
+ *
+ *    Copyright (c) 2023 Project CHIP Authors
+ *    All rights reserved.
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#pragma once
+
+#include <app-common/zap-generated/cluster-objects.h>
+#include <app/util/attribute-storage.h>
+#include <jni.h>
+
+/**
+ * @brief Handles interfacing between java code and C++ code for the purposes of PowerSource clusters.
+ */
+class PowerSourceManager
+{
+public:
+    // installed a bridege for a PowerSource cluster endpoint and java object
+    static void NewManager(jint endpoint, jobject manager);
+    static jboolean SetBatPercentRemaining(jint endpoint, jint value);
+
+private:
+    // init with java objects
+    CHIP_ERROR InitializeWithObjects(jobject managerObject);
+    jobject mPowerSourceManagerObject = nullptr;
+};
diff --git a/examples/virtual-device-app/android/java/src/com/matter/virtual/device/app/DeviceApp.java b/examples/virtual-device-app/android/java/src/com/matter/virtual/device/app/DeviceApp.java
index 3df52b0..feb9f91 100644
--- a/examples/virtual-device-app/android/java/src/com/matter/virtual/device/app/DeviceApp.java
+++ b/examples/virtual-device-app/android/java/src/com/matter/virtual/device/app/DeviceApp.java
@@ -53,11 +53,31 @@
   // called after Matter server is initiated
   public native void postServerInit(int deviceTypeId);
 
+  public native void setDACProvider(DACProvider provider);
+
   public native void setOnOffManager(int endpoint, OnOffManager manager);
 
   public native boolean setOnOff(int endpoint, boolean value);
 
-  public native void setDACProvider(DACProvider provider);
+  public native void setDoorLockManager(int endpoint, DoorLockManager manager);
+
+  public native boolean setLockType(int endpoint, int value);
+
+  public native boolean setLockState(int endpoint, int value);
+
+  public native boolean setActuatorEnabled(int endpoint, boolean value);
+
+  public native boolean setAutoRelockTime(int endpoint, int value);
+
+  public native boolean setOperatingMode(int endpoint, int value);
+
+  public native boolean setSupportedOperatingModes(int endpoint, int value);
+
+  public native boolean sendLockAlarmEvent(int endpoint);
+
+  public native void setPowerSourceManager(int endpoint, PowerSourceManager manager);
+
+  public native boolean setBatPercentRemaining(int endpoint, int value);
 
   static {
     System.loadLibrary("DeviceApp");
diff --git a/examples/virtual-device-app/android/java/src/com/matter/virtual/device/app/DoorLockManager.java b/examples/virtual-device-app/android/java/src/com/matter/virtual/device/app/DoorLockManager.java
new file mode 100644
index 0000000..6e03667
--- /dev/null
+++ b/examples/virtual-device-app/android/java/src/com/matter/virtual/device/app/DoorLockManager.java
@@ -0,0 +1,39 @@
+package com.matter.virtual.device.app;
+
+public interface DoorLockManager {
+
+  int DlLockState_kNotFullyLocked = 0;
+  int DlLockState_kLocked = 1;
+  int DlLockState_kUnlocked = 2;
+  int DlLockState_kUnlatched = 3;
+
+  int DlLockType_kDeadBolt = 0;
+  int DlLockType_kMagnetic = 1;
+  int DlLockType_kOther = 2;
+  int DlLockType_kMortise = 3;
+  int DlLockType_kRim = 4;
+  int DlLockType_kLatchBolt = 5;
+  int DlLockType_kCylindricalLock = 6;
+  int DlLockType_kTubularLock = 7;
+  int DlLockType_kInterconnectedLock = 8;
+  int DlLockType_kDeadLatch = 9;
+  int DlLockType_kDoorFurniture = 10;
+  int DlLockType_kEurocylinder = 11;
+
+  int OperatingModeEnum_kNormal = 0;
+  int OperatingModeEnum_kVacation = 1;
+  int OperatingModeEnum_kPrivacy = 2;
+  int OperatingModeEnum_kNoRemoteLockUnlock = 3;
+  int OperatingModeEnum_kPassage = 4;
+
+  int DlSupportedOperatingModes_kNormal = 0x1;
+  int DlSupportedOperatingModes_kVacation = 0x2;
+  int DlSupportedOperatingModes_kPrivacy = 0x4;
+  int DlSupportedOperatingModes_kNoRemoteLockUnlock = 0x8;
+  int DlSupportedOperatingModes_kPassage = 0x10;
+
+  /** initialize attribute by DeviceApp */
+  void initAttributeValue();
+
+  void handleLockStateChanged(int value);
+}
diff --git a/examples/virtual-device-app/android/java/src/com/matter/virtual/device/app/PowerSourceManager.java b/examples/virtual-device-app/android/java/src/com/matter/virtual/device/app/PowerSourceManager.java
new file mode 100644
index 0000000..5245b49
--- /dev/null
+++ b/examples/virtual-device-app/android/java/src/com/matter/virtual/device/app/PowerSourceManager.java
@@ -0,0 +1,6 @@
+package com.matter.virtual.device.app;
+
+public interface PowerSourceManager {
+
+  void initAttributeValue();
+}