From ed0cd7d23e1614148b22cfeb68c1a8d2c6e9e0c6 Mon Sep 17 00:00:00 2001
From: Lazar Kovacic <lkovacic@amazon.com>
Date: Mon, 17 Jun 2024 16:19:16 +0200
Subject: [PATCH] Add new app install flow

---
 .../all-clusters-minimal-app.matter           |   4 +
 .../placeholder/linux/apps/app1/config.matter |   8 ++
 .../placeholder/linux/apps/app2/config.matter |   8 ++
 .../tv-app/android/java/AppPlatform-JNI.cpp   |   2 +-
 examples/tv-app/android/java/TVApp-JNI.cpp    |  16 +++
 examples/tv-app/tv-common/include/AppTv.h     |   4 +
 .../tv-common/shell/AppTvShellCommands.cpp    |  22 +++
 examples/tv-app/tv-common/src/AppTv.cpp       | 129 +++++++++++++++---
 examples/tv-app/tv-common/tv-app.matter       |   4 +
 .../tv-casting-common/tv-casting-app.matter   |   4 +
 .../application-basic-delegate.h              |   2 +-
 .../application-basic-server.cpp              |   1 +
 .../chip/application-basic-cluster.xml        |   4 +
 .../CommissionerDiscoveryController.cpp       |  16 +--
 .../CommissionerDiscoveryController.h         |  28 ++--
 .../data_model/controller-clusters.matter     |   4 +
 .../python/chip/clusters/Objects.py           |   6 +-
 .../CHIP/zap-generated/MTRBaseClusters.h      |   4 +
 .../zap-generated/cluster-enums-check.h       |   4 +
 .../app-common/zap-generated/cluster-enums.h  |   6 +-
 20 files changed, 218 insertions(+), 58 deletions(-)

diff --git a/examples/all-clusters-minimal-app/all-clusters-common/all-clusters-minimal-app.matter b/examples/all-clusters-minimal-app/all-clusters-common/all-clusters-minimal-app.matter
index 962eed946b905a..d71c874f9d304d 100644
--- a/examples/all-clusters-minimal-app/all-clusters-common/all-clusters-minimal-app.matter
+++ b/examples/all-clusters-minimal-app/all-clusters-common/all-clusters-minimal-app.matter
@@ -5324,6 +5324,10 @@ cluster ApplicationBasic = 1293 {
     kActiveVisibleFocus = 1;
     kActiveHidden = 2;
     kActiveVisibleNotFocus = 3;
+    kNotInstalled = 4;
+    kInstalling = 5;
+    kInstallationFailed = 6;
+    kInstalled = 7;
   }
 
   struct ApplicationStruct {
diff --git a/examples/placeholder/linux/apps/app1/config.matter b/examples/placeholder/linux/apps/app1/config.matter
index 39f6a4b3894475..e46ee0021135a4 100644
--- a/examples/placeholder/linux/apps/app1/config.matter
+++ b/examples/placeholder/linux/apps/app1/config.matter
@@ -8014,6 +8014,10 @@ cluster ApplicationBasic = 1293 {
     kActiveVisibleFocus = 1;
     kActiveHidden = 2;
     kActiveVisibleNotFocus = 3;
+    kNotInstalled = 4;
+    kInstalling = 5;
+    kInstallationFailed = 6;
+    kInstalled = 7;
   }
 
   struct ApplicationStruct {
@@ -8046,6 +8050,10 @@ cluster ApplicationBasic = 1293 {
     kActiveVisibleFocus = 1;
     kActiveHidden = 2;
     kActiveVisibleNotFocus = 3;
+    kNotInstalled = 4;
+    kInstalling = 5;
+    kInstallationFailed = 6;
+    kInstalled = 7;
   }
 
   struct ApplicationStruct {
diff --git a/examples/placeholder/linux/apps/app2/config.matter b/examples/placeholder/linux/apps/app2/config.matter
index d3ec0ea0e26723..353322795e76b8 100644
--- a/examples/placeholder/linux/apps/app2/config.matter
+++ b/examples/placeholder/linux/apps/app2/config.matter
@@ -7971,6 +7971,10 @@ cluster ApplicationBasic = 1293 {
     kActiveVisibleFocus = 1;
     kActiveHidden = 2;
     kActiveVisibleNotFocus = 3;
+    kNotInstalled = 4;
+    kInstalling = 5;
+    kInstallationFailed = 6;
+    kInstalled = 7;
   }
 
   struct ApplicationStruct {
@@ -8003,6 +8007,10 @@ cluster ApplicationBasic = 1293 {
     kActiveVisibleFocus = 1;
     kActiveHidden = 2;
     kActiveVisibleNotFocus = 3;
+    kNotInstalled = 4;
+    kInstalling = 5;
+    kInstallationFailed = 6;
+    kInstalled = 7;
   }
 
   struct ApplicationStruct {
diff --git a/examples/tv-app/android/java/AppPlatform-JNI.cpp b/examples/tv-app/android/java/AppPlatform-JNI.cpp
index 2daebbdd890b6a..52905cf0ba130b 100644
--- a/examples/tv-app/android/java/AppPlatform-JNI.cpp
+++ b/examples/tv-app/android/java/AppPlatform-JNI.cpp
@@ -128,7 +128,7 @@ std::vector<ContentApp::SupportedCluster> convert_to_cpp(JNIEnv * env, jobject s
     // Find Java classes. WARNING: Reflection
     jclass collectionClass = env->FindClass("java/util/Collection");
     jclass iteratorClass   = env->FindClass("java/util/Iterator");
-    jclass clusterClass    = env->FindClass("com/matter/tv/server/tvapp/SupportedCluster");
+    jclass clusterClass    = env->FindClass("com/matter/tv/server/tvapp/ContentAppSupportedCluster");
     if (collectionClass == nullptr || iteratorClass == nullptr || clusterClass == nullptr)
     {
         return {};
diff --git a/examples/tv-app/android/java/TVApp-JNI.cpp b/examples/tv-app/android/java/TVApp-JNI.cpp
index f154b0759c2b1a..c35793c7d25af0 100644
--- a/examples/tv-app/android/java/TVApp-JNI.cpp
+++ b/examples/tv-app/android/java/TVApp-JNI.cpp
@@ -240,6 +240,21 @@ class MyPincodeService : public PasscodeService
 };
 MyPincodeService gMyPincodeService;
 
+class MyAppInstallationService : public AppInstallationService
+{
+    bool LookupTargetContentApp(uint16_t vendorId, uint16_t productId) override
+    {
+        return ContentAppPlatform::GetInstance().LoadContentAppByClient(vendorId, productId) != nullptr;
+    }
+
+    void AddUninstalledContentApp(uint16_t vendorId, uint16_t productId) override
+    {
+        // TODO: Add Uninstall Content App For Android
+    }
+};
+
+MyAppInstallationService gMyAppInstallationService;
+
 class MyPostCommissioningListener : public PostCommissioningListener
 {
     void CommissioningCompleted(uint16_t vendorId, uint16_t productId, NodeId nodeId, Messaging::ExchangeManager & exchangeMgr,
@@ -372,6 +387,7 @@ void TvAppJNI::InitializeCommissioner(JNIMyUserPrompter * userPrompter)
     if (cdc != nullptr && userPrompter != nullptr)
     {
         cdc->SetPasscodeService(&gMyPincodeService);
+        cdc->SetAppInstallationService(&gMyAppInstallationService);
         cdc->SetUserPrompter(userPrompter);
         cdc->SetPostCommissioningListener(&gMyPostCommissioningListener);
     }
diff --git a/examples/tv-app/tv-common/include/AppTv.h b/examples/tv-app/tv-common/include/AppTv.h
index 8ddd793c611f70..b6284edfa7d538 100644
--- a/examples/tv-app/tv-common/include/AppTv.h
+++ b/examples/tv-app/tv-common/include/AppTv.h
@@ -149,6 +149,10 @@ class DLL_EXPORT ContentAppFactoryImpl : public ContentAppFactory
     void InstallContentApp(uint16_t vendorId, uint16_t productId);
     // Remove the app from the list of mContentApps
     bool UninstallContentApp(uint16_t vendorId, uint16_t productId);
+    // Add uninstalled content app to the list of mContentApps
+    void AddUninstalledContentApp(uint16_t vendorId, uint16_t productId);
+    // Print mContentApps and endpoints
+    void PrintInstalledApps();
 
 protected:
     std::vector<std::unique_ptr<ContentAppImpl>> mContentApps;
diff --git a/examples/tv-app/tv-common/shell/AppTvShellCommands.cpp b/examples/tv-app/tv-common/shell/AppTvShellCommands.cpp
index b39e84ad40f89a..583a5afc2b9a8a 100644
--- a/examples/tv-app/tv-common/shell/AppTvShellCommands.cpp
+++ b/examples/tv-app/tv-common/shell/AppTvShellCommands.cpp
@@ -205,6 +205,15 @@ static CHIP_ERROR PrintAllCommands()
     streamer_printf(sout,
                     "  add-admin-vendor <vid>         Add vendor ID to list which will receive admin privileges. Usage: app "
                     "add-admin-vendor 65521\r\n");
+    streamer_printf(sout,
+                    "  app list              List installed apps. Usage: app list");
+    streamer_printf(sout,
+                    "  app install <vid> <pid>        Install app with given vendor ID  and product ID. Usage: app install "
+                    "65521 32768\r\n");
+    streamer_printf(
+        sout,
+        "  app uninstall <vid> <pid>        Uinstall app at given vendor ID  and product ID. Usage: app uninstall "
+        "65521 32768\r\n");
 #endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED
 #if CHIP_DEVICE_CONFIG_ENABLE_BOTH_COMMISSIONER_AND_COMMISSIONEE
     streamer_printf(sout, "  print-app-access     Print all ACLs for app platform fabric. Usage: app print-app-access\r\n");
@@ -243,6 +252,19 @@ static CHIP_ERROR AppPlatformHandler(int argc, char ** argv)
 
         return CHIP_NO_ERROR;
     }
+
+    else if (strcmp(argv[0], "list") == 0)
+    {
+        if (argc > 1)
+        {
+            return PrintAllCommands();
+        }
+
+        ContentAppFactoryImpl * factory = GetContentAppFactoryImpl();
+        factory->PrintInstalledApps();
+
+        return CHIP_NO_ERROR;
+    }
     else if (strcmp(argv[0], "install") == 0)
     {
         if (argc < 2)
diff --git a/examples/tv-app/tv-common/src/AppTv.cpp b/examples/tv-app/tv-common/src/AppTv.cpp
index bcdeac1a52b39f..a96b2c3d879956 100644
--- a/examples/tv-app/tv-common/src/AppTv.cpp
+++ b/examples/tv-app/tv-common/src/AppTv.cpp
@@ -98,12 +98,6 @@ class MyUserPrompter : public UserPrompter
 
     // tv should override this with a dialog prompt
     inline void PromptCommissioningFailed(const char * commissioneeName, CHIP_ERROR error) override { return; }
-
-    // tv should override this with a dialog prompt
-    inline void PromptForAppInstallOKPermission(uint16_t vendorId, uint16_t productId, const char * commissioneeName) override
-    {
-        return;
-    }
 };
 
 MyUserPrompter gMyUserPrompter;
@@ -158,6 +152,11 @@ class MyAppInstallationService : public AppInstallationService
     {
         return ContentAppPlatform::GetInstance().LoadContentAppByClient(vendorId, productId) != nullptr;
     }
+
+    void AddUninstalledContentApp(uint16_t vendorId, uint16_t productId) override
+    {
+        GetContentAppFactoryImpl()->AddUninstalledContentApp(vendorId, productId);
+    }
 };
 
 MyAppInstallationService gMyAppInstallationService;
@@ -571,6 +570,21 @@ void ContentAppFactoryImpl::AddAdminVendorId(uint16_t vendorId)
     mAdminVendorIds.push_back(vendorId);
 }
 
+void ContentAppFactoryImpl::AddUninstalledContentApp(uint16_t vendorId, uint16_t productId)
+{
+    auto make_default_supported_clusters = []() {
+        return std::vector<ContentApp::SupportedCluster>{ { Descriptor::Id },      { ApplicationBasic::Id },
+                                                          { KeypadInput::Id },     { ApplicationLauncher::Id } };
+    };
+
+    auto ptr = std::make_unique<ContentAppImpl>("Vendor1", vendorId, "exampleid", productId, "Version1",
+                                                                   "0", make_default_supported_clusters());
+
+    ptr.get()->GetApplicationBasicDelegate()->SetApplicationStatus(app::Clusters::ApplicationBasic::ApplicationStatusEnum::kNotInstalled);
+
+    mContentApps.emplace_back(std::move(ptr));
+}
+
 void ContentAppFactoryImpl::InstallContentApp(uint16_t vendorId, uint16_t productId)
 {
     auto make_default_supported_clusters = []() {
@@ -583,28 +597,48 @@ void ContentAppFactoryImpl::InstallContentApp(uint16_t vendorId, uint16_t produc
     ChipLogProgress(DeviceLayer, "ContentAppFactoryImpl: InstallContentApp vendorId=%d productId=%d ", vendorId, productId);
     if (vendorId == 1 && productId == 11)
     {
-        mContentApps.emplace_back(std::make_unique<ContentAppImpl>("Vendor1", vendorId, "exampleid", productId, "Version1",
-                                                                   "34567890", make_default_supported_clusters()));
+        auto ptr = std::make_unique<ContentAppImpl>("Vendor1", vendorId, "exampleid", productId, "Version1",
+                                                                   "34567890", make_default_supported_clusters());
+
+        ptr.get()->GetApplicationBasicDelegate()->SetApplicationStatus(app::Clusters::ApplicationBasic::ApplicationStatusEnum::kInstalled);
+
+        mContentApps.emplace_back(std::move(ptr));
     }
-    else if (vendorId == 65521 && productId == 32768)
+    else if (vendorId == 65521 && productId == 32769)
     {
-        mContentApps.emplace_back(std::make_unique<ContentAppImpl>("Vendor2", vendorId, "exampleString", productId, "Version2",
-                                                                   "20202021", make_default_supported_clusters()));
+        auto ptr = std::make_unique<ContentAppImpl>("Vendor2", vendorId, "exampleString", productId, "Version2",
+                                                                   "20202021", make_default_supported_clusters());
+
+        ptr.get()->GetApplicationBasicDelegate()->SetApplicationStatus(app::Clusters::ApplicationBasic::ApplicationStatusEnum::kInstalled);
+
+        mContentApps.emplace_back(std::move(ptr));
     }
     else if (vendorId == 9050 && productId == 22)
-    {
-        mContentApps.emplace_back(std::make_unique<ContentAppImpl>("Vendor3", vendorId, "App3", productId, "Version3", "20202021",
-                                                                   make_default_supported_clusters()));
+    {   
+        auto ptr = std::make_unique<ContentAppImpl>("Vendor3", vendorId, "App3", productId, "Version3", "20202021",
+                                                                   make_default_supported_clusters());
+
+        ptr.get()->GetApplicationBasicDelegate()->SetApplicationStatus(app::Clusters::ApplicationBasic::ApplicationStatusEnum::kInstalled);
+
+        mContentApps.emplace_back(std::move(ptr));
     }
     else if (vendorId == 1111 && productId == 22)
     {
-        mContentApps.emplace_back(std::make_unique<ContentAppImpl>("TestSuiteVendor", vendorId, "applicationId", productId, "v2",
-                                                                   "20202021", make_default_supported_clusters()));
+        auto ptr = std::make_unique<ContentAppImpl>("TestSuiteVendor", vendorId, "applicationId", productId, "v2",
+                                                                   "20202021", make_default_supported_clusters());
+
+        ptr.get()->GetApplicationBasicDelegate()->SetApplicationStatus(app::Clusters::ApplicationBasic::ApplicationStatusEnum::kInstalled);
+
+        mContentApps.emplace_back(std::move(ptr));
     }
     else
     {
-        mContentApps.emplace_back(std::make_unique<ContentAppImpl>("NewAppVendor", vendorId, "newAppApplicationId", productId, "v2",
-                                                                   "20202021", make_default_supported_clusters()));
+        auto ptr = std::make_unique<ContentAppImpl>("NewAppVendor", vendorId, "newAppApplicationId", productId, "v2",
+                                                                   "20202021", make_default_supported_clusters());
+
+        ptr.get()->GetApplicationBasicDelegate()->SetApplicationStatus(app::Clusters::ApplicationBasic::ApplicationStatusEnum::kInstalled);
+
+        mContentApps.emplace_back(std::move(ptr));
     }
 }
 
@@ -627,6 +661,7 @@ bool ContentAppFactoryImpl::UninstallContentApp(uint16_t vendorId, uint16_t prod
                             app->GetApplicationBasicDelegate()->HandleGetVendorId(),
                             app->GetApplicationBasicDelegate()->HandleGetProductId());
             mContentApps.erase(mContentApps.begin() + index);
+            // TODO: call ContentAppPlatform->RemoveContentApp(ids...)
             return true;
         }
 
@@ -635,6 +670,46 @@ bool ContentAppFactoryImpl::UninstallContentApp(uint16_t vendorId, uint16_t prod
     return false;
 }
 
+// Helper function to convert enum to string
+std::string ApplicationBasicStatusToString(app::Clusters::ApplicationBasic::ApplicationStatusEnum status)
+{
+    switch (status)
+    {
+        case app::Clusters::ApplicationBasic::ApplicationStatusEnum::kStopped:
+            return "kStopped";
+        case app::Clusters::ApplicationBasic::ApplicationStatusEnum::kActiveVisibleFocus:
+            return "kActiveVisibleFocus";
+        case app::Clusters::ApplicationBasic::ApplicationStatusEnum::kActiveHidden:
+            return "kActiveHidden";
+        case app::Clusters::ApplicationBasic::ApplicationStatusEnum::kActiveVisibleNotFocus:
+            return "kActiveVisibleNotFocus";
+        case app::Clusters::ApplicationBasic::ApplicationStatusEnum::kNotInstalled:
+            return "kNotInstalled";
+        case app::Clusters::ApplicationBasic::ApplicationStatusEnum::kInstalling:
+            return "kInstalling";
+        case app::Clusters::ApplicationBasic::ApplicationStatusEnum::kInstallationFailed:
+            return "kInstallationFailed";
+        case app::Clusters::ApplicationBasic::ApplicationStatusEnum::kInstalled:
+            return "kInstalled";
+        case app::Clusters::ApplicationBasic::ApplicationStatusEnum::kUnknownEnumValue:
+            return "kUnknownEnumValue";
+        default:
+            return "UnknownEnumValue";
+    }
+}
+
+void ContentAppFactoryImpl::PrintInstalledApps()
+{
+    for (auto & contentApp : mContentApps)
+    {
+        auto app = contentApp.get();
+
+        ChipLogProgress(DeviceLayer, "Content app vid=%d pid=%d is on ep=%d with app's basic status=%s", app->GetApplicationBasicDelegate()->HandleGetVendorId(),
+                        app->GetApplicationBasicDelegate()->HandleGetProductId(), app->GetEndpointId(), 
+                        ApplicationBasicStatusToString(app->GetApplicationBasicDelegate()->HandleGetStatus()).c_str());
+    }
+}
+
 Access::Privilege ContentAppFactoryImpl::GetVendorPrivilege(uint16_t vendorId)
 {
     for (size_t i = 0; i < mAdminVendorIds.size(); ++i)
@@ -689,12 +764,22 @@ std::list<ClusterId> ContentAppFactoryImpl::GetAllowedClusterListForStaticEndpoi
 CHIP_ERROR AppTvInit()
 {
 #if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED
+    // test data for apps
+    constexpr uint16_t kApp1VendorId  = 1;
+    constexpr uint16_t kApp1ProductId = 11;
+    constexpr uint16_t kApp2VendorId  = 65521;
+    constexpr uint16_t kApp2ProductId = 32769;
+    constexpr uint16_t kApp3VendorId  = 9050;
+    constexpr uint16_t kApp3ProductId = 22;
+    constexpr uint16_t kApp4VendorId  = 1111;
+    constexpr uint16_t kApp4ProductId = 22;
+
     ContentAppPlatform::GetInstance().SetupAppPlatform();
     ContentAppPlatform::GetInstance().SetContentAppFactory(&gFactory);
-    gFactory.InstallContentApp((uint16_t) 1, (uint16_t) 11);
-    gFactory.InstallContentApp((uint16_t) 65521, (uint16_t) 32768);
-    gFactory.InstallContentApp((uint16_t) 9050, (uint16_t) 22);
-    gFactory.InstallContentApp((uint16_t) 1111, (uint16_t) 22);
+    gFactory.InstallContentApp(kApp1VendorId, kApp1ProductId);
+    gFactory.InstallContentApp(kApp2VendorId, kApp2ProductId);
+    gFactory.InstallContentApp(kApp3VendorId, kApp3ProductId);
+    gFactory.InstallContentApp(kApp4VendorId, kApp4ProductId);
     uint16_t value;
     if (DeviceLayer::GetDeviceInstanceInfoProvider()->GetVendorId(value) != CHIP_NO_ERROR)
     {
diff --git a/examples/tv-app/tv-common/tv-app.matter b/examples/tv-app/tv-common/tv-app.matter
index e7ed8d2996728e..9db235f68cc276 100644
--- a/examples/tv-app/tv-common/tv-app.matter
+++ b/examples/tv-app/tv-common/tv-app.matter
@@ -2988,6 +2988,10 @@ cluster ApplicationBasic = 1293 {
     kActiveVisibleFocus = 1;
     kActiveHidden = 2;
     kActiveVisibleNotFocus = 3;
+    kNotInstalled = 4;
+    kInstalling = 5;
+    kInstallationFailed = 6;
+    kInstalled = 7;
   }
 
   struct ApplicationStruct {
diff --git a/examples/tv-casting-app/tv-casting-common/tv-casting-app.matter b/examples/tv-casting-app/tv-casting-common/tv-casting-app.matter
index d4064ec1dccc64..4c3168460d015c 100644
--- a/examples/tv-casting-app/tv-casting-common/tv-casting-app.matter
+++ b/examples/tv-casting-app/tv-casting-common/tv-casting-app.matter
@@ -2450,6 +2450,10 @@ cluster ApplicationBasic = 1293 {
     kActiveVisibleFocus = 1;
     kActiveHidden = 2;
     kActiveVisibleNotFocus = 3;
+    kNotInstalled = 4;
+    kInstalling = 5;
+    kInstallationFailed = 6;
+    kInstalled = 7;
   }
 
   struct ApplicationStruct {
diff --git a/src/app/clusters/application-basic-server/application-basic-delegate.h b/src/app/clusters/application-basic-server/application-basic-delegate.h
index 6d9b796ca29c39..87ff9355da2d6d 100644
--- a/src/app/clusters/application-basic-server/application-basic-delegate.h
+++ b/src/app/clusters/application-basic-server/application-basic-delegate.h
@@ -91,7 +91,7 @@ class Delegate
 
 protected:
     CatalogVendorApp mCatalogVendorApp;
-    ApplicationStatusEnum mApplicationStatus = ApplicationStatusEnum::kStopped;
+    ApplicationStatusEnum mApplicationStatus = ApplicationStatusEnum::kNotInstalled;
 };
 
 } // namespace ApplicationBasic
diff --git a/src/app/clusters/application-basic-server/application-basic-server.cpp b/src/app/clusters/application-basic-server/application-basic-server.cpp
index 3841b81873660b..3663aa3e66cc6e 100644
--- a/src/app/clusters/application-basic-server/application-basic-server.cpp
+++ b/src/app/clusters/application-basic-server/application-basic-server.cpp
@@ -225,6 +225,7 @@ CHIP_ERROR ApplicationBasicAttrAccess::ReadApplicationAttribute(app::AttributeVa
 
 CHIP_ERROR ApplicationBasicAttrAccess::ReadStatusAttribute(app::AttributeValueEncoder & aEncoder, Delegate * delegate)
 {
+    ChipLogProgress(DeviceLayer, "Here 1");
     ApplicationStatusEnum status = delegate->HandleGetStatus();
     return aEncoder.Encode(status);
 }
diff --git a/src/app/zap-templates/zcl/data-model/chip/application-basic-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/application-basic-cluster.xml
index e6331f698fd423..84eb69318319b7 100644
--- a/src/app/zap-templates/zcl/data-model/chip/application-basic-cluster.xml
+++ b/src/app/zap-templates/zcl/data-model/chip/application-basic-cluster.xml
@@ -51,5 +51,9 @@ limitations under the License.
     <item name="ActiveVisibleFocus" value="0x01"/>
     <item name="ActiveHidden" value="0x02"/>
     <item name="ActiveVisibleNotFocus" value="0x03"/>
+    <item name="NotInstalled" value="0x04"/>
+    <item name="Installing" value="0x05"/>
+    <item name="InstallationFailed" value="0x06"/>
+    <item name="Installed" value="0x07"/>
   </enum>
 </configurator>
diff --git a/src/controller/CommissionerDiscoveryController.cpp b/src/controller/CommissionerDiscoveryController.cpp
index d1a5747491ad50..be04cff999343f 100644
--- a/src/controller/CommissionerDiscoveryController.cpp
+++ b/src/controller/CommissionerDiscoveryController.cpp
@@ -230,21 +230,7 @@ void CommissionerDiscoveryController::InternalOk()
     {
         ChipLogDetail(AppServer, "UX InternalOk: app not installed.");
 
-        // notify client that app will be installed
-        CommissionerDeclaration cd;
-        cd.SetErrorCode(CommissionerDeclaration::CdError::kAppInstallConsentPending);
-        mUdcServer->SendCDCMessage(cd, Transport::PeerAddress::UDP(client->GetPeerAddress().GetIPAddress(), client->GetCdPort()));
-
-        // dialog
-        ChipLogDetail(Controller, "------PROMPT USER: %s is requesting to install app on this TV. vendorId=%d, productId=%d",
-                      client->GetDeviceName(), client->GetVendorId(), client->GetProductId());
-
-        if (mUserPrompter != nullptr)
-        {
-            mUserPrompter->PromptForAppInstallOKPermission(client->GetVendorId(), client->GetProductId(), client->GetDeviceName());
-        }
-        ChipLogDetail(Controller, "------Via Shell Enter: app install <pid> <vid>");
-        return;
+        mAppInstallationService->AddUninstalledContentApp(client->GetVendorId(), client->GetProductId());
     }
 
     if (client->GetUDCClientProcessingState() != UDCClientProcessingState::kPromptingUser)
diff --git a/src/controller/CommissionerDiscoveryController.h b/src/controller/CommissionerDiscoveryController.h
index b2211641fd0ede..20e54c95d34a1a 100644
--- a/src/controller/CommissionerDiscoveryController.h
+++ b/src/controller/CommissionerDiscoveryController.h
@@ -150,21 +150,6 @@ class DLL_EXPORT UserPrompter
      */
     virtual void PromptCommissioningFailed(const char * commissioneeName, CHIP_ERROR error) = 0;
 
-    /**
-     * @brief
-     *   Called to prompt the user for consent to allow the app commissioneeName/vendorId/productId to be installed.
-     * For example "[commissioneeName] is requesting permission to install app to this TV, approve?"
-     *
-     * If user responds with OK then implementor should call CommissionerRespondOk();
-     * If user responds with Cancel then implementor should call CommissionerRespondCancel();
-     *
-     *  @param[in]    vendorId           The vendorId in the DNS-SD advertisement of the requesting commissionee.
-     *  @param[in]    productId          The productId in the DNS-SD advertisement of the requesting commissionee.
-     *  @param[in]    commissioneeName   The commissioneeName in the DNS-SD advertisement of the requesting commissionee.
-     *
-     */
-    virtual void PromptForAppInstallOKPermission(uint16_t vendorId, uint16_t productId, const char * commissioneeName) = 0;
-
     virtual ~UserPrompter() = default;
 };
 
@@ -227,14 +212,23 @@ class DLL_EXPORT AppInstallationService
      *   Called to check if the given target app is available to the commissione with th given
      *   vendorId/productId
      *
-     * This will be called by the main chip thread so any blocking work should be moved to a separate thread.
-     *
      *  @param[in]    vendorId           The vendorId in the DNS-SD advertisement of the requesting commissionee.
      *  @param[in]    productId          The productId in the DNS-SD advertisement of the requesting commissionee.
      *
      */
     virtual bool LookupTargetContentApp(uint16_t vendorId, uint16_t productId) = 0;
 
+    /**
+     * @brief
+     *   Called to add uninstalled content app to the list of mContentApps
+     *   vendorId/productId
+     *
+     *  @param[in]    vendorId           The vendorId in the DNS-SD advertisement of the requesting commissionee.
+     *  @param[in]    productId          The productId in the DNS-SD advertisement of the requesting commissionee.
+     *
+     */
+    virtual void AddUninstalledContentApp(uint16_t vendorId, uint16_t productId) = 0;
+
     virtual ~AppInstallationService() = default;
 };
 
diff --git a/src/controller/data_model/controller-clusters.matter b/src/controller/data_model/controller-clusters.matter
index 2b21d4b4a4e708..937402b99114c8 100644
--- a/src/controller/data_model/controller-clusters.matter
+++ b/src/controller/data_model/controller-clusters.matter
@@ -8633,6 +8633,10 @@ cluster ApplicationBasic = 1293 {
     kActiveVisibleFocus = 1;
     kActiveHidden = 2;
     kActiveVisibleNotFocus = 3;
+    kNotInstalled = 4;
+    kInstalling = 5;
+    kInstallationFailed = 6;
+    kInstalled = 7;
   }
 
   struct ApplicationStruct {
diff --git a/src/controller/python/chip/clusters/Objects.py b/src/controller/python/chip/clusters/Objects.py
index 8dc1fad59e2591..75e180063e8753 100644
--- a/src/controller/python/chip/clusters/Objects.py
+++ b/src/controller/python/chip/clusters/Objects.py
@@ -44246,11 +44246,15 @@ class ApplicationStatusEnum(MatterIntEnum):
             kActiveVisibleFocus = 0x01
             kActiveHidden = 0x02
             kActiveVisibleNotFocus = 0x03
+            kNotInstalled = 0x04
+            kInstalling = 0x05
+            kInstallationFailed = 0x06
+            kInstalled = 0x07
             # All received enum values that are not listed above will be mapped
             # to kUnknownEnumValue. This is a helper enum value that should only
             # be used by code to process how it handles receiving and unknown
             # enum value. This specific should never be transmitted.
-            kUnknownEnumValue = 4,
+            kUnknownEnumValue = 8,
 
     class Structs:
         @dataclass
diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h
index 4c81148a2b6c40..7e9b63818c5a28 100644
--- a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h
+++ b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h
@@ -20194,6 +20194,10 @@ typedef NS_ENUM(uint8_t, MTRApplicationBasicApplicationStatus) {
     MTRApplicationBasicApplicationStatusActiveVisibleFocus MTR_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)) = 0x01,
     MTRApplicationBasicApplicationStatusActiveHidden MTR_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)) = 0x02,
     MTRApplicationBasicApplicationStatusActiveVisibleNotFocus MTR_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)) = 0x03,
+    MTRApplicationBasicApplicationStatusNotInstalled MTR_PROVISIONALLY_AVAILABLE = 0x04,
+    MTRApplicationBasicApplicationStatusInstalling MTR_PROVISIONALLY_AVAILABLE = 0x05,
+    MTRApplicationBasicApplicationStatusInstallationFailed MTR_PROVISIONALLY_AVAILABLE = 0x06,
+    MTRApplicationBasicApplicationStatusInstalled MTR_PROVISIONALLY_AVAILABLE = 0x07,
 } MTR_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1));
 
 typedef NS_OPTIONS(uint32_t, MTRContentControlFeature) {
diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-enums-check.h b/zzz_generated/app-common/app-common/zap-generated/cluster-enums-check.h
index 4ff889fac693cd..dee55af3e86f86 100644
--- a/zzz_generated/app-common/app-common/zap-generated/cluster-enums-check.h
+++ b/zzz_generated/app-common/app-common/zap-generated/cluster-enums-check.h
@@ -3088,6 +3088,10 @@ static auto __attribute__((unused)) EnsureKnownEnumValue(ApplicationBasic::Appli
     case EnumType::kActiveVisibleFocus:
     case EnumType::kActiveHidden:
     case EnumType::kActiveVisibleNotFocus:
+    case EnumType::kNotInstalled:
+    case EnumType::kInstalling:
+    case EnumType::kInstallationFailed:
+    case EnumType::kInstalled:
         return val;
     default:
         return EnumType::kUnknownEnumValue;
diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h b/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h
index 40d73a3fade401..ba3eecbad98ee4 100644
--- a/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h
+++ b/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h
@@ -4794,11 +4794,15 @@ enum class ApplicationStatusEnum : uint8_t
     kActiveVisibleFocus    = 0x01,
     kActiveHidden          = 0x02,
     kActiveVisibleNotFocus = 0x03,
+    kNotInstalled          = 0x04,
+    kInstalling            = 0x05,
+    kInstallationFailed    = 0x06,
+    kInstalled             = 0x07,
     // All received enum values that are not listed above will be mapped
     // to kUnknownEnumValue. This is a helper enum value that should only
     // be used by code to process how it handles receiving and unknown
     // enum value. This specific should never be transmitted.
-    kUnknownEnumValue = 4,
+    kUnknownEnumValue = 8,
 };
 } // namespace ApplicationBasic