From 182137a9a4b09c8188d2cbffa312550c5dc83641 Mon Sep 17 00:00:00 2001
From: Liam <byteslice@airmail.cc>
Date: Tue, 2 Jan 2024 18:29:03 -0500
Subject: [PATCH] am: migrate global state to per-applet state structure

---
 src/core/core.cpp                             |   9 +-
 src/core/core.h                               |   3 +-
 src/core/hle/kernel/kernel.cpp                |  30 +-
 src/core/hle/service/am/am.cpp                |  10 +-
 src/core/hle/service/am/am_results.h          |   1 +
 src/core/hle/service/am/applet_ae.cpp         |  72 ++--
 src/core/hle/service/am/applet_ae.h           |  10 +-
 .../service/am/applet_common_functions.cpp    |  11 +-
 .../hle/service/am/applet_common_functions.h  |   6 +-
 src/core/hle/service/am/applet_manager.cpp    |   2 +-
 .../hle/service/am/applet_message_queue.cpp   |   7 +-
 .../hle/service/am/applet_message_queue.h     |   1 +
 src/core/hle/service/am/applet_oe.cpp         |  35 +-
 src/core/hle/service/am/applet_oe.h           |  10 +-
 .../hle/service/am/application_functions.cpp  | 156 ++++----
 .../hle/service/am/application_functions.h    |  13 +-
 src/core/hle/service/am/application_proxy.cpp |  21 +-
 src/core/hle/service/am/application_proxy.h   |   9 +-
 .../hle/service/am/common_state_getter.cpp    |  51 +--
 src/core/hle/service/am/common_state_getter.h |  10 +-
 src/core/hle/service/am/frontend/applets.cpp  |  20 +-
 src/core/hle/service/am/frontend/applets.h    |   1 -
 .../service/am/library_applet_accessor.cpp    |  80 ++---
 .../hle/service/am/library_applet_accessor.h  |  13 +-
 .../hle/service/am/library_applet_creator.cpp | 115 +++++-
 .../hle/service/am/library_applet_creator.h   |   6 +-
 .../hle/service/am/library_applet_proxy.cpp   |  23 +-
 .../hle/service/am/library_applet_proxy.h     |   9 +-
 .../am/library_applet_self_accessor.cpp       | 336 ++++++------------
 .../service/am/library_applet_self_accessor.h |  18 +-
 .../service/am/process_winding_controller.cpp |  35 +-
 .../service/am/process_winding_controller.h   |   6 +-
 src/core/hle/service/am/self_controller.cpp   | 187 +++++-----
 src/core/hle/service/am/self_controller.h     |  25 +-
 .../hle/service/am/system_applet_proxy.cpp    |  19 +-
 src/core/hle/service/am/system_applet_proxy.h |   8 +-
 src/core/hle/service/am/window_controller.cpp |  18 +-
 src/core/hle/service/am/window_controller.h   |   6 +-
 src/core/hle/service/vi/vi.cpp                |   8 +
 src/yuzu/configuration/configure_input.cpp    |  17 +-
 src/yuzu/main.cpp                             |  28 +-
 41 files changed, 692 insertions(+), 753 deletions(-)

diff --git a/src/core/core.cpp b/src/core/core.cpp
index 8c04685a5..435ef6793 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -392,6 +392,7 @@ struct System::Impl {
         // All threads are started, begin main process execution, now that we're in the clear.
         main_process->Run(load_parameters->main_thread_priority,
                           load_parameters->main_thread_stack_size);
+        main_process->Close();
 
         if (Settings::values.gamecard_inserted) {
             if (Settings::values.gamecard_current_game) {
@@ -886,10 +887,6 @@ void System::SetFrontendAppletSet(Service::AM::Frontend::FrontendAppletSet&& set
     impl->frontend_applets.SetFrontendAppletSet(std::move(set));
 }
 
-void System::SetDefaultAppletFrontendSet() {
-    impl->frontend_applets.SetDefaultAppletFrontendSet();
-}
-
 Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() {
     return impl->frontend_applets;
 }
@@ -898,6 +895,10 @@ const Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHold
     return impl->frontend_applets;
 }
 
+Service::AM::AppletManager& System::GetAppletManager() {
+    return impl->applet_manager;
+}
+
 void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) {
     impl->content_provider = std::move(provider);
 }
diff --git a/src/core/core.h b/src/core/core.h
index 97e2d4b50..90826bd3a 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -350,12 +350,13 @@ public:
                            u64 main_region_size);
 
     void SetFrontendAppletSet(Service::AM::Frontend::FrontendAppletSet&& set);
-    void SetDefaultAppletFrontendSet();
 
     [[nodiscard]] Service::AM::Frontend::FrontendAppletHolder& GetFrontendAppletHolder();
     [[nodiscard]] const Service::AM::Frontend::FrontendAppletHolder& GetFrontendAppletHolder()
         const;
 
+    [[nodiscard]] Service::AM::AppletManager& GetAppletManager();
+
     void SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider);
 
     [[nodiscard]] FileSys::ContentProvider& GetContentProvider();
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index f3683cdcc..34b25be66 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -97,8 +97,14 @@ struct KernelCore::Impl {
         RegisterHostThread(nullptr);
     }
 
-    void TerminateApplicationProcess() {
-        application_process.load()->Terminate();
+    void TerminateAllProcesses() {
+        std::scoped_lock lk{process_list_lock};
+        for (auto& process : process_list) {
+            process->Terminate();
+            process->Close();
+            process = nullptr;
+        }
+        process_list.clear();
     }
 
     void Shutdown() {
@@ -107,18 +113,9 @@ struct KernelCore::Impl {
 
         CloseServices();
 
-        auto* old_process = application_process.exchange(nullptr);
-        if (old_process) {
-            old_process->Close();
-        }
-
-        {
-            std::scoped_lock lk{process_list_lock};
-            for (auto* const process : process_list) {
-                process->Terminate();
-                process->Close();
-            }
-            process_list.clear();
+        if (application_process) {
+            application_process->Close();
+            application_process = nullptr;
         }
 
         next_object_id = 0;
@@ -354,6 +351,7 @@ struct KernelCore::Impl {
 
     void MakeApplicationProcess(KProcess* process) {
         application_process = process;
+        application_process->Open();
     }
 
     static inline thread_local u8 host_thread_id = UINT8_MAX;
@@ -779,7 +777,7 @@ struct KernelCore::Impl {
     // Lists all processes that exist in the current session.
     std::mutex process_list_lock;
     std::vector<KProcess*> process_list;
-    std::atomic<KProcess*> application_process{};
+    KProcess* application_process{};
     std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context;
     std::unique_ptr<Kernel::KHardwareTimer> hardware_timer;
 
@@ -1243,7 +1241,7 @@ void KernelCore::SuspendApplication(bool suspended) {
 }
 
 void KernelCore::ShutdownCores() {
-    impl->TerminateApplicationProcess();
+    impl->TerminateAllProcesses();
 
     KScopedSchedulerLock lk{*this};
 
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 46bc4f703..8f90eba34 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -3,7 +3,6 @@
 
 #include "core/hle/service/am/am.h"
 #include "core/hle/service/am/applet_ae.h"
-#include "core/hle/service/am/applet_message_queue.h"
 #include "core/hle/service/am/applet_oe.h"
 #include "core/hle/service/am/idle.h"
 #include "core/hle/service/am/omm.h"
@@ -13,13 +12,12 @@
 namespace Service::AM {
 
 void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system) {
-    auto message_queue = std::make_shared<AppletMessageQueue>(system);
     auto server_manager = std::make_unique<ServerManager>(system);
 
-    server_manager->RegisterNamedService(
-        "appletAE", std::make_shared<AppletAE>(nvnflinger, message_queue, system));
-    server_manager->RegisterNamedService(
-        "appletOE", std::make_shared<AppletOE>(nvnflinger, message_queue, system));
+    server_manager->RegisterNamedService("appletAE",
+                                         std::make_shared<AppletAE>(nvnflinger, system));
+    server_manager->RegisterNamedService("appletOE",
+                                         std::make_shared<AppletOE>(nvnflinger, system));
     server_manager->RegisterNamedService("idle:sys", std::make_shared<IdleSys>(system));
     server_manager->RegisterNamedService("omm", std::make_shared<OMM>(system));
     server_manager->RegisterNamedService("spsm", std::make_shared<SPSM>(system));
diff --git a/src/core/hle/service/am/am_results.h b/src/core/hle/service/am/am_results.h
index 528b334ad..e82d391ad 100644
--- a/src/core/hle/service/am/am_results.h
+++ b/src/core/hle/service/am/am_results.h
@@ -10,5 +10,6 @@ namespace Service::AM {
 constexpr Result ResultNoDataInChannel{ErrorModule::AM, 2};
 constexpr Result ResultNoMessages{ErrorModule::AM, 3};
 constexpr Result ResultInvalidOffset{ErrorModule::AM, 503};
+constexpr Result ResultFatalSectionCountImbalance{ErrorModule::AM, 512};
 
 } // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp
index bd9e5f505..1b715dea6 100644
--- a/src/core/hle/service/am/applet_ae.cpp
+++ b/src/core/hle/service/am/applet_ae.cpp
@@ -2,40 +2,15 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 
 #include "core/hle/service/am/applet_ae.h"
+#include "core/hle/service/am/applet_manager.h"
 #include "core/hle/service/am/library_applet_proxy.h"
 #include "core/hle/service/am/system_applet_proxy.h"
 #include "core/hle/service/ipc_helpers.h"
 
 namespace Service::AM {
 
-void AppletAE::OpenSystemAppletProxy(HLERequestContext& ctx) {
-    LOG_DEBUG(Service_AM, "called");
-
-    IPC::ResponseBuilder rb{ctx, 2, 0, 1};
-    rb.Push(ResultSuccess);
-    rb.PushIpcInterface<ISystemAppletProxy>(nvnflinger, msg_queue, system);
-}
-
-void AppletAE::OpenLibraryAppletProxy(HLERequestContext& ctx) {
-    LOG_DEBUG(Service_AM, "called");
-
-    IPC::ResponseBuilder rb{ctx, 2, 0, 1};
-    rb.Push(ResultSuccess);
-    rb.PushIpcInterface<ILibraryAppletProxy>(nvnflinger, msg_queue, system);
-}
-
-void AppletAE::OpenLibraryAppletProxyOld(HLERequestContext& ctx) {
-    LOG_DEBUG(Service_AM, "called");
-
-    IPC::ResponseBuilder rb{ctx, 2, 0, 1};
-    rb.Push(ResultSuccess);
-    rb.PushIpcInterface<ILibraryAppletProxy>(nvnflinger, msg_queue, system);
-}
-
-AppletAE::AppletAE(Nvnflinger::Nvnflinger& nvnflinger_,
-                   std::shared_ptr<AppletMessageQueue> msg_queue_, Core::System& system_)
-    : ServiceFramework{system_, "appletAE"}, nvnflinger{nvnflinger_},
-      msg_queue{std::move(msg_queue_)} {
+AppletAE::AppletAE(Nvnflinger::Nvnflinger& nvnflinger_, Core::System& system_)
+    : ServiceFramework{system_, "appletAE"}, nvnflinger{nvnflinger_} {
     // clang-format off
     static const FunctionInfo functions[] = {
         {100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"},
@@ -54,8 +29,45 @@ AppletAE::AppletAE(Nvnflinger::Nvnflinger& nvnflinger_,
 
 AppletAE::~AppletAE() = default;
 
-const std::shared_ptr<AppletMessageQueue>& AppletAE::GetMessageQueue() const {
-    return msg_queue;
+void AppletAE::OpenSystemAppletProxy(HLERequestContext& ctx) {
+    LOG_DEBUG(Service_AM, "called");
+
+    if (const auto applet = GetAppletFromContext(ctx)) {
+        IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+        rb.Push(ResultSuccess);
+        rb.PushIpcInterface<ISystemAppletProxy>(nvnflinger, applet, system);
+    } else {
+        UNIMPLEMENTED();
+
+        IPC::ResponseBuilder rb{ctx, 2};
+        rb.Push(ResultUnknown);
+    }
+}
+
+void AppletAE::OpenLibraryAppletProxy(HLERequestContext& ctx) {
+    LOG_DEBUG(Service_AM, "called");
+
+    if (const auto applet = GetAppletFromContext(ctx)) {
+        IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+        rb.Push(ResultSuccess);
+        rb.PushIpcInterface<ILibraryAppletProxy>(nvnflinger, applet, system);
+    } else {
+        UNIMPLEMENTED();
+
+        IPC::ResponseBuilder rb{ctx, 2};
+        rb.Push(ResultUnknown);
+    }
+}
+
+void AppletAE::OpenLibraryAppletProxyOld(HLERequestContext& ctx) {
+    LOG_DEBUG(Service_AM, "called");
+
+    return OpenLibraryAppletProxy(ctx);
+}
+
+std::shared_ptr<Applet> AppletAE::GetAppletFromContext(HLERequestContext& ctx) {
+    const auto aruid = ctx.GetPID();
+    return system.GetAppletManager().GetByAppletResourceUserId(aruid);
 }
 
 } // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h
index 538ce2903..3d7961fa1 100644
--- a/src/core/hle/service/am/applet_ae.h
+++ b/src/core/hle/service/am/applet_ae.h
@@ -18,23 +18,21 @@ class Nvnflinger;
 
 namespace AM {
 
-class AppletMessageQueue;
+struct Applet;
 
 class AppletAE final : public ServiceFramework<AppletAE> {
 public:
-    explicit AppletAE(Nvnflinger::Nvnflinger& nvnflinger_,
-                      std::shared_ptr<AppletMessageQueue> msg_queue_, Core::System& system_);
+    explicit AppletAE(Nvnflinger::Nvnflinger& nvnflinger_, Core::System& system_);
     ~AppletAE() override;
 
-    const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const;
-
 private:
     void OpenSystemAppletProxy(HLERequestContext& ctx);
     void OpenLibraryAppletProxy(HLERequestContext& ctx);
     void OpenLibraryAppletProxyOld(HLERequestContext& ctx);
 
+    std::shared_ptr<Applet> GetAppletFromContext(HLERequestContext& ctx);
+
     Nvnflinger::Nvnflinger& nvnflinger;
-    std::shared_ptr<AppletMessageQueue> msg_queue;
 };
 
 } // namespace AM
diff --git a/src/core/hle/service/am/applet_common_functions.cpp b/src/core/hle/service/am/applet_common_functions.cpp
index 81c01a48b..a5c54ce87 100644
--- a/src/core/hle/service/am/applet_common_functions.cpp
+++ b/src/core/hle/service/am/applet_common_functions.cpp
@@ -1,13 +1,15 @@
 // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
 // SPDX-License-Identifier: GPL-2.0-or-later
 
+#include "core/hle/service/am/applet.h"
 #include "core/hle/service/am/applet_common_functions.h"
 #include "core/hle/service/ipc_helpers.h"
 
 namespace Service::AM {
 
-IAppletCommonFunctions::IAppletCommonFunctions(Core::System& system_)
-    : ServiceFramework{system_, "IAppletCommonFunctions"} {
+IAppletCommonFunctions::IAppletCommonFunctions(Core::System& system_,
+                                               std::shared_ptr<Applet> applet_)
+    : ServiceFramework{system_, "IAppletCommonFunctions"}, applet{std::move(applet_)} {
     // clang-format off
     static const FunctionInfo functions[] = {
         {0, nullptr, "SetTerminateResult"},
@@ -40,6 +42,11 @@ IAppletCommonFunctions::~IAppletCommonFunctions() = default;
 void IAppletCommonFunctions::SetCpuBoostRequestPriority(HLERequestContext& ctx) {
     LOG_WARNING(Service_AM, "(STUBBED) called");
 
+    IPC::RequestParser rp{ctx};
+
+    std::scoped_lock lk{applet->lock};
+    applet->cpu_boost_request_priority = rp.Pop<s32>();
+
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
 }
diff --git a/src/core/hle/service/am/applet_common_functions.h b/src/core/hle/service/am/applet_common_functions.h
index be87b3820..229555669 100644
--- a/src/core/hle/service/am/applet_common_functions.h
+++ b/src/core/hle/service/am/applet_common_functions.h
@@ -7,13 +7,17 @@
 
 namespace Service::AM {
 
+struct Applet;
+
 class IAppletCommonFunctions final : public ServiceFramework<IAppletCommonFunctions> {
 public:
-    explicit IAppletCommonFunctions(Core::System& system_);
+    explicit IAppletCommonFunctions(Core::System& system_, std::shared_ptr<Applet> applet_);
     ~IAppletCommonFunctions() override;
 
 private:
     void SetCpuBoostRequestPriority(HLERequestContext& ctx);
+
+    const std::shared_ptr<Applet> applet;
 };
 
 } // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_manager.cpp b/src/core/hle/service/am/applet_manager.cpp
index 4aac5dba7..efbd0108c 100644
--- a/src/core/hle/service/am/applet_manager.cpp
+++ b/src/core/hle/service/am/applet_manager.cpp
@@ -294,8 +294,8 @@ void AppletManager::CreateAndInsertByFrontendAppletParameters(
     }
 
     // Applet was started by frontend, so it is foreground.
-    applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
     applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
+    applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
     applet->focus_state = FocusState::InFocus;
 
     this->InsertApplet(std::move(applet));
diff --git a/src/core/hle/service/am/applet_message_queue.cpp b/src/core/hle/service/am/applet_message_queue.cpp
index e4931031d..5ed996b70 100644
--- a/src/core/hle/service/am/applet_message_queue.cpp
+++ b/src/core/hle/service/am/applet_message_queue.cpp
@@ -26,11 +26,15 @@ Kernel::KReadableEvent& AppletMessageQueue::GetOperationModeChangedEvent() {
 }
 
 void AppletMessageQueue::PushMessage(AppletMessage msg) {
-    messages.push(msg);
+    {
+        std::scoped_lock lk{lock};
+        messages.push(msg);
+    }
     on_new_message->Signal();
 }
 
 AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() {
+    std::scoped_lock lk{lock};
     if (messages.empty()) {
         on_new_message->Clear();
         return AppletMessage::None;
@@ -44,6 +48,7 @@ AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() {
 }
 
 std::size_t AppletMessageQueue::GetMessageCount() const {
+    std::scoped_lock lk{lock};
     return messages.size();
 }
 
diff --git a/src/core/hle/service/am/applet_message_queue.h b/src/core/hle/service/am/applet_message_queue.h
index 60145aae9..5cb236d47 100644
--- a/src/core/hle/service/am/applet_message_queue.h
+++ b/src/core/hle/service/am/applet_message_queue.h
@@ -69,6 +69,7 @@ private:
     Kernel::KEvent* on_new_message;
     Kernel::KEvent* on_operation_mode_changed;
 
+    mutable std::mutex lock;
     std::queue<AppletMessage> messages;
 };
 
diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp
index f373d1136..56bafd162 100644
--- a/src/core/hle/service/am/applet_oe.cpp
+++ b/src/core/hle/service/am/applet_oe.cpp
@@ -2,24 +2,15 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 
 #include "core/hle/service/am/am.h"
+#include "core/hle/service/am/applet_manager.h"
 #include "core/hle/service/am/applet_oe.h"
 #include "core/hle/service/am/application_proxy.h"
 #include "core/hle/service/ipc_helpers.h"
 
 namespace Service::AM {
 
-void AppletOE::OpenApplicationProxy(HLERequestContext& ctx) {
-    LOG_DEBUG(Service_AM, "called");
-
-    IPC::ResponseBuilder rb{ctx, 2, 0, 1};
-    rb.Push(ResultSuccess);
-    rb.PushIpcInterface<IApplicationProxy>(nvnflinger, msg_queue, system);
-}
-
-AppletOE::AppletOE(Nvnflinger::Nvnflinger& nvnflinger_,
-                   std::shared_ptr<AppletMessageQueue> msg_queue_, Core::System& system_)
-    : ServiceFramework{system_, "appletOE"}, nvnflinger{nvnflinger_},
-      msg_queue{std::move(msg_queue_)} {
+AppletOE::AppletOE(Nvnflinger::Nvnflinger& nvnflinger_, Core::System& system_)
+    : ServiceFramework{system_, "appletOE"}, nvnflinger{nvnflinger_} {
     static const FunctionInfo functions[] = {
         {0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"},
     };
@@ -28,8 +19,24 @@ AppletOE::AppletOE(Nvnflinger::Nvnflinger& nvnflinger_,
 
 AppletOE::~AppletOE() = default;
 
-const std::shared_ptr<AppletMessageQueue>& AppletOE::GetMessageQueue() const {
-    return msg_queue;
+void AppletOE::OpenApplicationProxy(HLERequestContext& ctx) {
+    LOG_DEBUG(Service_AM, "called");
+
+    if (const auto applet = GetAppletFromContext(ctx)) {
+        IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+        rb.Push(ResultSuccess);
+        rb.PushIpcInterface<IApplicationProxy>(nvnflinger, applet, system);
+    } else {
+        UNIMPLEMENTED();
+
+        IPC::ResponseBuilder rb{ctx, 2};
+        rb.Push(ResultUnknown);
+    }
+}
+
+std::shared_ptr<Applet> AppletOE::GetAppletFromContext(HLERequestContext& ctx) {
+    const auto aruid = ctx.GetPID();
+    return system.GetAppletManager().GetByAppletResourceUserId(aruid);
 }
 
 } // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h
index 39eccc4ab..f2ba1c924 100644
--- a/src/core/hle/service/am/applet_oe.h
+++ b/src/core/hle/service/am/applet_oe.h
@@ -18,21 +18,19 @@ class Nvnflinger;
 
 namespace AM {
 
-class AppletMessageQueue;
+struct Applet;
 
 class AppletOE final : public ServiceFramework<AppletOE> {
 public:
-    explicit AppletOE(Nvnflinger::Nvnflinger& nvnflinger_,
-                      std::shared_ptr<AppletMessageQueue> msg_queue_, Core::System& system_);
+    explicit AppletOE(Nvnflinger::Nvnflinger& nvnflinger_, Core::System& system_);
     ~AppletOE() override;
 
-    const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const;
-
 private:
     void OpenApplicationProxy(HLERequestContext& ctx);
 
+    std::shared_ptr<Applet> GetAppletFromContext(HLERequestContext& ctx);
+
     Nvnflinger::Nvnflinger& nvnflinger;
-    std::shared_ptr<AppletMessageQueue> msg_queue;
 };
 
 } // namespace AM
diff --git a/src/core/hle/service/am/application_functions.cpp b/src/core/hle/service/am/application_functions.cpp
index fef45c732..51c5be2d1 100644
--- a/src/core/hle/service/am/application_functions.cpp
+++ b/src/core/hle/service/am/application_functions.cpp
@@ -9,6 +9,7 @@
 #include "core/file_sys/savedata_factory.h"
 #include "core/hle/service/acc/profile_manager.h"
 #include "core/hle/service/am/am_results.h"
+#include "core/hle/service/am/applet.h"
 #include "core/hle/service/am/application_functions.h"
 #include "core/hle/service/am/storage.h"
 #include "core/hle/service/filesystem/filesystem.h"
@@ -24,19 +25,8 @@ enum class LaunchParameterKind : u32 {
     AccountPreselectedUser = 2,
 };
 
-constexpr u32 LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC = 0xC79497CA;
-
-struct LaunchParameterAccountPreselectedUser {
-    u32_le magic;
-    u32_le is_account_selected;
-    Common::UUID current_user;
-    INSERT_PADDING_BYTES(0x70);
-};
-static_assert(sizeof(LaunchParameterAccountPreselectedUser) == 0x88);
-
-IApplicationFunctions::IApplicationFunctions(Core::System& system_)
-    : ServiceFramework{system_, "IApplicationFunctions"},
-      service_context{system, "IApplicationFunctions"} {
+IApplicationFunctions::IApplicationFunctions(Core::System& system_, std::shared_ptr<Applet> applet_)
+    : ServiceFramework{system_, "IApplicationFunctions"}, applet{std::move(applet_)} {
     // clang-format off
     static const FunctionInfo functions[] = {
         {1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"},
@@ -105,27 +95,16 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
     // clang-format on
 
     RegisterHandlers(functions);
-
-    gpu_error_detected_event =
-        service_context.CreateEvent("IApplicationFunctions:GpuErrorDetectedSystemEvent");
-    friend_invitation_storage_channel_event =
-        service_context.CreateEvent("IApplicationFunctions:FriendInvitationStorageChannelEvent");
-    notification_storage_channel_event =
-        service_context.CreateEvent("IApplicationFunctions:NotificationStorageChannelEvent");
-    health_warning_disappeared_system_event =
-        service_context.CreateEvent("IApplicationFunctions:HealthWarningDisappearedSystemEvent");
 }
 
-IApplicationFunctions::~IApplicationFunctions() {
-    service_context.CloseEvent(gpu_error_detected_event);
-    service_context.CloseEvent(friend_invitation_storage_channel_event);
-    service_context.CloseEvent(notification_storage_channel_event);
-    service_context.CloseEvent(health_warning_disappeared_system_event);
-}
+IApplicationFunctions::~IApplicationFunctions() = default;
 
 void IApplicationFunctions::EnableApplicationCrashReport(HLERequestContext& ctx) {
     LOG_WARNING(Service_AM, "(STUBBED) called");
 
+    std::scoped_lock lk{applet->lock};
+    applet->application_crash_report_enabled = true;
+
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
 }
@@ -157,6 +136,10 @@ void IApplicationFunctions::SetApplicationCopyrightVisibility(HLERequestContext&
 void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx) {
     LOG_WARNING(Service_AM, "(STUBBED) called");
 
+    std::scoped_lock lk{applet->lock};
+    applet->home_button_long_pressed_blocked = true;
+    applet->home_button_short_pressed_blocked = true;
+
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
 }
@@ -164,6 +147,10 @@ void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(HLEReques
 void IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed(HLERequestContext& ctx) {
     LOG_WARNING(Service_AM, "(STUBBED) called");
 
+    std::scoped_lock lk{applet->lock};
+    applet->home_button_long_pressed_blocked = false;
+    applet->home_button_short_pressed_blocked = false;
+
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
 }
@@ -171,6 +158,11 @@ void IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed(HLERequestC
 void IApplicationFunctions::BeginBlockingHomeButton(HLERequestContext& ctx) {
     LOG_WARNING(Service_AM, "(STUBBED) called");
 
+    std::scoped_lock lk{applet->lock};
+    applet->home_button_long_pressed_blocked = true;
+    applet->home_button_short_pressed_blocked = true;
+    applet->home_button_double_click_enabled = true;
+
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
 }
@@ -178,6 +170,11 @@ void IApplicationFunctions::BeginBlockingHomeButton(HLERequestContext& ctx) {
 void IApplicationFunctions::EndBlockingHomeButton(HLERequestContext& ctx) {
     LOG_WARNING(Service_AM, "(STUBBED) called");
 
+    std::scoped_lock lk{applet->lock};
+    applet->home_button_long_pressed_blocked = false;
+    applet->home_button_short_pressed_blocked = false;
+    applet->home_button_double_click_enabled = false;
+
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
 }
@@ -188,47 +185,25 @@ void IApplicationFunctions::PopLaunchParameter(HLERequestContext& ctx) {
 
     LOG_INFO(Service_AM, "called, kind={:08X}", kind);
 
-    if (kind == LaunchParameterKind::UserChannel) {
-        auto channel = system.GetUserChannel();
-        if (channel.empty()) {
-            LOG_ERROR(Service_AM, "Attempted to load launch parameter but none was found!");
-            IPC::ResponseBuilder rb{ctx, 2};
-            rb.Push(AM::ResultNoDataInChannel);
-            return;
-        }
+    std::scoped_lock lk{applet->lock};
 
-        auto data = channel.back();
-        channel.pop_back();
+    auto& channel = kind == LaunchParameterKind::UserChannel
+                        ? applet->user_channel_launch_parameter
+                        : applet->preselected_user_launch_parameter;
 
-        IPC::ResponseBuilder rb{ctx, 2, 0, 1};
-        rb.Push(ResultSuccess);
-        rb.PushIpcInterface<IStorage>(system, std::move(data));
-    } else if (kind == LaunchParameterKind::AccountPreselectedUser &&
-               !launch_popped_account_preselect) {
-        // TODO: Verify this is hw-accurate
-        LaunchParameterAccountPreselectedUser params{};
-
-        params.magic = LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC;
-        params.is_account_selected = 1;
-
-        Account::ProfileManager profile_manager{};
-        const auto uuid = profile_manager.GetUser(static_cast<s32>(Settings::values.current_user));
-        ASSERT(uuid.has_value() && uuid->IsValid());
-        params.current_user = *uuid;
-
-        IPC::ResponseBuilder rb{ctx, 2, 0, 1};
-        rb.Push(ResultSuccess);
-
-        std::vector<u8> buffer(sizeof(LaunchParameterAccountPreselectedUser));
-        std::memcpy(buffer.data(), &params, buffer.size());
-
-        rb.PushIpcInterface<IStorage>(system, std::move(buffer));
-        launch_popped_account_preselect = true;
-    } else {
-        LOG_ERROR(Service_AM, "Unknown launch parameter kind.");
+    if (channel.empty()) {
+        LOG_WARNING(Service_AM, "Attempted to pop parameter {} but none was found!", kind);
         IPC::ResponseBuilder rb{ctx, 2};
         rb.Push(AM::ResultNoDataInChannel);
+        return;
     }
+
+    auto data = channel.back();
+    channel.pop_back();
+
+    IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+    rb.Push(ResultSuccess);
+    rb.PushIpcInterface<IStorage>(system, std::move(data));
 }
 
 void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(HLERequestContext& ctx) {
@@ -245,7 +220,7 @@ void IApplicationFunctions::EnsureSaveData(HLERequestContext& ctx) {
     LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]);
 
     FileSys::SaveDataAttribute attribute{};
-    attribute.title_id = system.GetApplicationProcessProgramID();
+    attribute.title_id = applet->program_id;
     attribute.user_id = user_id;
     attribute.type = FileSys::SaveDataType::SaveData;
 
@@ -267,6 +242,9 @@ void IApplicationFunctions::SetTerminateResult(HLERequestContext& ctx) {
     u32 result = rp.Pop<u32>();
     LOG_WARNING(Service_AM, "(STUBBED) called, result=0x{:08X}", result);
 
+    std::scoped_lock lk{applet->lock};
+    applet->terminate_result = Result(result);
+
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
 }
@@ -277,16 +255,14 @@ void IApplicationFunctions::GetDisplayVersion(HLERequestContext& ctx) {
     std::array<u8, 0x10> version_string{};
 
     const auto res = [this] {
-        const auto title_id = system.GetApplicationProcessProgramID();
-
-        const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
+        const FileSys::PatchManager pm{applet->program_id, system.GetFileSystemController(),
                                        system.GetContentProvider()};
         auto metadata = pm.GetControlMetadata();
         if (metadata.first != nullptr) {
             return metadata;
         }
 
-        const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id),
+        const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(applet->program_id),
                                               system.GetFileSystemController(),
                                               system.GetContentProvider()};
         return pm_update.GetControlMetadata();
@@ -314,16 +290,14 @@ void IApplicationFunctions::GetDesiredLanguage(HLERequestContext& ctx) {
     u32 supported_languages = 0;
 
     const auto res = [this] {
-        const auto title_id = system.GetApplicationProcessProgramID();
-
-        const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
+        const FileSys::PatchManager pm{applet->program_id, system.GetFileSystemController(),
                                        system.GetContentProvider()};
         auto metadata = pm.GetControlMetadata();
         if (metadata.first != nullptr) {
             return metadata;
         }
 
-        const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id),
+        const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(applet->program_id),
                                               system.GetFileSystemController(),
                                               system.GetContentProvider()};
         return pm_update.GetControlMetadata();
@@ -368,11 +342,9 @@ void IApplicationFunctions::GetDesiredLanguage(HLERequestContext& ctx) {
 void IApplicationFunctions::IsGamePlayRecordingSupported(HLERequestContext& ctx) {
     LOG_WARNING(Service_AM, "(STUBBED) called");
 
-    constexpr bool gameplay_recording_supported = false;
-
     IPC::ResponseBuilder rb{ctx, 3};
     rb.Push(ResultSuccess);
-    rb.Push(gameplay_recording_supported);
+    rb.Push(applet->gameplay_recording_supported);
 }
 
 void IApplicationFunctions::InitializeGamePlayRecording(HLERequestContext& ctx) {
@@ -385,6 +357,11 @@ void IApplicationFunctions::InitializeGamePlayRecording(HLERequestContext& ctx)
 void IApplicationFunctions::SetGamePlayRecordingState(HLERequestContext& ctx) {
     LOG_WARNING(Service_AM, "(STUBBED) called");
 
+    IPC::RequestParser rp{ctx};
+
+    std::scoped_lock lk{applet->lock};
+    applet->gameplay_recording_state = rp.PopRaw<GameplayRecordingState>();
+
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
 }
@@ -392,6 +369,9 @@ void IApplicationFunctions::SetGamePlayRecordingState(HLERequestContext& ctx) {
 void IApplicationFunctions::NotifyRunning(HLERequestContext& ctx) {
     LOG_WARNING(Service_AM, "(STUBBED) called");
 
+    std::scoped_lock lk{applet->lock};
+    applet->is_running = true;
+
     IPC::ResponseBuilder rb{ctx, 3};
     rb.Push(ResultSuccess);
     rb.Push<u8>(0); // Unknown, seems to be ignored by official processes
@@ -426,8 +406,7 @@ void IApplicationFunctions::ExtendSaveData(HLERequestContext& ctx) {
               static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size);
 
     system.GetFileSystemController().OpenSaveDataController()->WriteSaveDataSize(
-        type, system.GetApplicationProcessProgramID(), user_id,
-        {new_normal_size, new_journal_size});
+        type, applet->program_id, user_id, {new_normal_size, new_journal_size});
 
     IPC::ResponseBuilder rb{ctx, 4};
     rb.Push(ResultSuccess);
@@ -451,7 +430,7 @@ void IApplicationFunctions::GetSaveDataSize(HLERequestContext& ctx) {
               user_id[0]);
 
     const auto size = system.GetFileSystemController().OpenSaveDataController()->ReadSaveDataSize(
-        type, system.GetApplicationProcessProgramID(), user_id);
+        type, applet->program_id, user_id);
 
     IPC::ResponseBuilder rb{ctx, 6};
     rb.Push(ResultSuccess);
@@ -528,13 +507,15 @@ void IApplicationFunctions::ExecuteProgram(HLERequestContext& ctx) {
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
 
+    // Swap user channel ownership into the system so that it will be preserved
+    system.GetUserChannel().swap(applet->user_channel_launch_parameter);
     system.ExecuteProgram(program_index);
 }
 
 void IApplicationFunctions::ClearUserChannel(HLERequestContext& ctx) {
     LOG_DEBUG(Service_AM, "called");
 
-    system.GetUserChannel().clear();
+    applet->user_channel_launch_parameter.clear();
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
@@ -546,7 +527,7 @@ void IApplicationFunctions::UnpopToUserChannel(HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     const auto storage = rp.PopIpcInterface<IStorage>().lock();
     if (storage) {
-        system.GetUserChannel().push_back(storage->GetData());
+        applet->user_channel_launch_parameter.push_back(storage->GetData());
     }
 
     IPC::ResponseBuilder rb{ctx, 2};
@@ -558,7 +539,7 @@ void IApplicationFunctions::GetPreviousProgramIndex(HLERequestContext& ctx) {
 
     IPC::ResponseBuilder rb{ctx, 3};
     rb.Push(ResultSuccess);
-    rb.Push<s32>(previous_program_index);
+    rb.Push<s32>(applet->previous_program_index);
 }
 
 void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(HLERequestContext& ctx) {
@@ -566,7 +547,7 @@ void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(HLERequestContext& ct
 
     IPC::ResponseBuilder rb{ctx, 2, 1};
     rb.Push(ResultSuccess);
-    rb.PushCopyObjects(gpu_error_detected_event->GetReadableEvent());
+    rb.PushCopyObjects(applet->gpu_error_detected_event.GetHandle());
 }
 
 void IApplicationFunctions::GetFriendInvitationStorageChannelEvent(HLERequestContext& ctx) {
@@ -574,7 +555,7 @@ void IApplicationFunctions::GetFriendInvitationStorageChannelEvent(HLERequestCon
 
     IPC::ResponseBuilder rb{ctx, 2, 1};
     rb.Push(ResultSuccess);
-    rb.PushCopyObjects(friend_invitation_storage_channel_event->GetReadableEvent());
+    rb.PushCopyObjects(applet->friend_invitation_storage_channel_event.GetHandle());
 }
 
 void IApplicationFunctions::TryPopFromFriendInvitationStorageChannel(HLERequestContext& ctx) {
@@ -589,7 +570,7 @@ void IApplicationFunctions::GetNotificationStorageChannelEvent(HLERequestContext
 
     IPC::ResponseBuilder rb{ctx, 2, 1};
     rb.Push(ResultSuccess);
-    rb.PushCopyObjects(notification_storage_channel_event->GetReadableEvent());
+    rb.PushCopyObjects(applet->notification_storage_channel_event.GetHandle());
 }
 
 void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(HLERequestContext& ctx) {
@@ -597,12 +578,15 @@ void IApplicationFunctions::GetHealthWarningDisappearedSystemEvent(HLERequestCon
 
     IPC::ResponseBuilder rb{ctx, 2, 1};
     rb.Push(ResultSuccess);
-    rb.PushCopyObjects(health_warning_disappeared_system_event->GetReadableEvent());
+    rb.PushCopyObjects(applet->health_warning_disappeared_system_event.GetHandle());
 }
 
 void IApplicationFunctions::PrepareForJit(HLERequestContext& ctx) {
     LOG_WARNING(Service_AM, "(STUBBED) called");
 
+    std::scoped_lock lk{applet->lock};
+    applet->jit_service_launched = true;
+
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
 }
diff --git a/src/core/hle/service/am/application_functions.h b/src/core/hle/service/am/application_functions.h
index 22aab1c8f..55eb21d39 100644
--- a/src/core/hle/service/am/application_functions.h
+++ b/src/core/hle/service/am/application_functions.h
@@ -8,9 +8,11 @@
 
 namespace Service::AM {
 
+struct Applet;
+
 class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
 public:
-    explicit IApplicationFunctions(Core::System& system_);
+    explicit IApplicationFunctions(Core::System& system_, std::shared_ptr<Applet> applet_);
     ~IApplicationFunctions() override;
 
 private:
@@ -50,14 +52,7 @@ private:
     void GetHealthWarningDisappearedSystemEvent(HLERequestContext& ctx);
     void PrepareForJit(HLERequestContext& ctx);
 
-    KernelHelpers::ServiceContext service_context;
-
-    bool launch_popped_account_preselect = false;
-    s32 previous_program_index{-1};
-    Kernel::KEvent* gpu_error_detected_event;
-    Kernel::KEvent* friend_invitation_storage_channel_event;
-    Kernel::KEvent* notification_storage_channel_event;
-    Kernel::KEvent* health_warning_disappeared_system_event;
+    const std::shared_ptr<Applet> applet;
 };
 
 } // namespace Service::AM
diff --git a/src/core/hle/service/am/application_proxy.cpp b/src/core/hle/service/am/application_proxy.cpp
index e9cd0aa71..99e97f4bc 100644
--- a/src/core/hle/service/am/application_proxy.cpp
+++ b/src/core/hle/service/am/application_proxy.cpp
@@ -18,10 +18,9 @@
 namespace Service::AM {
 
 IApplicationProxy::IApplicationProxy(Nvnflinger::Nvnflinger& nvnflinger_,
-                                     std::shared_ptr<AppletMessageQueue> msg_queue_,
-                                     Core::System& system_)
-    : ServiceFramework{system_, "IApplicationProxy"}, nvnflinger{nvnflinger_},
-      msg_queue{std::move(msg_queue_)} {
+                                     std::shared_ptr<Applet> applet_, Core::System& system_)
+    : ServiceFramework{system_, "IApplicationProxy"}, nvnflinger{nvnflinger_}, applet{std::move(
+                                                                                   applet_)} {
     // clang-format off
     static const FunctionInfo functions[] = {
         {0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"},
@@ -39,6 +38,8 @@ IApplicationProxy::IApplicationProxy(Nvnflinger::Nvnflinger& nvnflinger_,
     RegisterHandlers(functions);
 }
 
+IApplicationProxy::~IApplicationProxy() = default;
+
 void IApplicationProxy::GetAudioController(HLERequestContext& ctx) {
     LOG_DEBUG(Service_AM, "called");
 
@@ -60,7 +61,7 @@ void IApplicationProxy::GetProcessWindingController(HLERequestContext& ctx) {
 
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     rb.Push(ResultSuccess);
-    rb.PushIpcInterface<IProcessWindingController>(system);
+    rb.PushIpcInterface<IProcessWindingController>(system, applet);
 }
 
 void IApplicationProxy::GetDebugFunctions(HLERequestContext& ctx) {
@@ -76,7 +77,7 @@ void IApplicationProxy::GetWindowController(HLERequestContext& ctx) {
 
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     rb.Push(ResultSuccess);
-    rb.PushIpcInterface<IWindowController>(system);
+    rb.PushIpcInterface<IWindowController>(system, applet);
 }
 
 void IApplicationProxy::GetSelfController(HLERequestContext& ctx) {
@@ -84,7 +85,7 @@ void IApplicationProxy::GetSelfController(HLERequestContext& ctx) {
 
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     rb.Push(ResultSuccess);
-    rb.PushIpcInterface<ISelfController>(system, nvnflinger);
+    rb.PushIpcInterface<ISelfController>(system, applet, nvnflinger);
 }
 
 void IApplicationProxy::GetCommonStateGetter(HLERequestContext& ctx) {
@@ -92,7 +93,7 @@ void IApplicationProxy::GetCommonStateGetter(HLERequestContext& ctx) {
 
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     rb.Push(ResultSuccess);
-    rb.PushIpcInterface<ICommonStateGetter>(system, msg_queue);
+    rb.PushIpcInterface<ICommonStateGetter>(system, applet);
 }
 
 void IApplicationProxy::GetLibraryAppletCreator(HLERequestContext& ctx) {
@@ -100,7 +101,7 @@ void IApplicationProxy::GetLibraryAppletCreator(HLERequestContext& ctx) {
 
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     rb.Push(ResultSuccess);
-    rb.PushIpcInterface<ILibraryAppletCreator>(system);
+    rb.PushIpcInterface<ILibraryAppletCreator>(system, applet);
 }
 
 void IApplicationProxy::GetApplicationFunctions(HLERequestContext& ctx) {
@@ -108,7 +109,7 @@ void IApplicationProxy::GetApplicationFunctions(HLERequestContext& ctx) {
 
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     rb.Push(ResultSuccess);
-    rb.PushIpcInterface<IApplicationFunctions>(system);
+    rb.PushIpcInterface<IApplicationFunctions>(system, applet);
 }
 
 } // namespace Service::AM
diff --git a/src/core/hle/service/am/application_proxy.h b/src/core/hle/service/am/application_proxy.h
index 4f620242b..eb98b095c 100644
--- a/src/core/hle/service/am/application_proxy.h
+++ b/src/core/hle/service/am/application_proxy.h
@@ -3,16 +3,17 @@
 
 #pragma once
 
-#include "core/hle/service/am/applet_message_queue.h"
 #include "core/hle/service/service.h"
 
 namespace Service::AM {
 
+struct Applet;
+
 class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
 public:
     explicit IApplicationProxy(Nvnflinger::Nvnflinger& nvnflinger_,
-                               std::shared_ptr<AppletMessageQueue> msg_queue_,
-                               Core::System& system_);
+                               std::shared_ptr<Applet> msg_queue_, Core::System& system_);
+    ~IApplicationProxy();
 
 private:
     void GetAudioController(HLERequestContext& ctx);
@@ -26,7 +27,7 @@ private:
     void GetApplicationFunctions(HLERequestContext& ctx);
 
     Nvnflinger::Nvnflinger& nvnflinger;
-    std::shared_ptr<AppletMessageQueue> msg_queue;
+    std::shared_ptr<Applet> applet;
 };
 
 } // namespace Service::AM
diff --git a/src/core/hle/service/am/common_state_getter.cpp b/src/core/hle/service/am/common_state_getter.cpp
index 0b54b769d..77f3fd868 100644
--- a/src/core/hle/service/am/common_state_getter.cpp
+++ b/src/core/hle/service/am/common_state_getter.cpp
@@ -3,6 +3,7 @@
 
 #include "common/settings.h"
 #include "core/hle/service/am/am_results.h"
+#include "core/hle/service/am/applet.h"
 #include "core/hle/service/am/common_state_getter.h"
 #include "core/hle/service/am/lock_accessor.h"
 #include "core/hle/service/apm/apm_controller.h"
@@ -14,10 +15,8 @@
 
 namespace Service::AM {
 
-ICommonStateGetter::ICommonStateGetter(Core::System& system_,
-                                       std::shared_ptr<AppletMessageQueue> msg_queue_)
-    : ServiceFramework{system_, "ICommonStateGetter"}, msg_queue{std::move(msg_queue_)},
-      service_context{system_, "ICommonStateGetter"} {
+ICommonStateGetter::ICommonStateGetter(Core::System& system_, std::shared_ptr<Applet> applet_)
+    : ServiceFramework{system_, "ICommonStateGetter"}, applet{std::move(applet_)} {
     // clang-format off
     static const FunctionInfo functions[] = {
         {0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"},
@@ -75,17 +74,9 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_,
     // clang-format on
 
     RegisterHandlers(functions);
-
-    sleep_lock_event = service_context.CreateEvent("ICommonStateGetter::SleepLockEvent");
-
-    // Configure applets to be in foreground state
-    msg_queue->PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
-    msg_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
 }
 
-ICommonStateGetter::~ICommonStateGetter() {
-    service_context.CloseEvent(sleep_lock_event);
-};
+ICommonStateGetter::~ICommonStateGetter() = default;
 
 void ICommonStateGetter::GetBootMode(HLERequestContext& ctx) {
     LOG_DEBUG(Service_AM, "called");
@@ -96,17 +87,17 @@ void ICommonStateGetter::GetBootMode(HLERequestContext& ctx) {
 }
 
 void ICommonStateGetter::GetEventHandle(HLERequestContext& ctx) {
-    LOG_DEBUG(Service_AM, "called");
+    LOG_DEBUG(Service_AM, "(STUBBED) called");
 
     IPC::ResponseBuilder rb{ctx, 2, 1};
     rb.Push(ResultSuccess);
-    rb.PushCopyObjects(msg_queue->GetMessageReceiveEvent());
+    rb.PushCopyObjects(applet->message_queue.GetMessageReceiveEvent());
 }
 
 void ICommonStateGetter::ReceiveMessage(HLERequestContext& ctx) {
     LOG_DEBUG(Service_AM, "called");
 
-    const auto message = msg_queue->PopMessage();
+    const auto message = applet->message_queue.PopMessage();
     IPC::ResponseBuilder rb{ctx, 3};
 
     if (message == AppletMessageQueue::AppletMessage::None) {
@@ -123,9 +114,11 @@ void ICommonStateGetter::ReceiveMessage(HLERequestContext& ctx) {
 void ICommonStateGetter::GetCurrentFocusState(HLERequestContext& ctx) {
     LOG_DEBUG(Service_AM, "(STUBBED) called");
 
+    std::scoped_lock lk{applet->lock};
+
     IPC::ResponseBuilder rb{ctx, 3};
     rb.Push(ResultSuccess);
-    rb.Push(static_cast<u8>(FocusState::InFocus));
+    rb.Push(static_cast<u8>(applet->focus_state));
 }
 
 void ICommonStateGetter::GetOperationMode(HLERequestContext& ctx) {
@@ -149,7 +142,7 @@ void ICommonStateGetter::RequestToAcquireSleepLock(HLERequestContext& ctx) {
     LOG_WARNING(Service_AM, "(STUBBED) called");
 
     // Sleep lock is acquired immediately.
-    sleep_lock_event->Signal();
+    applet->sleep_lock_event.Signal();
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
@@ -172,22 +165,25 @@ void ICommonStateGetter::GetAcquiredSleepLockEvent(HLERequestContext& ctx) {
 
     IPC::ResponseBuilder rb{ctx, 2, 1};
     rb.Push(ResultSuccess);
-    rb.PushCopyObjects(sleep_lock_event->GetReadableEvent());
+    rb.PushCopyObjects(applet->sleep_lock_event.GetHandle());
 }
 
 void ICommonStateGetter::IsVrModeEnabled(HLERequestContext& ctx) {
     LOG_DEBUG(Service_AM, "called");
 
+    std::scoped_lock lk{applet->lock};
+
     IPC::ResponseBuilder rb{ctx, 3};
     rb.Push(ResultSuccess);
-    rb.Push(vr_mode_state);
+    rb.Push(applet->vr_mode_enabled);
 }
 
 void ICommonStateGetter::SetVrModeEnabled(HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
-    vr_mode_state = rp.Pop<bool>();
 
-    LOG_WARNING(Service_AM, "VR Mode is {}", vr_mode_state ? "on" : "off");
+    std::scoped_lock lk{applet->lock};
+    applet->vr_mode_enabled = rp.Pop<bool>();
+    LOG_WARNING(Service_AM, "VR Mode is {}", applet->vr_mode_enabled ? "on" : "off");
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
@@ -207,6 +203,9 @@ void ICommonStateGetter::SetLcdBacklighOffEnabled(HLERequestContext& ctx) {
 void ICommonStateGetter::BeginVrModeEx(HLERequestContext& ctx) {
     LOG_WARNING(Service_AM, "(STUBBED) called");
 
+    std::scoped_lock lk{applet->lock};
+    applet->vr_mode_enabled = true;
+
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
 }
@@ -214,6 +213,9 @@ void ICommonStateGetter::BeginVrModeEx(HLERequestContext& ctx) {
 void ICommonStateGetter::EndVrModeEx(HLERequestContext& ctx) {
     LOG_WARNING(Service_AM, "(STUBBED) called");
 
+    std::scoped_lock lk{applet->lock};
+    applet->vr_mode_enabled = false;
+
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
 }
@@ -223,7 +225,7 @@ void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(HLERequestContex
 
     IPC::ResponseBuilder rb{ctx, 2, 1};
     rb.Push(ResultSuccess);
-    rb.PushCopyObjects(msg_queue->GetOperationModeChangedEvent());
+    rb.PushCopyObjects(applet->message_queue.GetOperationModeChangedEvent());
 }
 
 void ICommonStateGetter::GetDefaultDisplayResolution(HLERequestContext& ctx) {
@@ -281,6 +283,9 @@ void ICommonStateGetter::SetRequestExitToLibraryAppletAtExecuteNextProgramEnable
     HLERequestContext& ctx) {
     LOG_WARNING(Service_AM, "(STUBBED) called");
 
+    std::scoped_lock lk{applet->lock};
+    applet->request_exit_to_library_applet_at_execute_next_program_enabled = true;
+
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
 }
diff --git a/src/core/hle/service/am/common_state_getter.h b/src/core/hle/service/am/common_state_getter.h
index 11d66f10a..643ca4dc5 100644
--- a/src/core/hle/service/am/common_state_getter.h
+++ b/src/core/hle/service/am/common_state_getter.h
@@ -10,10 +10,11 @@
 
 namespace Service::AM {
 
+struct Applet;
+
 class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
 public:
-    explicit ICommonStateGetter(Core::System& system_,
-                                std::shared_ptr<AppletMessageQueue> msg_queue_);
+    explicit ICommonStateGetter(Core::System& system_, std::shared_ptr<Applet> applet_);
     ~ICommonStateGetter() override;
 
 private:
@@ -69,10 +70,7 @@ private:
     void GetSettingsPlatformRegion(HLERequestContext& ctx);
     void SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(HLERequestContext& ctx);
 
-    std::shared_ptr<AppletMessageQueue> msg_queue;
-    bool vr_mode_state{};
-    Kernel::KEvent* sleep_lock_event;
-    KernelHelpers::ServiceContext service_context;
+    const std::shared_ptr<Applet> applet;
 };
 
 } // namespace Service::AM
diff --git a/src/core/hle/service/am/frontend/applets.cpp b/src/core/hle/service/am/frontend/applets.cpp
index 4e8f806d7..38495ee19 100644
--- a/src/core/hle/service/am/frontend/applets.cpp
+++ b/src/core/hle/service/am/frontend/applets.cpp
@@ -16,6 +16,7 @@
 #include "core/hle/kernel/k_event.h"
 #include "core/hle/service/am/am.h"
 #include "core/hle/service/am/applet_ae.h"
+#include "core/hle/service/am/applet_manager.h"
 #include "core/hle/service/am/applet_message_queue.h"
 #include "core/hle/service/am/applet_oe.h"
 #include "core/hle/service/am/frontend/applet_cabinet.h"
@@ -122,21 +123,11 @@ void AppletDataBroker::PushInteractiveDataFromApplet(std::shared_ptr<IStorage>&&
 void AppletDataBroker::SignalStateChanged() {
     state_changed_event->Signal();
 
+    // TODO proper window management
     switch (applet_mode) {
     case LibraryAppletMode::AllForeground:
     case LibraryAppletMode::AllForegroundInitiallyHidden: {
-        auto applet_oe = system.ServiceManager().GetService<AppletOE>("appletOE");
-        auto applet_ae = system.ServiceManager().GetService<AppletAE>("appletAE");
-
-        if (applet_oe) {
-            applet_oe->GetMessageQueue()->FocusStateChanged();
-            break;
-        }
-
-        if (applet_ae) {
-            applet_ae->GetMessageQueue()->FocusStateChanged();
-            break;
-        }
+        system.GetAppletManager().FocusStateChanged();
         break;
     }
     default:
@@ -255,11 +246,6 @@ void FrontendAppletHolder::SetCurrentAppletId(AppletId applet_id) {
     current_applet_id = applet_id;
 }
 
-void FrontendAppletHolder::SetDefaultAppletFrontendSet() {
-    ClearAll();
-    SetDefaultAppletsIfMissing();
-}
-
 void FrontendAppletHolder::SetDefaultAppletsIfMissing() {
     if (frontend.cabinet == nullptr) {
         frontend.cabinet = std::make_unique<Core::Frontend::DefaultCabinetApplet>();
diff --git a/src/core/hle/service/am/frontend/applets.h b/src/core/hle/service/am/frontend/applets.h
index f58147955..dec1d63b2 100644
--- a/src/core/hle/service/am/frontend/applets.h
+++ b/src/core/hle/service/am/frontend/applets.h
@@ -188,7 +188,6 @@ public:
     void SetFrontendAppletSet(FrontendAppletSet set);
     void SetCabinetMode(NFP::CabinetMode mode);
     void SetCurrentAppletId(AppletId applet_id);
-    void SetDefaultAppletFrontendSet();
     void SetDefaultAppletsIfMissing();
     void ClearAll();
 
diff --git a/src/core/hle/service/am/library_applet_accessor.cpp b/src/core/hle/service/am/library_applet_accessor.cpp
index dabbd6dbe..d3be493e6 100644
--- a/src/core/hle/service/am/library_applet_accessor.cpp
+++ b/src/core/hle/service/am/library_applet_accessor.cpp
@@ -1,7 +1,9 @@
 // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
 // SPDX-License-Identifier: GPL-2.0-or-later
 
+#include "common/scope_exit.h"
 #include "core/hle/service/am/am_results.h"
+#include "core/hle/service/am/frontend/applets.h"
 #include "core/hle/service/am/library_applet_accessor.h"
 #include "core/hle/service/am/storage.h"
 #include "core/hle/service/ipc_helpers.h"
@@ -9,8 +11,10 @@
 namespace Service::AM {
 
 ILibraryAppletAccessor::ILibraryAppletAccessor(Core::System& system_,
-                                               std::shared_ptr<Frontend::FrontendApplet> applet_)
-    : ServiceFramework{system_, "ILibraryAppletAccessor"}, applet{std::move(applet_)} {
+                                               std::shared_ptr<AppletStorageHolder> storage_,
+                                               std::shared_ptr<Applet> applet_)
+    : ServiceFramework{system_, "ILibraryAppletAccessor"}, storage{std::move(storage_)},
+      applet{std::move(applet_)} {
     // clang-format off
     static const FunctionInfo functions[] = {
         {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"},
@@ -38,27 +42,31 @@ ILibraryAppletAccessor::ILibraryAppletAccessor(Core::System& system_,
     RegisterHandlers(functions);
 }
 
+ILibraryAppletAccessor::~ILibraryAppletAccessor() = default;
+
 void ILibraryAppletAccessor::GetAppletStateChangedEvent(HLERequestContext& ctx) {
     LOG_DEBUG(Service_AM, "called");
 
     IPC::ResponseBuilder rb{ctx, 2, 1};
     rb.Push(ResultSuccess);
-    rb.PushCopyObjects(applet->GetBroker().GetStateChangedEvent());
+    rb.PushCopyObjects(storage->state_changed_event.GetHandle());
 }
 
 void ILibraryAppletAccessor::IsCompleted(HLERequestContext& ctx) {
     LOG_DEBUG(Service_AM, "called");
 
+    std::scoped_lock lk{applet->lock};
+
     IPC::ResponseBuilder rb{ctx, 3};
     rb.Push(ResultSuccess);
-    rb.Push<u32>(applet->TransactionComplete());
+    rb.Push<u32>(applet->is_completed);
 }
 
 void ILibraryAppletAccessor::GetResult(HLERequestContext& ctx) {
     LOG_DEBUG(Service_AM, "called");
 
     IPC::ResponseBuilder rb{ctx, 2};
-    rb.Push(applet->GetStatus());
+    rb.Push(applet->terminate_result);
 }
 
 void ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero(HLERequestContext& ctx) {
@@ -71,10 +79,7 @@ void ILibraryAppletAccessor::PresetLibraryAppletGpuTimeSliceZero(HLERequestConte
 void ILibraryAppletAccessor::Start(HLERequestContext& ctx) {
     LOG_DEBUG(Service_AM, "called");
 
-    ASSERT(applet != nullptr);
-
-    applet->Initialize();
-    applet->Execute();
+    applet->process->Run();
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
@@ -84,16 +89,17 @@ void ILibraryAppletAccessor::RequestExit(HLERequestContext& ctx) {
     LOG_DEBUG(Service_AM, "called");
 
     ASSERT(applet != nullptr);
+    applet->message_queue.RequestExit();
 
     IPC::ResponseBuilder rb{ctx, 2};
-    rb.Push(applet->RequestExit());
+    rb.Push(ResultSuccess);
 }
 
 void ILibraryAppletAccessor::PushInData(HLERequestContext& ctx) {
     LOG_DEBUG(Service_AM, "called");
 
     IPC::RequestParser rp{ctx};
-    applet->GetBroker().PushNormalDataFromGame(rp.PopIpcInterface<IStorage>().lock());
+    storage->in_data.PushData(rp.PopIpcInterface<IStorage>().lock());
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
@@ -102,29 +108,24 @@ void ILibraryAppletAccessor::PushInData(HLERequestContext& ctx) {
 void ILibraryAppletAccessor::PopOutData(HLERequestContext& ctx) {
     LOG_DEBUG(Service_AM, "called");
 
-    auto storage = applet->GetBroker().PopNormalDataToGame();
-    if (storage == nullptr) {
-        LOG_DEBUG(Service_AM,
-                  "storage is a nullptr. There is no data in the current normal channel");
-        IPC::ResponseBuilder rb{ctx, 2};
-        rb.Push(AM::ResultNoDataInChannel);
-        return;
-    }
+    std::shared_ptr<IStorage> data;
+    const auto res = storage->out_data.PopData(&data);
 
-    IPC::ResponseBuilder rb{ctx, 2, 0, 1};
-    rb.Push(ResultSuccess);
-    rb.PushIpcInterface<IStorage>(std::move(storage));
+    if (res.IsSuccess()) {
+        IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+        rb.Push(res);
+        rb.PushIpcInterface(std::move(data));
+    } else {
+        IPC::ResponseBuilder rb{ctx, 2};
+        rb.Push(res);
+    }
 }
 
 void ILibraryAppletAccessor::PushInteractiveInData(HLERequestContext& ctx) {
     LOG_DEBUG(Service_AM, "called");
 
     IPC::RequestParser rp{ctx};
-    applet->GetBroker().PushInteractiveDataFromGame(rp.PopIpcInterface<IStorage>().lock());
-
-    ASSERT(applet->IsInitialized());
-    applet->ExecuteInteractive();
-    applet->Execute();
+    storage->interactive_in_data.PushData(rp.PopIpcInterface<IStorage>().lock());
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
@@ -133,18 +134,17 @@ void ILibraryAppletAccessor::PushInteractiveInData(HLERequestContext& ctx) {
 void ILibraryAppletAccessor::PopInteractiveOutData(HLERequestContext& ctx) {
     LOG_DEBUG(Service_AM, "called");
 
-    auto storage = applet->GetBroker().PopInteractiveDataToGame();
-    if (storage == nullptr) {
-        LOG_DEBUG(Service_AM,
-                  "storage is a nullptr. There is no data in the current interactive channel");
-        IPC::ResponseBuilder rb{ctx, 2};
-        rb.Push(AM::ResultNoDataInChannel);
-        return;
-    }
+    std::shared_ptr<IStorage> data;
+    const auto res = storage->interactive_out_data.PopData(&data);
 
-    IPC::ResponseBuilder rb{ctx, 2, 0, 1};
-    rb.Push(ResultSuccess);
-    rb.PushIpcInterface<IStorage>(std::move(storage));
+    if (res.IsSuccess()) {
+        IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+        rb.Push(res);
+        rb.PushIpcInterface(std::move(data));
+    } else {
+        IPC::ResponseBuilder rb{ctx, 2};
+        rb.Push(res);
+    }
 }
 
 void ILibraryAppletAccessor::GetPopOutDataEvent(HLERequestContext& ctx) {
@@ -152,7 +152,7 @@ void ILibraryAppletAccessor::GetPopOutDataEvent(HLERequestContext& ctx) {
 
     IPC::ResponseBuilder rb{ctx, 2, 1};
     rb.Push(ResultSuccess);
-    rb.PushCopyObjects(applet->GetBroker().GetNormalDataEvent());
+    rb.PushCopyObjects(storage->out_data.GetEvent());
 }
 
 void ILibraryAppletAccessor::GetPopInteractiveOutDataEvent(HLERequestContext& ctx) {
@@ -160,7 +160,7 @@ void ILibraryAppletAccessor::GetPopInteractiveOutDataEvent(HLERequestContext& ct
 
     IPC::ResponseBuilder rb{ctx, 2, 1};
     rb.Push(ResultSuccess);
-    rb.PushCopyObjects(applet->GetBroker().GetInteractiveDataEvent());
+    rb.PushCopyObjects(storage->interactive_out_data.GetEvent());
 }
 
 void ILibraryAppletAccessor::GetIndirectLayerConsumerHandle(HLERequestContext& ctx) {
diff --git a/src/core/hle/service/am/library_applet_accessor.h b/src/core/hle/service/am/library_applet_accessor.h
index 77f62906c..c34a1cbca 100644
--- a/src/core/hle/service/am/library_applet_accessor.h
+++ b/src/core/hle/service/am/library_applet_accessor.h
@@ -3,17 +3,21 @@
 
 #pragma once
 
-#include "core/hle/service/am/frontend/applets.h"
 #include "core/hle/service/service.h"
 
 namespace Service::AM {
 
+struct AppletStorageHolder;
+struct Applet;
+
 class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
 public:
     explicit ILibraryAppletAccessor(Core::System& system_,
-                                    std::shared_ptr<Frontend::FrontendApplet> applet_);
+                                    std::shared_ptr<AppletStorageHolder> storage_,
+                                    std::shared_ptr<Applet> applet_);
+    ~ILibraryAppletAccessor();
 
-private:
+protected:
     void GetAppletStateChangedEvent(HLERequestContext& ctx);
     void IsCompleted(HLERequestContext& ctx);
     void GetResult(HLERequestContext& ctx);
@@ -28,7 +32,8 @@ private:
     void GetPopInteractiveOutDataEvent(HLERequestContext& ctx);
     void GetIndirectLayerConsumerHandle(HLERequestContext& ctx);
 
-    std::shared_ptr<Frontend::FrontendApplet> applet;
+    const std::shared_ptr<AppletStorageHolder> storage;
+    const std::shared_ptr<Applet> applet;
 };
 
 } // namespace Service::AM
diff --git a/src/core/hle/service/am/library_applet_creator.cpp b/src/core/hle/service/am/library_applet_creator.cpp
index f80887517..e4332e244 100644
--- a/src/core/hle/service/am/library_applet_creator.cpp
+++ b/src/core/hle/service/am/library_applet_creator.cpp
@@ -2,16 +2,112 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 
 #include "core/hle/kernel/k_transfer_memory.h"
+#include "core/hle/service/am/applet_manager.h"
 #include "core/hle/service/am/frontend/applets.h"
 #include "core/hle/service/am/library_applet_accessor.h"
 #include "core/hle/service/am/library_applet_creator.h"
 #include "core/hle/service/am/storage.h"
 #include "core/hle/service/ipc_helpers.h"
+#include "core/hle/service/sm/sm.h"
 
 namespace Service::AM {
 
-ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_)
-    : ServiceFramework{system_, "ILibraryAppletCreator"} {
+namespace {
+
+AppletProgramId AppletIdToProgramId(AppletId applet_id) {
+    switch (applet_id) {
+    case AppletId::OverlayDisplay:
+        return AppletProgramId::OverlayDisplay;
+    case AppletId::QLaunch:
+        return AppletProgramId::QLaunch;
+    case AppletId::Starter:
+        return AppletProgramId::Starter;
+    case AppletId::Auth:
+        return AppletProgramId::Auth;
+    case AppletId::Cabinet:
+        return AppletProgramId::Cabinet;
+    case AppletId::Controller:
+        return AppletProgramId::Controller;
+    case AppletId::DataErase:
+        return AppletProgramId::DataErase;
+    case AppletId::Error:
+        return AppletProgramId::Error;
+    case AppletId::NetConnect:
+        return AppletProgramId::NetConnect;
+    case AppletId::ProfileSelect:
+        return AppletProgramId::ProfileSelect;
+    case AppletId::SoftwareKeyboard:
+        return AppletProgramId::SoftwareKeyboard;
+    case AppletId::MiiEdit:
+        return AppletProgramId::MiiEdit;
+    case AppletId::Web:
+        return AppletProgramId::Web;
+    case AppletId::Shop:
+        return AppletProgramId::Shop;
+    case AppletId::PhotoViewer:
+        return AppletProgramId::PhotoViewer;
+    case AppletId::Settings:
+        return AppletProgramId::Settings;
+    case AppletId::OfflineWeb:
+        return AppletProgramId::OfflineWeb;
+    case AppletId::LoginShare:
+        return AppletProgramId::LoginShare;
+    case AppletId::WebAuth:
+        return AppletProgramId::WebAuth;
+    case AppletId::MyPage:
+        return AppletProgramId::MyPage;
+    default:
+        return static_cast<AppletProgramId>(0);
+    }
+}
+
+std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
+                                                          std::shared_ptr<Applet> caller_applet,
+                                                          AppletId applet_id,
+                                                          LibraryAppletMode mode) {
+    const auto program_id = static_cast<u64>(AppletIdToProgramId(applet_id));
+    if (program_id == 0) {
+        // Unknown applet
+        return {};
+    }
+
+    auto process = std::make_unique<Process>(system);
+    if (!process->Initialize(program_id)) {
+        // Couldn't initialize the guest process
+        return {};
+    }
+
+    const auto applet = std::make_shared<Applet>(system, std::move(process));
+    applet->program_id = program_id;
+    applet->applet_id = applet_id;
+    applet->type = AppletType::LibraryApplet;
+    applet->library_applet_mode = mode;
+
+    // Library applet should be foreground
+    applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
+    applet->message_queue.PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
+    applet->focus_state = FocusState::InFocus;
+
+    auto storage = std::make_shared<AppletStorageHolder>(system);
+    applet->caller_applet = caller_applet;
+    applet->caller_applet_storage = storage;
+
+    system.GetAppletManager().InsertApplet(applet);
+
+    return std::make_shared<ILibraryAppletAccessor>(system, storage, applet);
+}
+
+std::shared_ptr<ILibraryAppletAccessor> CreateFrontendApplet(Core::System& system,
+                                                             AppletId applet_id,
+                                                             LibraryAppletMode mode) {
+    UNREACHABLE();
+    return {};
+}
+
+} // namespace
+
+ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_, std::shared_ptr<Applet> applet_)
+    : ServiceFramework{system_, "ILibraryAppletCreator"}, applet{std::move(applet_)} {
     static const FunctionInfo functions[] = {
         {0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"},
         {1, nullptr, "TerminateAllLibraryApplets"},
@@ -34,10 +130,11 @@ void ILibraryAppletCreator::CreateLibraryApplet(HLERequestContext& ctx) {
     LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", applet_id,
               applet_mode);
 
-    const auto& holder{system.GetFrontendAppletHolder()};
-    const auto applet = holder.GetApplet(applet_id, applet_mode);
-
-    if (applet == nullptr) {
+    auto library_applet = CreateGuestApplet(system, applet, applet_id, applet_mode);
+    if (!library_applet) {
+        library_applet = CreateFrontendApplet(system, applet_id, applet_mode);
+    }
+    if (!library_applet) {
         LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id);
 
         IPC::ResponseBuilder rb{ctx, 2};
@@ -45,10 +142,12 @@ void ILibraryAppletCreator::CreateLibraryApplet(HLERequestContext& ctx) {
         return;
     }
 
-    IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+    // Applet is created, can now be launched.
+    applet->library_applet_launchable_event.Signal();
 
+    IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     rb.Push(ResultSuccess);
-    rb.PushIpcInterface<ILibraryAppletAccessor>(system, applet);
+    rb.PushIpcInterface<ILibraryAppletAccessor>(library_applet);
 }
 
 void ILibraryAppletCreator::CreateStorage(HLERequestContext& ctx) {
diff --git a/src/core/hle/service/am/library_applet_creator.h b/src/core/hle/service/am/library_applet_creator.h
index 97f236fbc..551f287bd 100644
--- a/src/core/hle/service/am/library_applet_creator.h
+++ b/src/core/hle/service/am/library_applet_creator.h
@@ -7,9 +7,11 @@
 
 namespace Service::AM {
 
+struct Applet;
+
 class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
 public:
-    explicit ILibraryAppletCreator(Core::System& system_);
+    explicit ILibraryAppletCreator(Core::System& system_, std::shared_ptr<Applet> applet_);
     ~ILibraryAppletCreator() override;
 
 private:
@@ -17,6 +19,8 @@ private:
     void CreateStorage(HLERequestContext& ctx);
     void CreateTransferMemoryStorage(HLERequestContext& ctx);
     void CreateHandleStorage(HLERequestContext& ctx);
+
+    const std::shared_ptr<Applet> applet;
 };
 
 } // namespace Service::AM
diff --git a/src/core/hle/service/am/library_applet_proxy.cpp b/src/core/hle/service/am/library_applet_proxy.cpp
index 047fc40f4..1d88dd78c 100644
--- a/src/core/hle/service/am/library_applet_proxy.cpp
+++ b/src/core/hle/service/am/library_applet_proxy.cpp
@@ -19,10 +19,9 @@
 namespace Service::AM {
 
 ILibraryAppletProxy::ILibraryAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_,
-                                         std::shared_ptr<AppletMessageQueue> msg_queue_,
-                                         Core::System& system_)
-    : ServiceFramework{system_, "ILibraryAppletProxy"}, nvnflinger{nvnflinger_},
-      msg_queue{std::move(msg_queue_)} {
+                                         std::shared_ptr<Applet> applet_, Core::System& system_)
+    : ServiceFramework{system_, "ILibraryAppletProxy"}, nvnflinger{nvnflinger_}, applet{std::move(
+                                                                                     applet_)} {
     // clang-format off
         static const FunctionInfo functions[] = {
             {0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
@@ -43,12 +42,14 @@ ILibraryAppletProxy::ILibraryAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_,
     RegisterHandlers(functions);
 }
 
+ILibraryAppletProxy::~ILibraryAppletProxy() = default;
+
 void ILibraryAppletProxy::GetCommonStateGetter(HLERequestContext& ctx) {
     LOG_DEBUG(Service_AM, "called");
 
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     rb.Push(ResultSuccess);
-    rb.PushIpcInterface<ICommonStateGetter>(system, msg_queue);
+    rb.PushIpcInterface<ICommonStateGetter>(system, applet);
 }
 
 void ILibraryAppletProxy::GetSelfController(HLERequestContext& ctx) {
@@ -56,7 +57,7 @@ void ILibraryAppletProxy::GetSelfController(HLERequestContext& ctx) {
 
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     rb.Push(ResultSuccess);
-    rb.PushIpcInterface<ISelfController>(system, nvnflinger);
+    rb.PushIpcInterface<ISelfController>(system, applet, nvnflinger);
 }
 
 void ILibraryAppletProxy::GetWindowController(HLERequestContext& ctx) {
@@ -64,7 +65,7 @@ void ILibraryAppletProxy::GetWindowController(HLERequestContext& ctx) {
 
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     rb.Push(ResultSuccess);
-    rb.PushIpcInterface<IWindowController>(system);
+    rb.PushIpcInterface<IWindowController>(system, applet);
 }
 
 void ILibraryAppletProxy::GetAudioController(HLERequestContext& ctx) {
@@ -88,7 +89,7 @@ void ILibraryAppletProxy::GetProcessWindingController(HLERequestContext& ctx) {
 
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     rb.Push(ResultSuccess);
-    rb.PushIpcInterface<IProcessWindingController>(system);
+    rb.PushIpcInterface<IProcessWindingController>(system, applet);
 }
 
 void ILibraryAppletProxy::GetLibraryAppletCreator(HLERequestContext& ctx) {
@@ -96,7 +97,7 @@ void ILibraryAppletProxy::GetLibraryAppletCreator(HLERequestContext& ctx) {
 
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     rb.Push(ResultSuccess);
-    rb.PushIpcInterface<ILibraryAppletCreator>(system);
+    rb.PushIpcInterface<ILibraryAppletCreator>(system, applet);
 }
 
 void ILibraryAppletProxy::OpenLibraryAppletSelfAccessor(HLERequestContext& ctx) {
@@ -104,7 +105,7 @@ void ILibraryAppletProxy::OpenLibraryAppletSelfAccessor(HLERequestContext& ctx)
 
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     rb.Push(ResultSuccess);
-    rb.PushIpcInterface<ILibraryAppletSelfAccessor>(system);
+    rb.PushIpcInterface<ILibraryAppletSelfAccessor>(system, applet);
 }
 
 void ILibraryAppletProxy::GetAppletCommonFunctions(HLERequestContext& ctx) {
@@ -112,7 +113,7 @@ void ILibraryAppletProxy::GetAppletCommonFunctions(HLERequestContext& ctx) {
 
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     rb.Push(ResultSuccess);
-    rb.PushIpcInterface<IAppletCommonFunctions>(system);
+    rb.PushIpcInterface<IAppletCommonFunctions>(system, applet);
 }
 
 void ILibraryAppletProxy::GetHomeMenuFunctions(HLERequestContext& ctx) {
diff --git a/src/core/hle/service/am/library_applet_proxy.h b/src/core/hle/service/am/library_applet_proxy.h
index cd9e6d02b..8f7a25897 100644
--- a/src/core/hle/service/am/library_applet_proxy.h
+++ b/src/core/hle/service/am/library_applet_proxy.h
@@ -3,16 +3,17 @@
 
 #pragma once
 
-#include "core/hle/service/am/applet_message_queue.h"
 #include "core/hle/service/service.h"
 
 namespace Service::AM {
 
+struct Applet;
+
 class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> {
 public:
     explicit ILibraryAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_,
-                                 std::shared_ptr<AppletMessageQueue> msg_queue_,
-                                 Core::System& system_);
+                                 std::shared_ptr<Applet> applet_, Core::System& system_);
+    ~ILibraryAppletProxy();
 
 private:
     void GetCommonStateGetter(HLERequestContext& ctx);
@@ -29,7 +30,7 @@ private:
     void GetDebugFunctions(HLERequestContext& ctx);
 
     Nvnflinger::Nvnflinger& nvnflinger;
-    std::shared_ptr<AppletMessageQueue> msg_queue;
+    std::shared_ptr<Applet> applet;
 };
 
 } // namespace Service::AM
diff --git a/src/core/hle/service/am/library_applet_self_accessor.cpp b/src/core/hle/service/am/library_applet_self_accessor.cpp
index c36f141f4..74ee33213 100644
--- a/src/core/hle/service/am/library_applet_self_accessor.cpp
+++ b/src/core/hle/service/am/library_applet_self_accessor.cpp
@@ -1,9 +1,11 @@
 // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
 // SPDX-License-Identifier: GPL-2.0-or-later
 
+#include "common/scope_exit.h"
 #include "core/core_timing.h"
 #include "core/hle/service/acc/profile_manager.h"
 #include "core/hle/service/am/am_results.h"
+#include "core/hle/service/am/applet_manager.h"
 #include "core/hle/service/am/frontend/applet_cabinet.h"
 #include "core/hle/service/am/frontend/applet_controller.h"
 #include "core/hle/service/am/frontend/applet_mii_edit_types.h"
@@ -16,16 +18,44 @@
 
 namespace Service::AM {
 
-ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_)
-    : ServiceFramework{system_, "ILibraryAppletSelfAccessor"} {
+namespace {
+
+struct AppletIdentityInfo {
+    AppletId applet_id;
+    INSERT_PADDING_BYTES(0x4);
+    u64 application_id;
+};
+static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size.");
+
+AppletIdentityInfo GetCallerIdentity(std::shared_ptr<Applet> applet) {
+    if (const auto caller_applet = applet->caller_applet.lock(); caller_applet) {
+        // TODO: is this actually the application ID?
+        return {
+            .applet_id = applet->applet_id,
+            .application_id = applet->program_id,
+        };
+    } else {
+        return {
+            .applet_id = AppletId::QLaunch,
+            .application_id = 0x0100000000001000ull,
+        };
+    }
+}
+
+} // namespace
+
+ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_,
+                                                       std::shared_ptr<Applet> applet_)
+    : ServiceFramework{system_, "ILibraryAppletSelfAccessor"}, applet{std::move(applet_)},
+      storage{applet->caller_applet_storage} {
     // clang-format off
     static const FunctionInfo functions[] = {
         {0, &ILibraryAppletSelfAccessor::PopInData, "PopInData"},
         {1, &ILibraryAppletSelfAccessor::PushOutData, "PushOutData"},
-        {2, nullptr, "PopInteractiveInData"},
-        {3, nullptr, "PushInteractiveOutData"},
-        {5, nullptr, "GetPopInDataEvent"},
-        {6, nullptr, "GetPopInteractiveInDataEvent"},
+        {2, &ILibraryAppletSelfAccessor::PopInteractiveInData, "PopInteractiveInData"},
+        {3, &ILibraryAppletSelfAccessor::PushInteractiveOutData, "PushInteractiveOutData"},
+        {5, &ILibraryAppletSelfAccessor::GetPopInDataEvent, "GetPopInDataEvent"},
+        {6, &ILibraryAppletSelfAccessor::GetPopInteractiveInDataEvent, "GetPopInteractiveInDataEvent"},
         {10, &ILibraryAppletSelfAccessor::ExitProcessAndReturn, "ExitProcessAndReturn"},
         {11, &ILibraryAppletSelfAccessor::GetLibraryAppletInfo, "GetLibraryAppletInfo"},
         {12, &ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo, "GetMainAppletIdentityInfo"},
@@ -58,26 +88,6 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_)
     };
     // clang-format on
     RegisterHandlers(functions);
-
-    switch (system.GetFrontendAppletHolder().GetCurrentAppletId()) {
-    case AppletId::Cabinet:
-        PushInShowCabinetData();
-        break;
-    case AppletId::MiiEdit:
-        PushInShowMiiEditData();
-        break;
-    case AppletId::PhotoViewer:
-        PushInShowAlbum();
-        break;
-    case AppletId::SoftwareKeyboard:
-        PushInShowSoftwareKeyboard();
-        break;
-    case AppletId::Controller:
-        PushInShowController();
-        break;
-    default:
-        break;
-    }
 }
 
 ILibraryAppletSelfAccessor::~ILibraryAppletSelfAccessor() = default;
@@ -85,31 +95,81 @@ ILibraryAppletSelfAccessor::~ILibraryAppletSelfAccessor() = default;
 void ILibraryAppletSelfAccessor::PopInData(HLERequestContext& ctx) {
     LOG_INFO(Service_AM, "called");
 
-    if (queue_data.empty()) {
+    std::shared_ptr<IStorage> data;
+    const auto res = storage->in_data.PopData(&data);
+
+    if (res.IsSuccess()) {
+        IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+        rb.Push(res);
+        rb.PushIpcInterface(std::move(data));
+    } else {
         IPC::ResponseBuilder rb{ctx, 2};
-        rb.Push(AM::ResultNoDataInChannel);
-        return;
+        rb.Push(res);
     }
-
-    auto data = queue_data.front();
-    queue_data.pop_front();
-
-    IPC::ResponseBuilder rb{ctx, 2, 0, 1};
-    rb.Push(ResultSuccess);
-    rb.PushIpcInterface<IStorage>(system, std::move(data));
 }
 
 void ILibraryAppletSelfAccessor::PushOutData(HLERequestContext& ctx) {
-    LOG_WARNING(Service_AM, "(STUBBED) called");
+    LOG_INFO(Service_AM, "called");
+
+    IPC::RequestParser rp{ctx};
+    storage->out_data.PushData(rp.PopIpcInterface<IStorage>().lock());
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
 }
 
-void ILibraryAppletSelfAccessor::ExitProcessAndReturn(HLERequestContext& ctx) {
-    LOG_WARNING(Service_AM, "(STUBBED) called");
+void ILibraryAppletSelfAccessor::PopInteractiveInData(HLERequestContext& ctx) {
+    LOG_INFO(Service_AM, "called");
 
-    system.Exit();
+    std::shared_ptr<IStorage> data;
+    const auto res = storage->interactive_in_data.PopData(&data);
+
+    if (res.IsSuccess()) {
+        IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+        rb.Push(res);
+        rb.PushIpcInterface(std::move(data));
+    } else {
+        IPC::ResponseBuilder rb{ctx, 2};
+        rb.Push(res);
+    }
+}
+
+void ILibraryAppletSelfAccessor::PushInteractiveOutData(HLERequestContext& ctx) {
+    LOG_INFO(Service_AM, "called");
+
+    IPC::RequestParser rp{ctx};
+    storage->interactive_out_data.PushData(rp.PopIpcInterface<IStorage>().lock());
+
+    IPC::ResponseBuilder rb{ctx, 2};
+    rb.Push(ResultSuccess);
+}
+
+void ILibraryAppletSelfAccessor::GetPopInDataEvent(HLERequestContext& ctx) {
+    LOG_INFO(Service_AM, "called");
+
+    IPC::ResponseBuilder rb{ctx, 2, 1};
+    rb.Push(ResultSuccess);
+    rb.PushCopyObjects(storage->in_data.GetEvent());
+}
+
+void ILibraryAppletSelfAccessor::GetPopInteractiveInDataEvent(HLERequestContext& ctx) {
+    LOG_INFO(Service_AM, "called");
+
+    IPC::ResponseBuilder rb{ctx, 2, 1};
+    rb.Push(ResultSuccess);
+    rb.PushCopyObjects(storage->interactive_in_data.GetEvent());
+}
+
+void ILibraryAppletSelfAccessor::ExitProcessAndReturn(HLERequestContext& ctx) {
+    LOG_INFO(Service_AM, "called");
+
+    system.GetAppletManager().TerminateAndRemoveApplet(applet->aruid);
+
+    {
+        std::scoped_lock lk{applet->lock};
+        applet->is_completed = true;
+        storage->state_changed_event.Signal();
+    }
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
@@ -124,8 +184,8 @@ void ILibraryAppletSelfAccessor::GetLibraryAppletInfo(HLERequestContext& ctx) {
     LOG_WARNING(Service_AM, "(STUBBED) called");
 
     const LibraryAppletInfo applet_info{
-        .applet_id = system.GetFrontendAppletHolder().GetCurrentAppletId(),
-        .library_applet_mode = LibraryAppletMode::AllForeground,
+        .applet_id = applet->applet_id,
+        .library_applet_mode = applet->library_applet_mode,
     };
 
     IPC::ResponseBuilder rb{ctx, 4};
@@ -134,13 +194,6 @@ void ILibraryAppletSelfAccessor::GetLibraryAppletInfo(HLERequestContext& ctx) {
 }
 
 void ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo(HLERequestContext& ctx) {
-    struct AppletIdentityInfo {
-        AppletId applet_id;
-        INSERT_PADDING_BYTES(0x4);
-        u64 application_id;
-    };
-    static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size.");
-
     LOG_WARNING(Service_AM, "(STUBBED) called");
 
     const AppletIdentityInfo applet_info{
@@ -154,22 +207,11 @@ void ILibraryAppletSelfAccessor::GetMainAppletIdentityInfo(HLERequestContext& ct
 }
 
 void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext& ctx) {
-    struct AppletIdentityInfo {
-        AppletId applet_id;
-        INSERT_PADDING_BYTES(0x4);
-        u64 application_id;
-    };
-    static_assert(sizeof(AppletIdentityInfo) == 0x10, "AppletIdentityInfo has incorrect size.");
     LOG_WARNING(Service_AM, "(STUBBED) called");
 
-    const AppletIdentityInfo applet_info{
-        .applet_id = AppletId::QLaunch,
-        .application_id = 0x0100000000001000ull,
-    };
-
     IPC::ResponseBuilder rb{ctx, 6};
     rb.Push(ResultSuccess);
-    rb.PushRaw(applet_info);
+    rb.PushRaw(GetCallerIdentity(applet));
 }
 
 void ILibraryAppletSelfAccessor::GetDesirableKeyboardLayout(HLERequestContext& ctx) {
@@ -207,176 +249,4 @@ void ILibraryAppletSelfAccessor::ShouldSetGpuTimeSliceManually(HLERequestContext
     rb.Push<u8>(0);
 }
 
-void ILibraryAppletSelfAccessor::PushInShowAlbum() {
-    const CommonArguments arguments{
-        .arguments_version = CommonArgumentVersion::Version3,
-        .size = CommonArgumentSize::Version3,
-        .library_version = 1,
-        .theme_color = ThemeColor::BasicBlack,
-        .play_startup_sound = true,
-        .system_tick = system.CoreTiming().GetClockTicks(),
-    };
-
-    std::vector<u8> argument_data(sizeof(arguments));
-    std::vector<u8> settings_data{2};
-    std::memcpy(argument_data.data(), &arguments, sizeof(arguments));
-    queue_data.emplace_back(std::move(argument_data));
-    queue_data.emplace_back(std::move(settings_data));
-}
-
-void ILibraryAppletSelfAccessor::PushInShowController() {
-    const CommonArguments common_args = {
-        .arguments_version = CommonArgumentVersion::Version3,
-        .size = CommonArgumentSize::Version3,
-        .library_version = static_cast<u32>(Frontend::ControllerAppletVersion::Version8),
-        .theme_color = ThemeColor::BasicBlack,
-        .play_startup_sound = true,
-        .system_tick = system.CoreTiming().GetClockTicks(),
-    };
-
-    Frontend::ControllerSupportArgNew user_args = {
-        .header = {.player_count_min = 1,
-                   .player_count_max = 4,
-                   .enable_take_over_connection = true,
-                   .enable_left_justify = false,
-                   .enable_permit_joy_dual = true,
-                   .enable_single_mode = false,
-                   .enable_identification_color = false},
-        .identification_colors = {},
-        .enable_explain_text = false,
-        .explain_text = {},
-    };
-
-    Frontend::ControllerSupportArgPrivate private_args = {
-        .arg_private_size = sizeof(Frontend::ControllerSupportArgPrivate),
-        .arg_size = sizeof(Frontend::ControllerSupportArgNew),
-        .is_home_menu = true,
-        .flag_1 = true,
-        .mode = Frontend::ControllerSupportMode::ShowControllerSupport,
-        .caller = Frontend::ControllerSupportCaller::
-            Application, // switchbrew: Always zero except with
-                         // ShowControllerFirmwareUpdateForSystem/ShowControllerKeyRemappingForSystem,
-                         // which sets this to the input param
-        .style_set = Core::HID::NpadStyleSet::None,
-        .joy_hold_type = 0,
-    };
-    std::vector<u8> common_args_data(sizeof(common_args));
-    std::vector<u8> private_args_data(sizeof(private_args));
-    std::vector<u8> user_args_data(sizeof(user_args));
-
-    std::memcpy(common_args_data.data(), &common_args, sizeof(common_args));
-    std::memcpy(private_args_data.data(), &private_args, sizeof(private_args));
-    std::memcpy(user_args_data.data(), &user_args, sizeof(user_args));
-
-    queue_data.emplace_back(std::move(common_args_data));
-    queue_data.emplace_back(std::move(private_args_data));
-    queue_data.emplace_back(std::move(user_args_data));
-}
-
-void ILibraryAppletSelfAccessor::PushInShowCabinetData() {
-    const CommonArguments arguments{
-        .arguments_version = CommonArgumentVersion::Version3,
-        .size = CommonArgumentSize::Version3,
-        .library_version = static_cast<u32>(Frontend::CabinetAppletVersion::Version1),
-        .theme_color = ThemeColor::BasicBlack,
-        .play_startup_sound = true,
-        .system_tick = system.CoreTiming().GetClockTicks(),
-    };
-
-    const Frontend::StartParamForAmiiboSettings amiibo_settings{
-        .param_1 = 0,
-        .applet_mode = system.GetFrontendAppletHolder().GetCabinetMode(),
-        .flags = Frontend::CabinetFlags::None,
-        .amiibo_settings_1 = 0,
-        .device_handle = 0,
-        .tag_info{},
-        .register_info{},
-        .amiibo_settings_3{},
-    };
-
-    std::vector<u8> argument_data(sizeof(arguments));
-    std::vector<u8> settings_data(sizeof(amiibo_settings));
-    std::memcpy(argument_data.data(), &arguments, sizeof(arguments));
-    std::memcpy(settings_data.data(), &amiibo_settings, sizeof(amiibo_settings));
-    queue_data.emplace_back(std::move(argument_data));
-    queue_data.emplace_back(std::move(settings_data));
-}
-
-void ILibraryAppletSelfAccessor::PushInShowMiiEditData() {
-    struct MiiEditV3 {
-        Frontend::MiiEditAppletInputCommon common;
-        Frontend::MiiEditAppletInputV3 input;
-    };
-    static_assert(sizeof(MiiEditV3) == 0x100, "MiiEditV3 has incorrect size.");
-
-    MiiEditV3 mii_arguments{
-        .common =
-            {
-                .version = Frontend::MiiEditAppletVersion::Version3,
-                .applet_mode = Frontend::MiiEditAppletMode::ShowMiiEdit,
-            },
-        .input{},
-    };
-
-    std::vector<u8> argument_data(sizeof(mii_arguments));
-    std::memcpy(argument_data.data(), &mii_arguments, sizeof(mii_arguments));
-
-    queue_data.emplace_back(std::move(argument_data));
-}
-
-void ILibraryAppletSelfAccessor::PushInShowSoftwareKeyboard() {
-    const CommonArguments arguments{
-        .arguments_version = CommonArgumentVersion::Version3,
-        .size = CommonArgumentSize::Version3,
-        .library_version = static_cast<u32>(Frontend::SwkbdAppletVersion::Version524301),
-        .theme_color = ThemeColor::BasicBlack,
-        .play_startup_sound = true,
-        .system_tick = system.CoreTiming().GetClockTicks(),
-    };
-
-    std::vector<char16_t> initial_string(0);
-
-    const Frontend::SwkbdConfigCommon swkbd_config{
-        .type = Frontend::SwkbdType::Qwerty,
-        .ok_text{},
-        .left_optional_symbol_key{},
-        .right_optional_symbol_key{},
-        .use_prediction = false,
-        .key_disable_flags{},
-        .initial_cursor_position = Frontend::SwkbdInitialCursorPosition::Start,
-        .header_text{},
-        .sub_text{},
-        .guide_text{},
-        .max_text_length = 500,
-        .min_text_length = 0,
-        .password_mode = Frontend::SwkbdPasswordMode::Disabled,
-        .text_draw_type = Frontend::SwkbdTextDrawType::Box,
-        .enable_return_button = true,
-        .use_utf8 = false,
-        .use_blur_background = true,
-        .initial_string_offset{},
-        .initial_string_length = static_cast<u32>(initial_string.size()),
-        .user_dictionary_offset{},
-        .user_dictionary_entries{},
-        .use_text_check = false,
-    };
-
-    Frontend::SwkbdConfigNew swkbd_config_new{};
-
-    std::vector<u8> argument_data(sizeof(arguments));
-    std::vector<u8> swkbd_data(sizeof(swkbd_config) + sizeof(swkbd_config_new));
-    std::vector<u8> work_buffer(swkbd_config.initial_string_length * sizeof(char16_t));
-
-    std::memcpy(argument_data.data(), &arguments, sizeof(arguments));
-    std::memcpy(swkbd_data.data(), &swkbd_config, sizeof(swkbd_config));
-    std::memcpy(swkbd_data.data() + sizeof(swkbd_config), &swkbd_config_new,
-                sizeof(Frontend::SwkbdConfigNew));
-    std::memcpy(work_buffer.data(), initial_string.data(),
-                swkbd_config.initial_string_length * sizeof(char16_t));
-
-    queue_data.emplace_back(std::move(argument_data));
-    queue_data.emplace_back(std::move(swkbd_data));
-    queue_data.emplace_back(std::move(work_buffer));
-}
-
 } // namespace Service::AM
diff --git a/src/core/hle/service/am/library_applet_self_accessor.h b/src/core/hle/service/am/library_applet_self_accessor.h
index 45b325b77..b15040539 100644
--- a/src/core/hle/service/am/library_applet_self_accessor.h
+++ b/src/core/hle/service/am/library_applet_self_accessor.h
@@ -10,14 +10,21 @@
 
 namespace Service::AM {
 
+struct AppletStorageHolder;
+struct Applet;
+
 class ILibraryAppletSelfAccessor final : public ServiceFramework<ILibraryAppletSelfAccessor> {
 public:
-    explicit ILibraryAppletSelfAccessor(Core::System& system_);
+    explicit ILibraryAppletSelfAccessor(Core::System& system_, std::shared_ptr<Applet> applet_);
     ~ILibraryAppletSelfAccessor() override;
 
 private:
     void PopInData(HLERequestContext& ctx);
     void PushOutData(HLERequestContext& ctx);
+    void PopInteractiveInData(HLERequestContext& ctx);
+    void PushInteractiveOutData(HLERequestContext& ctx);
+    void GetPopInDataEvent(HLERequestContext& ctx);
+    void GetPopInteractiveInDataEvent(HLERequestContext& ctx);
     void GetLibraryAppletInfo(HLERequestContext& ctx);
     void GetMainAppletIdentityInfo(HLERequestContext& ctx);
     void ExitProcessAndReturn(HLERequestContext& ctx);
@@ -26,13 +33,8 @@ private:
     void GetMainAppletAvailableUsers(HLERequestContext& ctx);
     void ShouldSetGpuTimeSliceManually(HLERequestContext& ctx);
 
-    void PushInShowAlbum();
-    void PushInShowCabinetData();
-    void PushInShowMiiEditData();
-    void PushInShowSoftwareKeyboard();
-    void PushInShowController();
-
-    std::deque<std::vector<u8>> queue_data;
+    const std::shared_ptr<Applet> applet;
+    const std::shared_ptr<AppletStorageHolder> storage;
 };
 
 } // namespace Service::AM
diff --git a/src/core/hle/service/am/process_winding_controller.cpp b/src/core/hle/service/am/process_winding_controller.cpp
index 7954abd7a..f5ccc4643 100644
--- a/src/core/hle/service/am/process_winding_controller.cpp
+++ b/src/core/hle/service/am/process_winding_controller.cpp
@@ -8,8 +8,9 @@
 
 namespace Service::AM {
 
-IProcessWindingController::IProcessWindingController(Core::System& system_)
-    : ServiceFramework{system_, "IProcessWindingController"} {
+IProcessWindingController::IProcessWindingController(Core::System& system_,
+                                                     std::shared_ptr<Applet> applet_)
+    : ServiceFramework{system_, "IProcessWindingController"}, applet{std::move(applet_)} {
     // clang-format off
     static const FunctionInfo functions[] = {
         {0, &IProcessWindingController::GetLaunchReason, "GetLaunchReason"},
@@ -31,34 +32,15 @@ IProcessWindingController::~IProcessWindingController() = default;
 void IProcessWindingController::GetLaunchReason(HLERequestContext& ctx) {
     LOG_WARNING(Service_AM, "(STUBBED) called");
 
-    struct AppletProcessLaunchReason {
-        u8 flag;
-        INSERT_PADDING_BYTES(3);
-    };
-    static_assert(sizeof(AppletProcessLaunchReason) == 0x4,
-                  "AppletProcessLaunchReason is an invalid size");
-
-    AppletProcessLaunchReason reason{
-        .flag = 0,
-    };
-
     IPC::ResponseBuilder rb{ctx, 3};
     rb.Push(ResultSuccess);
-    rb.PushRaw(reason);
+    rb.PushRaw(applet->launch_reason);
 }
 
 void IProcessWindingController::OpenCallingLibraryApplet(HLERequestContext& ctx) {
-    const auto applet_id = system.GetFrontendAppletHolder().GetCurrentAppletId();
-    const auto applet_mode = LibraryAppletMode::AllForeground;
-
-    LOG_WARNING(Service_AM, "(STUBBED) called with applet_id={:08X}, applet_mode={:08X}", applet_id,
-                applet_mode);
-
-    const auto& holder{system.GetFrontendAppletHolder()};
-    const auto applet = holder.GetApplet(applet_id, applet_mode);
-
-    if (applet == nullptr) {
-        LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id);
+    const auto caller_applet = applet->caller_applet.lock();
+    if (caller_applet == nullptr) {
+        LOG_ERROR(Service_AM, "No calling applet available");
 
         IPC::ResponseBuilder rb{ctx, 2};
         rb.Push(ResultUnknown);
@@ -67,7 +49,8 @@ void IProcessWindingController::OpenCallingLibraryApplet(HLERequestContext& ctx)
 
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     rb.Push(ResultSuccess);
-    rb.PushIpcInterface<ILibraryAppletAccessor>(system, applet);
+    rb.PushIpcInterface<ILibraryAppletAccessor>(system, applet->caller_applet_storage,
+                                                caller_applet);
 }
 
 } // namespace Service::AM
diff --git a/src/core/hle/service/am/process_winding_controller.h b/src/core/hle/service/am/process_winding_controller.h
index 9b9704201..71ae4c4f5 100644
--- a/src/core/hle/service/am/process_winding_controller.h
+++ b/src/core/hle/service/am/process_winding_controller.h
@@ -7,14 +7,18 @@
 
 namespace Service::AM {
 
+struct Applet;
+
 class IProcessWindingController final : public ServiceFramework<IProcessWindingController> {
 public:
-    explicit IProcessWindingController(Core::System& system_);
+    explicit IProcessWindingController(Core::System& system_, std::shared_ptr<Applet> applet_);
     ~IProcessWindingController() override;
 
 private:
     void GetLaunchReason(HLERequestContext& ctx);
     void OpenCallingLibraryApplet(HLERequestContext& ctx);
+
+    const std::shared_ptr<Applet> applet;
 };
 
 } // namespace Service::AM
diff --git a/src/core/hle/service/am/self_controller.cpp b/src/core/hle/service/am/self_controller.cpp
index d5de1bb2f..3ac967b4d 100644
--- a/src/core/hle/service/am/self_controller.cpp
+++ b/src/core/hle/service/am/self_controller.cpp
@@ -1,6 +1,7 @@
 // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
 // SPDX-License-Identifier: GPL-2.0-or-later
 
+#include "core/hle/service/am/am_results.h"
 #include "core/hle/service/am/frontend/applets.h"
 #include "core/hle/service/am/self_controller.h"
 #include "core/hle/service/caps/caps_su.h"
@@ -12,9 +13,10 @@
 
 namespace Service::AM {
 
-ISelfController::ISelfController(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger_)
-    : ServiceFramework{system_, "ISelfController"}, nvnflinger{nvnflinger_},
-      service_context{system, "ISelfController"} {
+ISelfController::ISelfController(Core::System& system_, std::shared_ptr<Applet> applet_,
+                                 Nvnflinger::Nvnflinger& nvnflinger_)
+    : ServiceFramework{system_, "ISelfController"}, nvnflinger{nvnflinger_}, applet{std::move(
+                                                                                 applet_)} {
     // clang-format off
     static const FunctionInfo functions[] = {
         {0, &ISelfController::Exit, "Exit"},
@@ -69,24 +71,9 @@ ISelfController::ISelfController(Core::System& system_, Nvnflinger::Nvnflinger&
     // clang-format on
 
     RegisterHandlers(functions);
-
-    launchable_event = service_context.CreateEvent("ISelfController:LaunchableEvent");
-
-    // This event is created by AM on the first time GetAccumulatedSuspendedTickChangedEvent() is
-    // called. Yuzu can just create it unconditionally, since it doesn't need to support multiple
-    // ISelfControllers. The event is signaled on creation, and on transition from suspended -> not
-    // suspended if the event has previously been created by a call to
-    // GetAccumulatedSuspendedTickChangedEvent.
-
-    accumulated_suspended_tick_changed_event =
-        service_context.CreateEvent("ISelfController:AccumulatedSuspendedTickChangedEvent");
-    accumulated_suspended_tick_changed_event->Signal();
 }
 
-ISelfController::~ISelfController() {
-    service_context.CloseEvent(launchable_event);
-    service_context.CloseEvent(accumulated_suspended_tick_changed_event);
-}
+ISelfController::~ISelfController() = default;
 
 void ISelfController::Exit(HLERequestContext& ctx) {
     LOG_DEBUG(Service_AM, "called");
@@ -94,6 +81,7 @@ void ISelfController::Exit(HLERequestContext& ctx) {
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
 
+    // TODO
     system.Exit();
 }
 
@@ -120,8 +108,10 @@ void ISelfController::UnlockExit(HLERequestContext& ctx) {
 }
 
 void ISelfController::EnterFatalSection(HLERequestContext& ctx) {
-    ++num_fatal_sections_entered;
-    LOG_DEBUG(Service_AM, "called. Num fatal sections entered: {}", num_fatal_sections_entered);
+
+    std::scoped_lock lk{applet->lock};
+    applet->fatal_section_count++;
+    LOG_DEBUG(Service_AM, "called. Num fatal sections entered: {}", applet->fatal_section_count);
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
@@ -131,13 +121,14 @@ void ISelfController::LeaveFatalSection(HLERequestContext& ctx) {
     LOG_DEBUG(Service_AM, "called.");
 
     // Entry and exit of fatal sections must be balanced.
-    if (num_fatal_sections_entered == 0) {
+    std::scoped_lock lk{applet->lock};
+    if (applet->fatal_section_count == 0) {
         IPC::ResponseBuilder rb{ctx, 2};
-        rb.Push(Result{ErrorModule::AM, 512});
+        rb.Push(AM::ResultFatalSectionCountImbalance);
         return;
     }
 
-    --num_fatal_sections_entered;
+    applet->fatal_section_count--;
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
@@ -146,11 +137,11 @@ void ISelfController::LeaveFatalSection(HLERequestContext& ctx) {
 void ISelfController::GetLibraryAppletLaunchableEvent(HLERequestContext& ctx) {
     LOG_WARNING(Service_AM, "(STUBBED) called");
 
-    launchable_event->Signal();
+    applet->library_applet_launchable_event.Signal();
 
     IPC::ResponseBuilder rb{ctx, 2, 1};
     rb.Push(ResultSuccess);
-    rb.PushCopyObjects(launchable_event->GetReadableEvent());
+    rb.PushCopyObjects(applet->library_applet_launchable_event.GetHandle());
 }
 
 void ISelfController::SetScreenShotPermission(HLERequestContext& ctx) {
@@ -158,7 +149,8 @@ void ISelfController::SetScreenShotPermission(HLERequestContext& ctx) {
     const auto permission = rp.PopEnum<ScreenshotPermission>();
     LOG_DEBUG(Service_AM, "called, permission={}", permission);
 
-    screenshot_permission = permission;
+    std::scoped_lock lk{applet->lock};
+    applet->screenshot_permission = permission;
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
@@ -167,8 +159,11 @@ void ISelfController::SetScreenShotPermission(HLERequestContext& ctx) {
 void ISelfController::SetOperationModeChangedNotification(HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
 
-    bool flag = rp.Pop<bool>();
-    LOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag);
+    const bool notification_enabled = rp.Pop<bool>();
+    LOG_WARNING(Service_AM, "(STUBBED) called notification_enabled={}", notification_enabled);
+
+    std::scoped_lock lk{applet->lock};
+    applet->operation_mode_changed_notification_enabled = notification_enabled;
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
@@ -177,28 +172,27 @@ void ISelfController::SetOperationModeChangedNotification(HLERequestContext& ctx
 void ISelfController::SetPerformanceModeChangedNotification(HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
 
-    bool flag = rp.Pop<bool>();
-    LOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag);
+    const bool notification_enabled = rp.Pop<bool>();
+    LOG_WARNING(Service_AM, "(STUBBED) called notification_enabled={}", notification_enabled);
+
+    std::scoped_lock lk{applet->lock};
+    applet->performance_mode_changed_notification_enabled = notification_enabled;
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
 }
 
 void ISelfController::SetFocusHandlingMode(HLERequestContext& ctx) {
-    // Takes 3 input u8s with each field located immediately after the previous
-    // u8, these are bool flags. No output.
     IPC::RequestParser rp{ctx};
 
-    struct FocusHandlingModeParams {
-        u8 unknown0;
-        u8 unknown1;
-        u8 unknown2;
-    };
-    const auto flags = rp.PopRaw<FocusHandlingModeParams>();
+    const auto flags = rp.PopRaw<FocusHandlingMode>();
 
     LOG_WARNING(Service_AM, "(STUBBED) called. unknown0={}, unknown1={}, unknown2={}",
                 flags.unknown0, flags.unknown1, flags.unknown2);
 
+    std::scoped_lock lk{applet->lock};
+    applet->focus_handling_mode = flags;
+
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
 }
@@ -206,24 +200,35 @@ void ISelfController::SetFocusHandlingMode(HLERequestContext& ctx) {
 void ISelfController::SetRestartMessageEnabled(HLERequestContext& ctx) {
     LOG_WARNING(Service_AM, "(STUBBED) called");
 
+    std::scoped_lock lk{applet->lock};
+    applet->restart_message_enabled = true;
+
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
 }
 
 void ISelfController::SetOutOfFocusSuspendingEnabled(HLERequestContext& ctx) {
-    // Takes 3 input u8s with each field located immediately after the previous
-    // u8, these are bool flags. No output.
     IPC::RequestParser rp{ctx};
 
-    bool enabled = rp.Pop<bool>();
+    const bool enabled = rp.Pop<bool>();
     LOG_WARNING(Service_AM, "(STUBBED) called enabled={}", enabled);
 
+    std::scoped_lock lk{applet->lock};
+    ASSERT(applet->type == AppletType::Application);
+    applet->out_of_focus_suspension_enabled = enabled;
+
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
 }
 
 void ISelfController::SetAlbumImageOrientation(HLERequestContext& ctx) {
-    LOG_WARNING(Service_AM, "(STUBBED) called");
+    IPC::RequestParser rp{ctx};
+
+    const auto orientation = rp.PopRaw<Capture::AlbumImageOrientation>();
+    LOG_WARNING(Service_AM, "(STUBBED) called, orientation={}", static_cast<s32>(orientation));
+
+    std::scoped_lock lk{applet->lock};
+    applet->album_image_orientation = orientation;
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
@@ -232,14 +237,13 @@ void ISelfController::SetAlbumImageOrientation(HLERequestContext& ctx) {
 void ISelfController::CreateManagedDisplayLayer(HLERequestContext& ctx) {
     LOG_WARNING(Service_AM, "(STUBBED) called");
 
-    // TODO(Subv): Find out how AM determines the display to use, for now just
-    // create the layer in the Default display.
-    const auto display_id = nvnflinger.OpenDisplay("Default");
-    const auto layer_id = nvnflinger.CreateLayer(*display_id);
+    u64 layer_id{};
+    applet->managed_layer_holder.Initialize(&nvnflinger);
+    applet->managed_layer_holder.CreateManagedDisplayLayer(&layer_id);
 
     IPC::ResponseBuilder rb{ctx, 4};
     rb.Push(ResultSuccess);
-    rb.Push(*layer_id);
+    rb.Push(layer_id);
 }
 
 void ISelfController::IsSystemBufferSharingEnabled(HLERequestContext& ctx) {
@@ -252,56 +256,46 @@ void ISelfController::IsSystemBufferSharingEnabled(HLERequestContext& ctx) {
 void ISelfController::GetSystemSharedLayerHandle(HLERequestContext& ctx) {
     LOG_WARNING(Service_AM, "(STUBBED) called");
 
+    u64 buffer_id, layer_id;
+    applet->system_buffer_manager.GetSystemSharedLayerHandle(&buffer_id, &layer_id);
+
     IPC::ResponseBuilder rb{ctx, 6};
     rb.Push(this->EnsureBufferSharingEnabled(ctx.GetThread().GetOwnerProcess()));
-    rb.Push<s64>(system_shared_buffer_id);
-    rb.Push<s64>(system_shared_layer_id);
+    rb.Push<s64>(buffer_id);
+    rb.Push<s64>(layer_id);
 }
 
 void ISelfController::GetSystemSharedBufferHandle(HLERequestContext& ctx) {
     LOG_WARNING(Service_AM, "(STUBBED) called");
 
+    u64 buffer_id, layer_id;
+    applet->system_buffer_manager.GetSystemSharedLayerHandle(&buffer_id, &layer_id);
+
     IPC::ResponseBuilder rb{ctx, 4};
     rb.Push(this->EnsureBufferSharingEnabled(ctx.GetThread().GetOwnerProcess()));
-    rb.Push<s64>(system_shared_buffer_id);
+    rb.Push<s64>(buffer_id);
 }
 
 Result ISelfController::EnsureBufferSharingEnabled(Kernel::KProcess* process) {
-    if (buffer_sharing_enabled) {
+    if (applet->system_buffer_manager.Initialize(&nvnflinger, process, applet->applet_id)) {
         return ResultSuccess;
     }
 
-    if (system.GetFrontendAppletHolder().GetCurrentAppletId() <= AppletId::Application) {
-        return VI::ResultOperationFailed;
-    }
-
-    const auto display_id = nvnflinger.OpenDisplay("Default");
-    const auto result = nvnflinger.GetSystemBufferManager().Initialize(
-        &system_shared_buffer_id, &system_shared_layer_id, *display_id);
-
-    if (result.IsSuccess()) {
-        buffer_sharing_enabled = true;
-    }
-
-    return result;
+    return VI::ResultOperationFailed;
 }
 
 void ISelfController::CreateManagedDisplaySeparableLayer(HLERequestContext& ctx) {
     LOG_WARNING(Service_AM, "(STUBBED) called");
 
-    // TODO(Subv): Find out how AM determines the display to use, for now just
-    // create the layer in the Default display.
-    // This calls nn::vi::CreateRecordingLayer() which creates another layer.
-    // Currently we do not support more than 1 layer per display, output 1 layer id for now.
-    // Outputting 1 layer id instead of the expected 2 has not been observed to cause any adverse
-    // side effects.
-    // TODO: Support multiple layers
-    const auto display_id = nvnflinger.OpenDisplay("Default");
-    const auto layer_id = nvnflinger.CreateLayer(*display_id);
+    u64 layer_id{};
+    u64 recording_layer_id{};
+    applet->managed_layer_holder.Initialize(&nvnflinger);
+    applet->managed_layer_holder.CreateManagedDisplaySeparableLayer(&layer_id, &recording_layer_id);
 
-    IPC::ResponseBuilder rb{ctx, 4};
+    IPC::ResponseBuilder rb{ctx, 6};
     rb.Push(ResultSuccess);
-    rb.Push(*layer_id);
+    rb.Push(layer_id);
+    rb.Push(recording_layer_id);
 }
 
 void ISelfController::SetHandlesRequestToDisplay(HLERequestContext& ctx) {
@@ -320,9 +314,12 @@ void ISelfController::ApproveToDisplay(HLERequestContext& ctx) {
 
 void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
-    idle_time_detection_extension = rp.Pop<u32>();
-    LOG_DEBUG(Service_AM, "(STUBBED) called idle_time_detection_extension={}",
-              idle_time_detection_extension);
+
+    const auto extension = rp.PopRaw<IdleTimeDetectionExtension>();
+    LOG_DEBUG(Service_AM, "(STUBBED) called extension={}", extension);
+
+    std::scoped_lock lk{applet->lock};
+    applet->idle_time_detection_extension = extension;
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
@@ -331,9 +328,11 @@ void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) {
 void ISelfController::GetIdleTimeDetectionExtension(HLERequestContext& ctx) {
     LOG_WARNING(Service_AM, "(STUBBED) called");
 
+    std::scoped_lock lk{applet->lock};
+
     IPC::ResponseBuilder rb{ctx, 3};
     rb.Push(ResultSuccess);
-    rb.Push<u32>(idle_time_detection_extension);
+    rb.PushRaw<IdleTimeDetectionExtension>(applet->idle_time_detection_extension);
 }
 
 void ISelfController::ReportUserIsActive(HLERequestContext& ctx) {
@@ -345,7 +344,9 @@ void ISelfController::ReportUserIsActive(HLERequestContext& ctx) {
 
 void ISelfController::SetAutoSleepDisabled(HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
-    is_auto_sleep_disabled = rp.Pop<bool>();
+
+    std::scoped_lock lk{applet->lock};
+    applet->auto_sleep_disabled = rp.Pop<bool>();
 
     // On the system itself, if the previous state of is_auto_sleep_disabled
     // differed from the current value passed in, it'd signify the internal
@@ -357,7 +358,7 @@ void ISelfController::SetAutoSleepDisabled(HLERequestContext& ctx) {
     // and it's sufficient to simply set the member variable for querying via
     // IsAutoSleepDisabled().
 
-    LOG_DEBUG(Service_AM, "called. is_auto_sleep_disabled={}", is_auto_sleep_disabled);
+    LOG_DEBUG(Service_AM, "called. is_auto_sleep_disabled={}", applet->auto_sleep_disabled);
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
@@ -366,20 +367,23 @@ void ISelfController::SetAutoSleepDisabled(HLERequestContext& ctx) {
 void ISelfController::IsAutoSleepDisabled(HLERequestContext& ctx) {
     LOG_DEBUG(Service_AM, "called.");
 
+    std::scoped_lock lk{applet->lock};
+
     IPC::ResponseBuilder rb{ctx, 3};
     rb.Push(ResultSuccess);
-    rb.Push(is_auto_sleep_disabled);
+    rb.Push(applet->auto_sleep_disabled);
 }
 
 void ISelfController::GetAccumulatedSuspendedTickValue(HLERequestContext& ctx) {
     LOG_DEBUG(Service_AM, "called.");
 
+    std::scoped_lock lk{applet->lock};
     // This command returns the total number of system ticks since ISelfController creation
     // where the game was suspended. Since Yuzu doesn't implement game suspension, this command
     // can just always return 0 ticks.
     IPC::ResponseBuilder rb{ctx, 4};
     rb.Push(ResultSuccess);
-    rb.Push<u64>(0);
+    rb.Push<u64>(applet->suspended_ticks);
 }
 
 void ISelfController::GetAccumulatedSuspendedTickChangedEvent(HLERequestContext& ctx) {
@@ -387,7 +391,7 @@ void ISelfController::GetAccumulatedSuspendedTickChangedEvent(HLERequestContext&
 
     IPC::ResponseBuilder rb{ctx, 2, 1};
     rb.Push(ResultSuccess);
-    rb.PushCopyObjects(accumulated_suspended_tick_changed_event->GetReadableEvent());
+    rb.PushCopyObjects(applet->accumulated_suspended_tick_changed_event.GetHandle());
 }
 
 void ISelfController::SetAlbumImageTakenNotificationEnabled(HLERequestContext& ctx) {
@@ -396,10 +400,11 @@ void ISelfController::SetAlbumImageTakenNotificationEnabled(HLERequestContext& c
     // This service call sets an internal flag whether a notification is shown when an image is
     // captured. Currently we do not support capturing images via the capture button, so this can be
     // stubbed for now.
-    const bool album_image_taken_notification_enabled = rp.Pop<bool>();
+    const bool enabled = rp.Pop<bool>();
+    LOG_WARNING(Service_AM, "(STUBBED) called. enabled={}", enabled);
 
-    LOG_WARNING(Service_AM, "(STUBBED) called. album_image_taken_notification_enabled={}",
-                album_image_taken_notification_enabled);
+    std::scoped_lock lk{applet->lock};
+    applet->album_image_taken_notification_enabled = enabled;
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
@@ -427,9 +432,11 @@ void ISelfController::SaveCurrentScreenshot(HLERequestContext& ctx) {
 void ISelfController::SetRecordVolumeMuted(HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
 
-    const auto is_record_volume_muted = rp.Pop<bool>();
+    const auto enabled = rp.Pop<bool>();
+    LOG_WARNING(Service_AM, "(STUBBED) called. enabled={}", enabled);
 
-    LOG_WARNING(Service_AM, "(STUBBED) called. is_record_volume_muted={}", is_record_volume_muted);
+    std::scoped_lock lk{applet->lock};
+    applet->record_volume_muted = enabled;
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/am/self_controller.h b/src/core/hle/service/am/self_controller.h
index f157bb826..6e6975264 100644
--- a/src/core/hle/service/am/self_controller.h
+++ b/src/core/hle/service/am/self_controller.h
@@ -8,9 +8,12 @@
 
 namespace Service::AM {
 
+struct Applet;
+
 class ISelfController final : public ServiceFramework<ISelfController> {
 public:
-    explicit ISelfController(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger_);
+    explicit ISelfController(Core::System& system_, std::shared_ptr<Applet> applet_,
+                             Nvnflinger::Nvnflinger& nvnflinger_);
     ~ISelfController() override;
 
 private:
@@ -47,26 +50,8 @@ private:
 
     Result EnsureBufferSharingEnabled(Kernel::KProcess* process);
 
-    enum class ScreenshotPermission : u32 {
-        Inherit = 0,
-        Enable = 1,
-        Disable = 2,
-    };
-
     Nvnflinger::Nvnflinger& nvnflinger;
-
-    KernelHelpers::ServiceContext service_context;
-
-    Kernel::KEvent* launchable_event;
-    Kernel::KEvent* accumulated_suspended_tick_changed_event;
-
-    u32 idle_time_detection_extension = 0;
-    u64 num_fatal_sections_entered = 0;
-    u64 system_shared_buffer_id = 0;
-    u64 system_shared_layer_id = 0;
-    bool is_auto_sleep_disabled = false;
-    bool buffer_sharing_enabled = false;
-    ScreenshotPermission screenshot_permission = ScreenshotPermission::Inherit;
+    const std::shared_ptr<Applet> applet;
 };
 
 } // namespace Service::AM
diff --git a/src/core/hle/service/am/system_applet_proxy.cpp b/src/core/hle/service/am/system_applet_proxy.cpp
index d51a2c8db..e3013271b 100644
--- a/src/core/hle/service/am/system_applet_proxy.cpp
+++ b/src/core/hle/service/am/system_applet_proxy.cpp
@@ -20,10 +20,9 @@
 namespace Service::AM {
 
 ISystemAppletProxy::ISystemAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_,
-                                       std::shared_ptr<AppletMessageQueue> msg_queue_,
-                                       Core::System& system_)
-    : ServiceFramework{system_, "ISystemAppletProxy"}, nvnflinger{nvnflinger_},
-      msg_queue{std::move(msg_queue_)} {
+                                       std::shared_ptr<Applet> applet_, Core::System& system_)
+    : ServiceFramework{system_, "ISystemAppletProxy"}, nvnflinger{nvnflinger_}, applet{std::move(
+                                                                                    applet_)} {
     // clang-format off
     static const FunctionInfo functions[] = {
         {0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
@@ -44,12 +43,14 @@ ISystemAppletProxy::ISystemAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_,
     RegisterHandlers(functions);
 }
 
+ISystemAppletProxy::~ISystemAppletProxy() = default;
+
 void ISystemAppletProxy::GetCommonStateGetter(HLERequestContext& ctx) {
     LOG_DEBUG(Service_AM, "called");
 
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     rb.Push(ResultSuccess);
-    rb.PushIpcInterface<ICommonStateGetter>(system, msg_queue);
+    rb.PushIpcInterface<ICommonStateGetter>(system, applet);
 }
 
 void ISystemAppletProxy::GetSelfController(HLERequestContext& ctx) {
@@ -57,7 +58,7 @@ void ISystemAppletProxy::GetSelfController(HLERequestContext& ctx) {
 
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     rb.Push(ResultSuccess);
-    rb.PushIpcInterface<ISelfController>(system, nvnflinger);
+    rb.PushIpcInterface<ISelfController>(system, applet, nvnflinger);
 }
 
 void ISystemAppletProxy::GetWindowController(HLERequestContext& ctx) {
@@ -65,7 +66,7 @@ void ISystemAppletProxy::GetWindowController(HLERequestContext& ctx) {
 
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     rb.Push(ResultSuccess);
-    rb.PushIpcInterface<IWindowController>(system);
+    rb.PushIpcInterface<IWindowController>(system, applet);
 }
 
 void ISystemAppletProxy::GetAudioController(HLERequestContext& ctx) {
@@ -89,7 +90,7 @@ void ISystemAppletProxy::GetLibraryAppletCreator(HLERequestContext& ctx) {
 
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     rb.Push(ResultSuccess);
-    rb.PushIpcInterface<ILibraryAppletCreator>(system);
+    rb.PushIpcInterface<ILibraryAppletCreator>(system, applet);
 }
 
 void ISystemAppletProxy::GetHomeMenuFunctions(HLERequestContext& ctx) {
@@ -121,7 +122,7 @@ void ISystemAppletProxy::GetAppletCommonFunctions(HLERequestContext& ctx) {
 
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     rb.Push(ResultSuccess);
-    rb.PushIpcInterface<IAppletCommonFunctions>(system);
+    rb.PushIpcInterface<IAppletCommonFunctions>(system, applet);
 }
 
 void ISystemAppletProxy::GetDebugFunctions(HLERequestContext& ctx) {
diff --git a/src/core/hle/service/am/system_applet_proxy.h b/src/core/hle/service/am/system_applet_proxy.h
index b8855b1d6..0390cd1e5 100644
--- a/src/core/hle/service/am/system_applet_proxy.h
+++ b/src/core/hle/service/am/system_applet_proxy.h
@@ -8,11 +8,13 @@
 
 namespace Service::AM {
 
+struct Applet;
+
 class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> {
 public:
     explicit ISystemAppletProxy(Nvnflinger::Nvnflinger& nvnflinger_,
-                                std::shared_ptr<AppletMessageQueue> msg_queue_,
-                                Core::System& system_);
+                                std::shared_ptr<Applet> applet_, Core::System& system_);
+    ~ISystemAppletProxy();
 
 private:
     void GetCommonStateGetter(HLERequestContext& ctx);
@@ -28,7 +30,7 @@ private:
     void GetDebugFunctions(HLERequestContext& ctx);
 
     Nvnflinger::Nvnflinger& nvnflinger;
-    std::shared_ptr<AppletMessageQueue> msg_queue;
+    std::shared_ptr<Applet> applet;
 };
 
 } // namespace Service::AM
diff --git a/src/core/hle/service/am/window_controller.cpp b/src/core/hle/service/am/window_controller.cpp
index f2ba3c134..430ca180b 100644
--- a/src/core/hle/service/am/window_controller.cpp
+++ b/src/core/hle/service/am/window_controller.cpp
@@ -1,13 +1,14 @@
 // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
 // SPDX-License-Identifier: GPL-2.0-or-later
 
+#include "core/hle/service/am/applet.h"
 #include "core/hle/service/am/window_controller.h"
 #include "core/hle/service/ipc_helpers.h"
 
 namespace Service::AM {
 
-IWindowController::IWindowController(Core::System& system_)
-    : ServiceFramework{system_, "IWindowController"} {
+IWindowController::IWindowController(Core::System& system_, std::shared_ptr<Applet> applet_)
+    : ServiceFramework{system_, "IWindowController"}, applet{std::move(applet_)} {
     // clang-format off
     static const FunctionInfo functions[] = {
         {0, nullptr, "CreateWindow"},
@@ -27,23 +28,22 @@ IWindowController::IWindowController(Core::System& system_)
 IWindowController::~IWindowController() = default;
 
 void IWindowController::GetAppletResourceUserId(HLERequestContext& ctx) {
-    const u64 process_id = system.ApplicationProcess()->GetProcessId();
-
-    LOG_DEBUG(Service_AM, "called. Process ID=0x{:016X}", process_id);
-
     IPC::ResponseBuilder rb{ctx, 4};
     rb.Push(ResultSuccess);
-    rb.Push<u64>(process_id);
+    rb.Push<u64>(applet->aruid);
 }
 
 void IWindowController::GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx) {
-    const u64 process_id = 0;
+    u64 aruid = 0;
+    if (auto caller = applet->caller_applet.lock(); caller) {
+        aruid = caller->aruid;
+    }
 
     LOG_WARNING(Service_AM, "(STUBBED) called");
 
     IPC::ResponseBuilder rb{ctx, 4};
     rb.Push(ResultSuccess);
-    rb.Push<u64>(process_id);
+    rb.Push<u64>(aruid);
 }
 
 void IWindowController::AcquireForegroundRights(HLERequestContext& ctx) {
diff --git a/src/core/hle/service/am/window_controller.h b/src/core/hle/service/am/window_controller.h
index 07b0e0e17..d97f93737 100644
--- a/src/core/hle/service/am/window_controller.h
+++ b/src/core/hle/service/am/window_controller.h
@@ -7,15 +7,19 @@
 
 namespace Service::AM {
 
+struct Applet;
+
 class IWindowController final : public ServiceFramework<IWindowController> {
 public:
-    explicit IWindowController(Core::System& system_);
+    explicit IWindowController(Core::System& system_, std::shared_ptr<Applet> applet_);
     ~IWindowController() override;
 
 private:
     void GetAppletResourceUserId(HLERequestContext& ctx);
     void GetAppletResourceUserIdOfCallerApplet(HLERequestContext& ctx);
     void AcquireForegroundRights(HLERequestContext& ctx);
+
+    const std::shared_ptr<Applet> applet;
 };
 
 } // namespace Service::AM
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 1f3d82c57..73058db9a 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -535,6 +535,12 @@ public:
         RegisterHandlers(functions);
     }
 
+    ~IApplicationDisplayService() {
+        for (const auto layer_id : stray_layer_ids) {
+            nvnflinger.DestroyLayer(layer_id);
+        }
+    }
+
 private:
     enum class ConvertedScaleMode : u64 {
         Freeze = 0,
@@ -770,6 +776,7 @@ private:
             return;
         }
 
+        stray_layer_ids.push_back(*layer_id);
         const auto buffer_queue_id = nvnflinger.FindBufferQueueId(display_id, *layer_id);
         if (!buffer_queue_id) {
             LOG_ERROR(Service_VI, "Buffer queue id not found! display_id={}", display_id);
@@ -916,6 +923,7 @@ private:
 
     Nvnflinger::Nvnflinger& nvnflinger;
     Nvnflinger::HosBinderDriverServer& hos_binder_driver_server;
+    std::vector<u64> stray_layer_ids;
     bool vsync_event_fetched{false};
 };
 
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index 718534ba1..e28df10bd 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -9,6 +9,7 @@
 #include "core/core.h"
 #include "core/hle/service/am/am.h"
 #include "core/hle/service/am/applet_ae.h"
+#include "core/hle/service/am/applet_manager.h"
 #include "core/hle/service/am/applet_message_queue.h"
 #include "core/hle/service/am/applet_oe.h"
 #include "core/hle/service/sm/sm.h"
@@ -48,22 +49,8 @@ void OnDockedModeChanged(bool last_state, bool new_state, Core::System& system)
     if (!system.IsPoweredOn()) {
         return;
     }
-    Service::SM::ServiceManager& sm = system.ServiceManager();
 
-    // Message queue is shared between these services, we just need to signal an operation
-    // change to one and it will handle both automatically
-    auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
-    auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
-    bool has_signalled = false;
-
-    if (applet_oe != nullptr) {
-        applet_oe->GetMessageQueue()->OperationModeChanged();
-        has_signalled = true;
-    }
-
-    if (applet_ae != nullptr && !has_signalled) {
-        applet_ae->GetMessageQueue()->OperationModeChanged();
-    }
+    system.GetAppletManager().OperationModeChanged();
 }
 
 ConfigureInput::ConfigureInput(Core::System& system_, QWidget* parent)
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 02508b20d..4e5c4da53 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -4783,36 +4783,12 @@ void GMainWindow::RequestGameExit() {
         return;
     }
 
-    auto& sm{system->ServiceManager()};
-    auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
-    auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
-    bool has_signalled = false;
-
     system->SetExitRequested(true);
-
-    if (applet_oe != nullptr) {
-        applet_oe->GetMessageQueue()->RequestExit();
-        has_signalled = true;
-    }
-
-    if (applet_ae != nullptr && !has_signalled) {
-        applet_ae->GetMessageQueue()->RequestExit();
-    }
+    system->GetAppletManager().RequestExit();
 }
 
 void GMainWindow::RequestGameResume() {
-    auto& sm{system->ServiceManager()};
-    auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
-    auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
-
-    if (applet_oe != nullptr) {
-        applet_oe->GetMessageQueue()->RequestResume();
-        return;
-    }
-
-    if (applet_ae != nullptr) {
-        applet_ae->GetMessageQueue()->RequestResume();
-    }
+    system->GetAppletManager().RequestResume();
 }
 
 void GMainWindow::filterBarSetChecked(bool state) {