From e1d82a13f33304a9064387ff8f88c57c27c2ea05 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 12 Nov 2025 16:18:13 -0700 Subject: [PATCH] erpt: implement new 21.0.0 commands --- .../erpt/sf/erpt_sf_i_context.hpp | 49 +++++----- .../stratosphere/erpt/srv/erpt_srv_types.hpp | 4 + .../source/erpt/srv/erpt_srv_context_impl.cpp | 94 +++++++++++++++++++ .../source/erpt/srv/erpt_srv_context_impl.hpp | 5 + .../source/erpt/srv/erpt_srv_reporter.cpp | 44 ++++++++- .../source/erpt/srv/erpt_srv_reporter.hpp | 3 + 6 files changed, 175 insertions(+), 24 deletions(-) diff --git a/libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_context.hpp b/libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_context.hpp index a4fcc4dc6..d1244bb46 100644 --- a/libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_context.hpp +++ b/libraries/libstratosphere/include/stratosphere/erpt/sf/erpt_sf_i_context.hpp @@ -20,28 +20,33 @@ #include #include -#define AMS_ERPT_I_CONTEXT_INTERFACE_INFO(C, H) \ - AMS_SF_METHOD_INFO(C, H, 0, Result, SubmitContext, (const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &str_buffer), (ctx_buffer, str_buffer)) \ - AMS_SF_METHOD_INFO(C, H, 1, Result, CreateReportV0, (erpt::ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &str_buffer, const ams::sf::InBuffer &meta_buffer), (report_type, ctx_buffer, str_buffer, meta_buffer)) \ - AMS_SF_METHOD_INFO(C, H, 2, Result, SetInitialLaunchSettingsCompletionTime, (const time::SteadyClockTimePoint &time_point), (time_point), hos::Version_3_0_0) \ - AMS_SF_METHOD_INFO(C, H, 3, Result, ClearInitialLaunchSettingsCompletionTime, (), (), hos::Version_3_0_0) \ - AMS_SF_METHOD_INFO(C, H, 4, Result, UpdatePowerOnTime, (), (), hos::Version_3_0_0) \ - AMS_SF_METHOD_INFO(C, H, 5, Result, UpdateAwakeTime, (), (), hos::Version_3_0_0, hos::Version_12_0_0) \ - AMS_SF_METHOD_INFO(C, H, 6, Result, SubmitMultipleCategoryContext, (const erpt::MultipleCategoryContextEntry &ctx_entry, const ams::sf::InBuffer &str_buffer), (ctx_entry, str_buffer), hos::Version_5_0_0, hos::Version_12_0_0) \ - AMS_SF_METHOD_INFO(C, H, 7, Result, UpdateApplicationLaunchTime, (), (), hos::Version_6_0_0) \ - AMS_SF_METHOD_INFO(C, H, 8, Result, ClearApplicationLaunchTime, (), (), hos::Version_6_0_0) \ - AMS_SF_METHOD_INFO(C, H, 9, Result, SubmitAttachment, (ams::sf::Out out, const ams::sf::InBuffer &attachment_name, const ams::sf::InBuffer &attachment_data), (out, attachment_name, attachment_data), hos::Version_8_0_0) \ - AMS_SF_METHOD_INFO(C, H, 10, Result, CreateReportWithAttachmentsDeprecated, (erpt::ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &str_buffer, const ams::sf::InBuffer &attachment_ids_buffer), (report_type, ctx_buffer, str_buffer, attachment_ids_buffer), hos::Version_8_0_0, hos::Version_10_2_0) \ - AMS_SF_METHOD_INFO(C, H, 10, Result, CreateReportWithAttachmentsDeprecated2, (erpt::ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &str_buffer, const ams::sf::InBuffer &attachment_ids_buffer, Result result), (report_type, ctx_buffer, str_buffer, attachment_ids_buffer, result), hos::Version_11_0_0, hos::Version_16_1_0) \ - AMS_SF_METHOD_INFO(C, H, 10, Result, CreateReportWithAttachments, (erpt::ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &str_buffer, const ams::sf::InBuffer &attachment_ids_buffer, Result result, erpt::CreateReportOptionFlagSet flags), (report_type, ctx_buffer, str_buffer, attachment_ids_buffer, result, flags), hos::Version_17_0_0) \ - AMS_SF_METHOD_INFO(C, H, 11, Result, CreateReportV1, (erpt::ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &str_buffer, const ams::sf::InBuffer &meta_buffer, Result result), (report_type, ctx_buffer, str_buffer, meta_buffer, result), hos::Version_11_0_0) \ - AMS_SF_METHOD_INFO(C, H, 12, Result, CreateReport, (erpt::ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &str_buffer, const ams::sf::InBuffer &meta_buffer, Result result, erpt::CreateReportOptionFlagSet flags), (report_type, ctx_buffer, str_buffer, meta_buffer, result, flags), hos::Version_17_0_0) \ - AMS_SF_METHOD_INFO(C, H, 13, Result, SubmitAttachmentWithLz4Compression, (ams::sf::Out out, const ams::sf::InBuffer &attachment_name, const ams::sf::InBuffer &attachment_data), (out, attachment_name, attachment_data), hos::Version_20_0_0) \ - AMS_SF_METHOD_INFO(C, H, 14, Result, CreateReportWithSpecifiedReprotId, (erpt::ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &str_buffer, const ams::sf::InBuffer &meta_buffer, const ams::sf::InBuffer &attachment_ids_buffer, Result result, erpt::CreateReportOptionFlagSet flags, const erpt::ReportId &report_id), (report_type, ctx_buffer, str_buffer, meta_buffer, attachment_ids_buffer, result, flags, report_id), hos::Version_20_0_0) \ - AMS_SF_METHOD_INFO(C, H, 20, Result, RegisterRunningApplet, (ncm::ProgramId program_id), (program_id), hos::Version_12_0_0) \ - AMS_SF_METHOD_INFO(C, H, 21, Result, UnregisterRunningApplet, (ncm::ProgramId program_id), (program_id), hos::Version_12_0_0) \ - AMS_SF_METHOD_INFO(C, H, 22, Result, UpdateAppletSuspendedDuration, (ncm::ProgramId program_id, TimeSpanType duration), (program_id, duration), hos::Version_12_0_0) \ - AMS_SF_METHOD_INFO(C, H, 30, Result, InvalidateForcedShutdownDetection, (), (), hos::Version_12_0_0) +#define AMS_ERPT_I_CONTEXT_INTERFACE_INFO(C, H) \ + AMS_SF_METHOD_INFO(C, H, 0, Result, SubmitContext, (const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &str_buffer), (ctx_buffer, str_buffer)) \ + AMS_SF_METHOD_INFO(C, H, 1, Result, CreateReportV0, (erpt::ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &str_buffer, const ams::sf::InBuffer &meta_buffer), (report_type, ctx_buffer, str_buffer, meta_buffer)) \ + AMS_SF_METHOD_INFO(C, H, 2, Result, SetInitialLaunchSettingsCompletionTime, (const time::SteadyClockTimePoint &time_point), (time_point), hos::Version_3_0_0) \ + AMS_SF_METHOD_INFO(C, H, 3, Result, ClearInitialLaunchSettingsCompletionTime, (), (), hos::Version_3_0_0) \ + AMS_SF_METHOD_INFO(C, H, 4, Result, UpdatePowerOnTime, (), (), hos::Version_3_0_0) \ + AMS_SF_METHOD_INFO(C, H, 5, Result, UpdateAwakeTime, (), (), hos::Version_3_0_0, hos::Version_12_0_0) \ + AMS_SF_METHOD_INFO(C, H, 5, Result, CreateReportWithAdditionalContext, (erpt::ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &str_buffer, const ams::sf::InBuffer &meta_buffer, Result result, erpt::CreateReportOptionFlagSet flags, const ams::sf::InMapAliasArray &category_entries, const ams::sf::InMapAliasArray &field_entries, const ams::sf::InBuffer &array_buffer), (report_type, ctx_buffer, str_buffer, meta_buffer, result, flags, category_entries, field_entries, array_buffer), hos::Version_21_0_0) \ + AMS_SF_METHOD_INFO(C, H, 6, Result, SubmitMultipleCategoryContext, (const erpt::MultipleCategoryContextEntry &ctx_entry, const ams::sf::InBuffer &str_buffer), (ctx_entry, str_buffer), hos::Version_5_0_0, hos::Version_12_0_0) \ + AMS_SF_METHOD_INFO(C, H, 6, Result, SubmitMultipleContext, (const ams::sf::InMapAliasArray &category_entries, const ams::sf::InMapAliasArray &field_entries, const ams::sf::InBuffer &array_buffer), (category_entries, field_entries, array_buffer), hos::Version_21_0_0) \ + AMS_SF_METHOD_INFO(C, H, 7, Result, UpdateApplicationLaunchTime, (), (), hos::Version_6_0_0, hos::Version_20_5_0) \ + AMS_SF_METHOD_INFO(C, H, 7, Result, RegisterRunningApplicationInfo, (ncm::ApplicationId app_id, ncm::ProgramId program_id), (app_id, program_id), hos::Version_21_0_0) \ + AMS_SF_METHOD_INFO(C, H, 8, Result, ClearApplicationLaunchTime, (), (), hos::Version_6_0_0, hos::Version_20_5_0) \ + AMS_SF_METHOD_INFO(C, H, 8, Result, UnregisterRunningApplicationInfo, (), (), hos::Version_21_0_0) \ + AMS_SF_METHOD_INFO(C, H, 9, Result, SubmitAttachment, (ams::sf::Out out, const ams::sf::InBuffer &attachment_name, const ams::sf::InBuffer &attachment_data), (out, attachment_name, attachment_data), hos::Version_8_0_0) \ + AMS_SF_METHOD_INFO(C, H, 10, Result, CreateReportWithAttachmentsDeprecated, (erpt::ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &str_buffer, const ams::sf::InBuffer &attachment_ids_buffer), (report_type, ctx_buffer, str_buffer, attachment_ids_buffer), hos::Version_8_0_0, hos::Version_10_2_0) \ + AMS_SF_METHOD_INFO(C, H, 10, Result, CreateReportWithAttachmentsDeprecated2, (erpt::ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &str_buffer, const ams::sf::InBuffer &attachment_ids_buffer, Result result), (report_type, ctx_buffer, str_buffer, attachment_ids_buffer, result), hos::Version_11_0_0, hos::Version_16_1_0) \ + AMS_SF_METHOD_INFO(C, H, 10, Result, CreateReportWithAttachments, (erpt::ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &str_buffer, const ams::sf::InBuffer &attachment_ids_buffer, Result result, erpt::CreateReportOptionFlagSet flags), (report_type, ctx_buffer, str_buffer, attachment_ids_buffer, result, flags), hos::Version_17_0_0) \ + AMS_SF_METHOD_INFO(C, H, 11, Result, CreateReportV1, (erpt::ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &str_buffer, const ams::sf::InBuffer &meta_buffer, Result result), (report_type, ctx_buffer, str_buffer, meta_buffer, result), hos::Version_11_0_0) \ + AMS_SF_METHOD_INFO(C, H, 12, Result, CreateReport, (erpt::ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &str_buffer, const ams::sf::InBuffer &meta_buffer, Result result, erpt::CreateReportOptionFlagSet flags), (report_type, ctx_buffer, str_buffer, meta_buffer, result, flags), hos::Version_17_0_0) \ + AMS_SF_METHOD_INFO(C, H, 13, Result, SubmitAttachmentWithLz4Compression, (ams::sf::Out out, const ams::sf::InBuffer &attachment_name, const ams::sf::InBuffer &attachment_data), (out, attachment_name, attachment_data), hos::Version_20_0_0) \ + AMS_SF_METHOD_INFO(C, H, 14, Result, CreateReportWithSpecifiedReprotId, (erpt::ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &str_buffer, const ams::sf::InBuffer &meta_buffer, const ams::sf::InBuffer &attachment_ids_buffer, Result result, erpt::CreateReportOptionFlagSet flags, const erpt::ReportId &report_id), (report_type, ctx_buffer, str_buffer, meta_buffer, attachment_ids_buffer, result, flags, report_id), hos::Version_20_0_0) \ + AMS_SF_METHOD_INFO(C, H, 20, Result, RegisterRunningApplet, (ncm::ProgramId program_id), (program_id), hos::Version_12_0_0) \ + AMS_SF_METHOD_INFO(C, H, 21, Result, UnregisterRunningApplet, (ncm::ProgramId program_id), (program_id), hos::Version_12_0_0) \ + AMS_SF_METHOD_INFO(C, H, 22, Result, UpdateAppletSuspendedDuration, (ncm::ProgramId program_id, TimeSpanType duration), (program_id, duration), hos::Version_12_0_0) \ + AMS_SF_METHOD_INFO(C, H, 30, Result, InvalidateForcedShutdownDetection, (), (), hos::Version_12_0_0) \ + AMS_SF_METHOD_INFO(C, H, 40, Result, WaitForReportCreation, (), (), hos::Version_21_0_0) AMS_SF_DEFINE_INTERFACE(ams::erpt::sf, IContext, AMS_ERPT_I_CONTEXT_INTERFACE_INFO, 0xDD41DD03) diff --git a/libraries/libstratosphere/include/stratosphere/erpt/srv/erpt_srv_types.hpp b/libraries/libstratosphere/include/stratosphere/erpt/srv/erpt_srv_types.hpp index 348f3cb5f..4cdd3a677 100644 --- a/libraries/libstratosphere/include/stratosphere/erpt/srv/erpt_srv_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/erpt/srv/erpt_srv_types.hpp @@ -124,6 +124,10 @@ namespace ams::erpt::srv { } } + constexpr inline bool IsValidCategory(CategoryId id) { + return FindCategoryIndex(id).has_value(); + } + constexpr inline CategoryId ConvertFieldToCategory(FieldId id) { const auto index = FindFieldIndex(id); AMS_ASSERT(index.has_value()); diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_impl.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_impl.cpp index fcfb8af80..125501821 100644 --- a/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_impl.cpp +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_impl.cpp @@ -23,6 +23,59 @@ namespace ams::erpt::srv { + namespace { + + ContextEntry MakeContextEntry(const CategoryEntry &cat_entry, Span field_entries) { + /* Check pre-conditions. */ + AMS_ASSERT(cat_entry.field_count <= field_entries.size()); + + /* Make the entry. */ + ContextEntry entry = {}; + + entry.version = 0; + entry.category = cat_entry.category; + entry.field_count = cat_entry.field_count; + + for (size_t i = 0; i < cat_entry.field_count; ++i) { + entry.fields[i] = field_entries[i]; + } + + return entry; + } + + Result SubmitMultipleContextImpl(Span category_entries, Span field_entries, Span array_buf) { + /* Iterate over all category entries. */ + size_t field_entry_offset = 0; + size_t array_buf_offset = 0; + for (const auto &category_entry : category_entries) { + /* Check that the category is valid. */ + R_UNLESS(erpt::srv::IsValidCategory(category_entry.category), erpt::ResultInvalidArgument()); + + /* Check that there aren't too many fields for the category. */ + R_UNLESS(category_entry.field_count <= FieldsPerContext, erpt::ResultInvalidArgument()); + + /* Check that there isn't too much data in the array buf. */ + R_UNLESS(category_entry.array_buffer_count <= ArrayBufferSizeMax, erpt::ResultInvalidArgument()); + + /* Check that the fields/data fit into the provided buffer. */ + R_UNLESS(category_entry.field_count + field_entry_offset <= field_entries.size(), erpt::ResultInvalidArgument()); + R_UNLESS(category_entry.array_buffer_count + array_buf_offset <= array_buf.size_bytes(), erpt::ResultInvalidArgument()); + + /* Make the entry. */ + const auto ctx_entry = MakeContextEntry(category_entry, field_entries.subspan(field_entry_offset, category_entry.field_count)); + R_TRY(Context::SubmitContext(std::addressof(ctx_entry), array_buf.data() + array_buf_offset, category_entry.array_buffer_count)); + + /* Advance. */ + field_entry_offset += category_entry.field_count; + array_buf_offset += category_entry.array_buffer_count; + } + + /* We succeeded. */ + R_SUCCEED(); + } + + } + Result ContextImpl::SubmitContext(const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer) { const ContextEntry *ctx = reinterpret_cast( ctx_buffer.GetPointer()); const u8 *data = reinterpret_cast(data_buffer.GetPointer()); @@ -85,6 +138,28 @@ namespace ams::erpt::srv { R_SUCCEED(); } + Result ContextImpl::CreateReportWithAdditionalContext(ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer, const ams::sf::InBuffer &meta_buffer, Result result, erpt::CreateReportOptionFlagSet flags, const ams::sf::InMapAliasArray &category_entries, const ams::sf::InMapAliasArray &field_entries, const ams::sf::InBuffer &array_buffer) { + /* Submit the additional context. */ + R_TRY(SubmitMultipleContextImpl(category_entries.ToSpan(), field_entries.ToSpan(), MakeSpan(array_buffer.GetPointer(), array_buffer.GetSize()))); + + /* Clear the additional context when we're done. */ + ON_SCOPE_EXIT { + const auto category_span = category_entries.ToSpan(); + + for (const auto &entry : category_span) { + if (erpt::srv::IsValidCategory(entry.category)) { + static_cast(Context::ClearContext(entry.category)); + } + } + }; + + /* Create the report. */ + R_TRY(this->CreateReport(report_type, ctx_buffer, data_buffer, meta_buffer, result, flags)); + + /* We succeeded. */ + R_SUCCEED(); + } + Result ContextImpl::SubmitMultipleCategoryContext(const MultipleCategoryContextEntry &ctx_entry, const ams::sf::InBuffer &str_buffer) { R_UNLESS(ctx_entry.category_count <= CategoriesPerMultipleCategoryContext, erpt::ResultInvalidArgument()); @@ -114,6 +189,10 @@ namespace ams::erpt::srv { R_SUCCEED(); } + Result ContextImpl::SubmitMultipleContext(const ams::sf::InMapAliasArray &category_entries, const ams::sf::InMapAliasArray &field_entries, const ams::sf::InBuffer &array_buffer) { + R_RETURN(SubmitMultipleContextImpl(category_entries.ToSpan(), field_entries.ToSpan(), MakeSpan(array_buffer.GetPointer(), array_buffer.GetSize()))); + } + Result ContextImpl::UpdateApplicationLaunchTime() { Reporter::UpdateApplicationLaunchTime(); R_SUCCEED(); @@ -124,6 +203,16 @@ namespace ams::erpt::srv { R_SUCCEED(); } + Result ContextImpl::RegisterRunningApplicationInfo(ncm::ApplicationId app_id, ncm::ProgramId program_id) { + Reporter::RegisterRunningApplicationInfo(app_id, program_id); + R_SUCCEED(); + } + + Result ContextImpl::UnregisterRunningApplicationInfo() { + Reporter::UnregisterRunningApplicationInfo(); + R_SUCCEED(); + } + Result ContextImpl::SubmitAttachment(ams::sf::Out out, const ams::sf::InBuffer &attachment_name, const ams::sf::InBuffer &attachment_data) { const char *name = reinterpret_cast(attachment_name.GetPointer()); const u8 *data = reinterpret_cast(attachment_data.GetPointer()); @@ -215,4 +304,9 @@ namespace ams::erpt::srv { R_SUCCEED(); } + Result ContextImpl::WaitForReportCreation() { + /* This function currently does nothing. Maybe it only waits on Ounce? */ + R_SUCCEED(); + } + } diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_impl.hpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_impl.hpp index cada2ceb2..506a5c244 100644 --- a/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_impl.hpp +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_context_impl.hpp @@ -26,9 +26,13 @@ namespace ams::erpt::srv { Result ClearInitialLaunchSettingsCompletionTime(); Result UpdatePowerOnTime(); Result UpdateAwakeTime(); + Result CreateReportWithAdditionalContext(ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer, const ams::sf::InBuffer &meta_buffer, Result result, erpt::CreateReportOptionFlagSet flags, const ams::sf::InMapAliasArray &category_entries, const ams::sf::InMapAliasArray &field_entries, const ams::sf::InBuffer &array_buffer); Result SubmitMultipleCategoryContext(const MultipleCategoryContextEntry &ctx_entry, const ams::sf::InBuffer &str_buffer); + Result SubmitMultipleContext(const ams::sf::InMapAliasArray &category_entries, const ams::sf::InMapAliasArray &field_entries, const ams::sf::InBuffer &array_buffer); Result UpdateApplicationLaunchTime(); + Result RegisterRunningApplicationInfo(ncm::ApplicationId app_id, ncm::ProgramId program_id); Result ClearApplicationLaunchTime(); + Result UnregisterRunningApplicationInfo(); Result SubmitAttachment(ams::sf::Out out, const ams::sf::InBuffer &attachment_name, const ams::sf::InBuffer &attachment_data); Result CreateReportWithAttachmentsDeprecated(ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer, const ams::sf::InBuffer &attachment_ids_buffer); Result CreateReportWithAttachmentsDeprecated2(ReportType report_type, const ams::sf::InBuffer &ctx_buffer, const ams::sf::InBuffer &data_buffer, const ams::sf::InBuffer &attachment_ids_buffer, Result result); @@ -41,6 +45,7 @@ namespace ams::erpt::srv { Result UnregisterRunningApplet(ncm::ProgramId program_id); Result UpdateAppletSuspendedDuration(ncm::ProgramId program_id, TimeSpanType duration); Result InvalidateForcedShutdownDetection(); + Result WaitForReportCreation(); }; static_assert(erpt::sf::IsIContext); diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.cpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.cpp index f1c601698..995ffe30b 100644 --- a/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.cpp +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.cpp @@ -44,8 +44,10 @@ namespace ams::erpt::srv { static constexpr AppletActiveTimeInfo InvalidAppletActiveTimeInfo = { ncm::InvalidProgramId, os::Tick{}, TimeSpan::FromNanoSeconds(0) }; private: std::array m_list; + ncm::ApplicationId m_running_app_id; + ncm::ProgramId m_running_app_program_id; public: - constexpr AppletActiveTimeInfoList() : m_list{InvalidAppletActiveTimeInfo, InvalidAppletActiveTimeInfo, InvalidAppletActiveTimeInfo, InvalidAppletActiveTimeInfo, InvalidAppletActiveTimeInfo, InvalidAppletActiveTimeInfo, InvalidAppletActiveTimeInfo, InvalidAppletActiveTimeInfo} { + constexpr AppletActiveTimeInfoList() : m_list{InvalidAppletActiveTimeInfo, InvalidAppletActiveTimeInfo, InvalidAppletActiveTimeInfo, InvalidAppletActiveTimeInfo, InvalidAppletActiveTimeInfo, InvalidAppletActiveTimeInfo, InvalidAppletActiveTimeInfo, InvalidAppletActiveTimeInfo}, m_running_app_id{ncm::InvalidApplicationId}, m_running_app_program_id{ncm::InvalidProgramId} { m_list.fill(InvalidAppletActiveTimeInfo); } public: @@ -67,6 +69,32 @@ namespace ams::erpt::srv { *entry = InvalidAppletActiveTimeInfo; } + void RegisterApplicationInfo(ncm::ApplicationId app_id, ncm::ProgramId program_id) { + /* Set the running application info. */ + m_running_app_id = app_id; + m_running_app_program_id = program_id; + } + + void UnregisterApplicationInfo() { + m_running_app_id = ncm::InvalidApplicationId; + m_running_app_program_id = ncm::InvalidProgramId; + } + + util::optional GetApplicationStartTick() { + /* If we have a running application, try to find a matching entry. */ + if (m_running_app_id != ncm::InvalidApplicationId) { + /* NOTE: This seems to be a Nintendo bug? They are comparing the running app id to the info's program id, */ + /* instead of the running app program id. Granted, these should usually be the same, but I think this code */ + /* is literally incorrect. */ + const auto entry = util::range::find_if(m_list, [&](const AppletActiveTimeInfo &info) { return info.program_id == m_running_app_id; }); + if (entry != m_list.end()) { + return entry->register_tick; + } + } + + return util::nullopt; + } + void UpdateSuspendedDuration(ncm::ProgramId program_id, TimeSpan suspended_duration) { /* Find a matching entry. */ auto entry = util::range::find_if(m_list, [&](const AppletActiveTimeInfo &info) { return info.program_id == program_id; }); @@ -368,6 +396,14 @@ namespace ams::erpt::srv { R_SUCCEED(); } + void Reporter::RegisterRunningApplicationInfo(ncm::ApplicationId app_id, ncm::ProgramId program_id) { + g_applet_active_time_info_list.RegisterApplicationInfo(app_id, program_id); + } + + void Reporter::UnregisterRunningApplicationInfo() { + g_applet_active_time_info_list.UnregisterApplicationInfo(); + } + Result Reporter::CreateReport(ReportType type, Result ctx_result, const ContextEntry *ctx, const u8 *data, u32 data_size, const ReportMetaData *meta, const AttachmentId *attachments, u32 num_attachments, erpt::CreateReportOptionFlagSet flags, const ReportId *specified_report_id) { /* Create a context record for the report. */ auto record = std::make_unique(); @@ -464,7 +500,11 @@ namespace ams::erpt::srv { } } - if (s_application_launch_time) { + if (hos::GetVersion() >= hos::Version_21_0_0) { + if (auto start_tick = g_applet_active_time_info_list.GetApplicationStartTick(); start_tick.has_value()) { + static_cast(auto_record->Add(FieldId_ApplicationAliveTime, (occurrence_tick - *start_tick).ToTimeSpan().GetSeconds())); + } + } else if (s_application_launch_time) { static_cast(auto_record->Add(FieldId_ApplicationAliveTime, (occurrence_tick - *s_application_launch_time).ToTimeSpan().GetSeconds())); } diff --git a/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.hpp b/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.hpp index f5db475ec..48660542f 100644 --- a/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.hpp +++ b/libraries/libstratosphere/source/erpt/srv/erpt_srv_reporter.hpp @@ -54,6 +54,9 @@ namespace ams::erpt::srv { static Result UnregisterRunningApplet(ncm::ProgramId program_id); static Result UpdateAppletSuspendedDuration(ncm::ProgramId program_id, TimeSpan duration); + static void RegisterRunningApplicationInfo(ncm::ApplicationId app_id, ncm::ProgramId program_id); + static void UnregisterRunningApplicationInfo(); + static void SetRedirectNewReportsToSdCard(bool en) { s_redirect_new_reports = en; } private: static Result SubmitReportContexts(const ReportId &report_id, ReportType type, Result ctx_result, std::unique_ptr record, const time::PosixTime &user_timestamp, const time::PosixTime &network_timestamp, erpt::CreateReportOptionFlagSet flags);