diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 0109fa2b26..58d94768c3 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -34,8 +34,6 @@ static bool shared_font_loaded = false; static bool shared_font_relocated = false; static Kernel::SharedPtr lock; -static Kernel::SharedPtr notification_event; ///< APT notification event -static Kernel::SharedPtr parameter_event; ///< APT parameter event static u32 cpu_percent; ///< CPU time available to the running application @@ -44,32 +42,160 @@ static u8 unknown_ns_state_field; static ScreencapPostPermission screen_capture_post_permission; -/// Parameter data to be returned in the next call to Glance/ReceiveParameter +/// Parameter data to be returned in the next call to Glance/ReceiveParameter. +/// TODO(Subv): Use std::optional once we migrate to C++17. static boost::optional next_parameter; +enum class AppletPos { Application = 0, Library = 1, System = 2, SysLibrary = 3, Resident = 4 }; + +static constexpr size_t NumAppletSlot = 4; + +enum class AppletSlot : u8 { + Application, + SystemApplet, + HomeMenu, + LibraryApplet, + + // An invalid tag + Error, +}; + +union AppletAttributes { + u32 raw; + + BitField<0, 3, u32> applet_pos; + + AppletAttributes() : raw(0) {} + AppletAttributes(u32 attributes) : raw(attributes) {} +}; + +struct AppletSlotData { + AppletId applet_id; + AppletSlot slot; + bool registered; + AppletAttributes attributes; + Kernel::SharedPtr notification_event; + Kernel::SharedPtr parameter_event; +}; + +// Holds data about the concurrently running applets in the system. +static std::array applet_slots = {}; + +// This overload returns nullptr if no applet with the specified id has been started. +static AppletSlotData* GetAppletSlotData(AppletId id) { + auto GetSlot = [](AppletSlot slot) -> AppletSlotData* { + return &applet_slots[static_cast(slot)]; + }; + + if (id == AppletId::Application) { + auto* slot = GetSlot(AppletSlot::Application); + if (slot->applet_id != AppletId::None) + return slot; + + return nullptr; + } + + if (id == AppletId::AnySystemApplet) { + auto* system_slot = GetSlot(AppletSlot::SystemApplet); + if (system_slot->applet_id != AppletId::None) + return system_slot; + + // The Home Menu is also a system applet, but it lives in its own slot to be able to run + // concurrently with other system applets. + auto* home_slot = GetSlot(AppletSlot::HomeMenu); + if (home_slot->applet_id != AppletId::None) + return home_slot; + + return nullptr; + } + + if (id == AppletId::AnyLibraryApplet || id == AppletId::AnySysLibraryApplet) { + auto* slot = GetSlot(AppletSlot::LibraryApplet); + if (slot->applet_id == AppletId::None) + return nullptr; + + u32 applet_pos = slot->attributes.applet_pos; + + if (id == AppletId::AnyLibraryApplet && applet_pos == static_cast(AppletPos::Library)) + return slot; + + if (id == AppletId::AnySysLibraryApplet && + applet_pos == static_cast(AppletPos::SysLibrary)) + return slot; + + return nullptr; + } + + if (id == AppletId::HomeMenu || id == AppletId::AlternateMenu) { + auto* slot = GetSlot(AppletSlot::HomeMenu); + if (slot->applet_id != AppletId::None) + return slot; + + return nullptr; + } + + for (auto& slot : applet_slots) { + if (slot.applet_id == id) + return &slot; + } + + return nullptr; +} + +static AppletSlotData* GetAppletSlotData(AppletAttributes attributes) { + // Mapping from AppletPos to AppletSlot + static constexpr std::array applet_position_slots = { + AppletSlot::Application, AppletSlot::LibraryApplet, AppletSlot::SystemApplet, + AppletSlot::LibraryApplet, AppletSlot::Error, AppletSlot::LibraryApplet}; + + u32 applet_pos = attributes.applet_pos; + if (applet_pos >= applet_position_slots.size()) + return nullptr; + + AppletSlot slot = applet_position_slots[applet_pos]; + + if (slot == AppletSlot::Error) + return nullptr; + + return &applet_slots[static_cast(slot)]; +} + void SendParameter(const MessageParameter& parameter) { next_parameter = parameter; - // Signal the event to let the application know that a new parameter is ready to be read - parameter_event->Signal(); + // Signal the event to let the receiver know that a new parameter is ready to be read + auto* const slot_data = GetAppletSlotData(static_cast(parameter.destination_id)); + ASSERT(slot_data); + + slot_data->parameter_event->Signal(); } void Initialize(Service::Interface* self) { IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x2, 2, 0); // 0x20080 u32 app_id = rp.Pop(); - u32 flags = rp.Pop(); + u32 attributes = rp.Pop(); + + LOG_DEBUG(Service_APT, "called app_id=0x%08X, attributes=0x%08X", app_id, attributes); + + auto* const slot_data = GetAppletSlotData(attributes); + + // Note: The real NS service does not check if the attributes value is valid before accessing + // the data in the array + ASSERT_MSG(slot_data, "Invalid application attributes"); + + if (slot_data->registered) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::AlreadyExists, ErrorModule::Applet, + ErrorSummary::InvalidState, ErrorLevel::Status)); + return; + } + + slot_data->applet_id = static_cast(app_id); + slot_data->attributes.raw = attributes; + IPC::RequestBuilder rb = rp.MakeBuilder(1, 3); rb.Push(RESULT_SUCCESS); - rb.PushCopyHandles(Kernel::g_handle_table.Create(notification_event).Unwrap(), - Kernel::g_handle_table.Create(parameter_event).Unwrap()); - - // TODO(bunnei): Check if these events are cleared every time Initialize is called. - notification_event->Clear(); - parameter_event->Clear(); - - ASSERT_MSG((nullptr != lock), "Cannot initialize without lock"); - lock->Release(); - - LOG_DEBUG(Service_APT, "called app_id=0x%08X, flags=0x%08X", app_id, flags); + rb.PushCopyHandles(Kernel::g_handle_table.Create(slot_data->notification_event).Unwrap(), + Kernel::g_handle_table.Create(slot_data->parameter_event).Unwrap()); } void GetSharedFont(Service::Interface* self) { @@ -120,7 +246,12 @@ void GetLockHandle(Service::Interface* self) { // this will cause the app to wait until parameter_event is signaled. u32 applet_attributes = rp.Pop(); IPC::RequestBuilder rb = rp.MakeBuilder(3, 2); - rb.Push(RESULT_SUCCESS); // No error + rb.Push(RESULT_SUCCESS); // No error + + // TODO(Subv): The output attributes should have an AppletPos of either Library or System | + // Library (depending on the type of the last launched applet) if the input attributes' + // AppletPos has the Library bit set. + rb.Push(applet_attributes); // Applet Attributes, this value is passed to Enable. rb.Push(0); // Least significant bit = power button state Kernel::Handle handle_copy = Kernel::g_handle_table.Create(lock).Unwrap(); @@ -133,10 +264,22 @@ void GetLockHandle(Service::Interface* self) { void Enable(Service::Interface* self) { IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x3, 1, 0); // 0x30040 u32 attributes = rp.Pop(); + + LOG_DEBUG(Service_APT, "called attributes=0x%08X", attributes); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(RESULT_SUCCESS); // No error - parameter_event->Signal(); // Let the application know that it has been started - LOG_WARNING(Service_APT, "(STUBBED) called attributes=0x%08X", attributes); + + auto* const slot_data = GetAppletSlotData(attributes); + + if (!slot_data) { + rb.Push(ResultCode(ErrCodes::InvalidAppletSlot, ErrorModule::Applet, + ErrorSummary::InvalidState, ErrorLevel::Status)); + return; + } + + slot_data->registered = true; + + rb.Push(RESULT_SUCCESS); } void GetAppletManInfo(Service::Interface* self) { @@ -154,22 +297,27 @@ void GetAppletManInfo(Service::Interface* self) { void IsRegistered(Service::Interface* self) { IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x9, 1, 0); // 0x90040 - u32 app_id = rp.Pop(); + AppletId app_id = static_cast(rp.Pop()); IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); // No error - // TODO(Subv): An application is considered "registered" if it has already called APT::Enable - // handle this properly once we implement multiprocess support. - bool is_registered = false; // Set to not registered by default + auto* const slot_data = GetAppletSlotData(app_id); - if (app_id == static_cast(AppletId::AnyLibraryApplet)) { - is_registered = HLE::Applets::IsLibraryAppletRunning(); - } else if (auto applet = HLE::Applets::Applet::Get(static_cast(app_id))) { - is_registered = true; // Set to registered + // Check if an LLE applet was registered first, then fallback to HLE applets + bool is_registered = slot_data && slot_data->registered; + + if (!is_registered) { + if (app_id == AppletId::AnyLibraryApplet) { + is_registered = HLE::Applets::IsLibraryAppletRunning(); + } else if (auto applet = HLE::Applets::Applet::Get(app_id)) { + // The applet exists, set it as registered. + is_registered = true; + } } + rb.Push(is_registered); - LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id); + LOG_DEBUG(Service_APT, "called app_id=0x%08X", static_cast(app_id)); } void InquireNotification(Service::Interface* self) { @@ -864,14 +1012,23 @@ void Init() { screen_capture_post_permission = ScreencapPostPermission::CleanThePermission; // TODO(JamePeng): verify the initial value - // TODO(bunnei): Check if these are created in Initialize or on APT process startup. - notification_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Notification"); - parameter_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Start"); + for (size_t slot = 0; slot < applet_slots.size(); ++slot) { + auto& slot_data = applet_slots[slot]; + slot_data.slot = static_cast(slot); + slot_data.applet_id = AppletId::None; + slot_data.attributes.raw = 0; + slot_data.registered = false; + slot_data.notification_event = + Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Notification"); + slot_data.parameter_event = + Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Parameter"); + } // Initialize the parameter to wake up the application. next_parameter.emplace(); next_parameter->signal = static_cast(SignalType::Wakeup); next_parameter->destination_id = static_cast(AppletId::Application); + applet_slots[static_cast(AppletSlot::Application)].parameter_event->Signal(); } void Shutdown() { @@ -879,8 +1036,12 @@ void Shutdown() { shared_font_loaded = false; shared_font_relocated = false; lock = nullptr; - notification_event = nullptr; - parameter_event = nullptr; + + for (auto& slot : applet_slots) { + slot.registered = false; + slot.notification_event = nullptr; + slot.parameter_event = nullptr; + } next_parameter = boost::none; diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index 1067548534..96b28b438a 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h @@ -72,6 +72,8 @@ enum class SignalType : u32 { /// App Id's used by APT functions enum class AppletId : u32 { + None = 0, + AnySystemApplet = 0x100, HomeMenu = 0x101, AlternateMenu = 0x103, Camera = 0x110, @@ -83,6 +85,7 @@ enum class AppletId : u32 { Miiverse = 0x117, MiiversePost = 0x118, AmiiboSettings = 0x119, + AnySysLibraryApplet = 0x200, SoftwareKeyboard1 = 0x201, Ed1 = 0x202, PnoteApp = 0x204, @@ -119,8 +122,9 @@ enum class ScreencapPostPermission : u32 { namespace ErrCodes { enum { ParameterPresent = 2, + InvalidAppletSlot = 4, }; -} +} // namespace ErrCodes /// Send a parameter to the currently-running application, which will read it via ReceiveParameter void SendParameter(const MessageParameter& parameter);