service: am: Implement shared buffer
Co-authored-by: Liam <byteslice@airmail.cc>
This commit is contained in:
parent
35f25882e0
commit
2687a83f6a
@ -35,11 +35,13 @@
|
|||||||
#include "core/hle/service/filesystem/filesystem.h"
|
#include "core/hle/service/filesystem/filesystem.h"
|
||||||
#include "core/hle/service/ipc_helpers.h"
|
#include "core/hle/service/ipc_helpers.h"
|
||||||
#include "core/hle/service/ns/ns.h"
|
#include "core/hle/service/ns/ns.h"
|
||||||
|
#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
|
||||||
#include "core/hle/service/nvnflinger/nvnflinger.h"
|
#include "core/hle/service/nvnflinger/nvnflinger.h"
|
||||||
#include "core/hle/service/pm/pm.h"
|
#include "core/hle/service/pm/pm.h"
|
||||||
#include "core/hle/service/server_manager.h"
|
#include "core/hle/service/server_manager.h"
|
||||||
#include "core/hle/service/sm/sm.h"
|
#include "core/hle/service/sm/sm.h"
|
||||||
#include "core/hle/service/vi/vi.h"
|
#include "core/hle/service/vi/vi.h"
|
||||||
|
#include "core/hle/service/vi/vi_results.h"
|
||||||
#include "core/memory.h"
|
#include "core/memory.h"
|
||||||
|
|
||||||
namespace Service::AM {
|
namespace Service::AM {
|
||||||
@ -192,7 +194,7 @@ IDisplayController::IDisplayController(Core::System& system_)
|
|||||||
{4, nullptr, "UpdateCallerAppletCaptureImage"},
|
{4, nullptr, "UpdateCallerAppletCaptureImage"},
|
||||||
{5, nullptr, "GetLastForegroundCaptureImageEx"},
|
{5, nullptr, "GetLastForegroundCaptureImageEx"},
|
||||||
{6, nullptr, "GetLastApplicationCaptureImageEx"},
|
{6, nullptr, "GetLastApplicationCaptureImageEx"},
|
||||||
{7, nullptr, "GetCallerAppletCaptureImageEx"},
|
{7, &IDisplayController::GetCallerAppletCaptureImageEx, "GetCallerAppletCaptureImageEx"},
|
||||||
{8, &IDisplayController::TakeScreenShotOfOwnLayer, "TakeScreenShotOfOwnLayer"},
|
{8, &IDisplayController::TakeScreenShotOfOwnLayer, "TakeScreenShotOfOwnLayer"},
|
||||||
{9, nullptr, "CopyBetweenCaptureBuffers"},
|
{9, nullptr, "CopyBetweenCaptureBuffers"},
|
||||||
{10, nullptr, "AcquireLastApplicationCaptureBuffer"},
|
{10, nullptr, "AcquireLastApplicationCaptureBuffer"},
|
||||||
@ -210,8 +212,8 @@ IDisplayController::IDisplayController(Core::System& system_)
|
|||||||
{23, nullptr, "ReleaseLastApplicationCaptureSharedBuffer"},
|
{23, nullptr, "ReleaseLastApplicationCaptureSharedBuffer"},
|
||||||
{24, nullptr, "AcquireLastForegroundCaptureSharedBuffer"},
|
{24, nullptr, "AcquireLastForegroundCaptureSharedBuffer"},
|
||||||
{25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"},
|
{25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"},
|
||||||
{26, nullptr, "AcquireCallerAppletCaptureSharedBuffer"},
|
{26, &IDisplayController::AcquireCallerAppletCaptureSharedBuffer, "AcquireCallerAppletCaptureSharedBuffer"},
|
||||||
{27, nullptr, "ReleaseCallerAppletCaptureSharedBuffer"},
|
{27, &IDisplayController::ReleaseCallerAppletCaptureSharedBuffer, "ReleaseCallerAppletCaptureSharedBuffer"},
|
||||||
{28, nullptr, "TakeScreenShotOfOwnLayerEx"},
|
{28, nullptr, "TakeScreenShotOfOwnLayerEx"},
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
@ -221,6 +223,15 @@ IDisplayController::IDisplayController(Core::System& system_)
|
|||||||
|
|
||||||
IDisplayController::~IDisplayController() = default;
|
IDisplayController::~IDisplayController() = default;
|
||||||
|
|
||||||
|
void IDisplayController::GetCallerAppletCaptureImageEx(HLERequestContext& ctx) {
|
||||||
|
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.Push(1u);
|
||||||
|
rb.Push(0);
|
||||||
|
}
|
||||||
|
|
||||||
void IDisplayController::TakeScreenShotOfOwnLayer(HLERequestContext& ctx) {
|
void IDisplayController::TakeScreenShotOfOwnLayer(HLERequestContext& ctx) {
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||||
|
|
||||||
@ -228,6 +239,22 @@ void IDisplayController::TakeScreenShotOfOwnLayer(HLERequestContext& ctx) {
|
|||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IDisplayController::AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx) {
|
||||||
|
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.Push(1U);
|
||||||
|
rb.Push(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IDisplayController::ReleaseCallerAppletCaptureSharedBuffer(HLERequestContext& ctx) {
|
||||||
|
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
IDebugFunctions::IDebugFunctions(Core::System& system_)
|
IDebugFunctions::IDebugFunctions(Core::System& system_)
|
||||||
: ServiceFramework{system_, "IDebugFunctions"} {
|
: ServiceFramework{system_, "IDebugFunctions"} {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
@ -287,14 +314,14 @@ ISelfController::ISelfController(Core::System& system_, Nvnflinger::Nvnflinger&
|
|||||||
{20, nullptr, "SetDesirableKeyboardLayout"},
|
{20, nullptr, "SetDesirableKeyboardLayout"},
|
||||||
{21, nullptr, "GetScreenShotProgramId"},
|
{21, nullptr, "GetScreenShotProgramId"},
|
||||||
{40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"},
|
{40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"},
|
||||||
{41, nullptr, "IsSystemBufferSharingEnabled"},
|
{41, &ISelfController::IsSystemBufferSharingEnabled, "IsSystemBufferSharingEnabled"},
|
||||||
{42, nullptr, "GetSystemSharedLayerHandle"},
|
{42, &ISelfController::GetSystemSharedLayerHandle, "GetSystemSharedLayerHandle"},
|
||||||
{43, nullptr, "GetSystemSharedBufferHandle"},
|
{43, &ISelfController::GetSystemSharedBufferHandle, "GetSystemSharedBufferHandle"},
|
||||||
{44, &ISelfController::CreateManagedDisplaySeparableLayer, "CreateManagedDisplaySeparableLayer"},
|
{44, &ISelfController::CreateManagedDisplaySeparableLayer, "CreateManagedDisplaySeparableLayer"},
|
||||||
{45, nullptr, "SetManagedDisplayLayerSeparationMode"},
|
{45, nullptr, "SetManagedDisplayLayerSeparationMode"},
|
||||||
{46, nullptr, "SetRecordingLayerCompositionEnabled"},
|
{46, nullptr, "SetRecordingLayerCompositionEnabled"},
|
||||||
{50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"},
|
{50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"},
|
||||||
{51, nullptr, "ApproveToDisplay"},
|
{51, &ISelfController::ApproveToDisplay, "ApproveToDisplay"},
|
||||||
{60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"},
|
{60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"},
|
||||||
{61, nullptr, "SetMediaPlaybackState"},
|
{61, nullptr, "SetMediaPlaybackState"},
|
||||||
{62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"},
|
{62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"},
|
||||||
@ -493,6 +520,50 @@ void ISelfController::CreateManagedDisplayLayer(HLERequestContext& ctx) {
|
|||||||
rb.Push(*layer_id);
|
rb.Push(*layer_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ISelfController::IsSystemBufferSharingEnabled(HLERequestContext& ctx) {
|
||||||
|
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(this->EnsureBufferSharingEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ISelfController::GetSystemSharedLayerHandle(HLERequestContext& ctx) {
|
||||||
|
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 6};
|
||||||
|
rb.Push(this->EnsureBufferSharingEnabled());
|
||||||
|
rb.Push<s64>(system_shared_buffer_id);
|
||||||
|
rb.Push<s64>(system_shared_layer_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ISelfController::GetSystemSharedBufferHandle(HLERequestContext& ctx) {
|
||||||
|
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
|
rb.Push(this->EnsureBufferSharingEnabled());
|
||||||
|
rb.Push<s64>(system_shared_buffer_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result ISelfController::EnsureBufferSharingEnabled() {
|
||||||
|
if (buffer_sharing_enabled) {
|
||||||
|
return ResultSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (system.GetAppletManager().GetCurrentAppletId() <= Applets::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;
|
||||||
|
}
|
||||||
|
|
||||||
void ISelfController::CreateManagedDisplaySeparableLayer(HLERequestContext& ctx) {
|
void ISelfController::CreateManagedDisplaySeparableLayer(HLERequestContext& ctx) {
|
||||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||||
|
|
||||||
@ -518,6 +589,13 @@ void ISelfController::SetHandlesRequestToDisplay(HLERequestContext& ctx) {
|
|||||||
rb.Push(ResultSuccess);
|
rb.Push(ResultSuccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ISelfController::ApproveToDisplay(HLERequestContext& ctx) {
|
||||||
|
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) {
|
void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp{ctx};
|
IPC::RequestParser rp{ctx};
|
||||||
idle_time_detection_extension = rp.Pop<u32>();
|
idle_time_detection_extension = rp.Pop<u32>();
|
||||||
@ -688,7 +766,8 @@ void AppletMessageQueue::OperationModeChanged() {
|
|||||||
|
|
||||||
ICommonStateGetter::ICommonStateGetter(Core::System& system_,
|
ICommonStateGetter::ICommonStateGetter(Core::System& system_,
|
||||||
std::shared_ptr<AppletMessageQueue> msg_queue_)
|
std::shared_ptr<AppletMessageQueue> msg_queue_)
|
||||||
: ServiceFramework{system_, "ICommonStateGetter"}, msg_queue{std::move(msg_queue_)} {
|
: ServiceFramework{system_, "ICommonStateGetter"}, msg_queue{std::move(msg_queue_)},
|
||||||
|
service_context{system_, "ICommonStateGetter"} {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"},
|
{0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"},
|
||||||
@ -701,10 +780,10 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_,
|
|||||||
{7, nullptr, "GetCradleStatus"},
|
{7, nullptr, "GetCradleStatus"},
|
||||||
{8, &ICommonStateGetter::GetBootMode, "GetBootMode"},
|
{8, &ICommonStateGetter::GetBootMode, "GetBootMode"},
|
||||||
{9, &ICommonStateGetter::GetCurrentFocusState, "GetCurrentFocusState"},
|
{9, &ICommonStateGetter::GetCurrentFocusState, "GetCurrentFocusState"},
|
||||||
{10, nullptr, "RequestToAcquireSleepLock"},
|
{10, &ICommonStateGetter::RequestToAcquireSleepLock, "RequestToAcquireSleepLock"},
|
||||||
{11, nullptr, "ReleaseSleepLock"},
|
{11, nullptr, "ReleaseSleepLock"},
|
||||||
{12, nullptr, "ReleaseSleepLockTransiently"},
|
{12, nullptr, "ReleaseSleepLockTransiently"},
|
||||||
{13, nullptr, "GetAcquiredSleepLockEvent"},
|
{13, &ICommonStateGetter::GetAcquiredSleepLockEvent, "GetAcquiredSleepLockEvent"},
|
||||||
{14, nullptr, "GetWakeupCount"},
|
{14, nullptr, "GetWakeupCount"},
|
||||||
{20, nullptr, "PushToGeneralChannel"},
|
{20, nullptr, "PushToGeneralChannel"},
|
||||||
{30, nullptr, "GetHomeButtonReaderLockAccessor"},
|
{30, nullptr, "GetHomeButtonReaderLockAccessor"},
|
||||||
@ -747,6 +826,8 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_,
|
|||||||
|
|
||||||
RegisterHandlers(functions);
|
RegisterHandlers(functions);
|
||||||
|
|
||||||
|
sleep_lock_event = service_context.CreateEvent("ICommonStateGetter::SleepLockEvent");
|
||||||
|
|
||||||
// Configure applets to be in foreground state
|
// Configure applets to be in foreground state
|
||||||
msg_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
|
msg_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
|
||||||
msg_queue->PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
|
msg_queue->PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
|
||||||
@ -795,6 +876,24 @@ void ICommonStateGetter::GetCurrentFocusState(HLERequestContext& ctx) {
|
|||||||
rb.Push(static_cast<u8>(FocusState::InFocus));
|
rb.Push(static_cast<u8>(FocusState::InFocus));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ICommonStateGetter::RequestToAcquireSleepLock(HLERequestContext& ctx) {
|
||||||
|
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||||
|
|
||||||
|
// Sleep lock is acquired immediately.
|
||||||
|
sleep_lock_event->Signal();
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICommonStateGetter::GetAcquiredSleepLockEvent(HLERequestContext& ctx) {
|
||||||
|
LOG_WARNING(Service_AM, "called");
|
||||||
|
|
||||||
|
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||||
|
rb.Push(ResultSuccess);
|
||||||
|
rb.PushCopyObjects(sleep_lock_event->GetReadableEvent());
|
||||||
|
}
|
||||||
|
|
||||||
void ICommonStateGetter::IsVrModeEnabled(HLERequestContext& ctx) {
|
void ICommonStateGetter::IsVrModeEnabled(HLERequestContext& ctx) {
|
||||||
LOG_DEBUG(Service_AM, "called");
|
LOG_DEBUG(Service_AM, "called");
|
||||||
|
|
||||||
|
@ -122,7 +122,10 @@ public:
|
|||||||
~IDisplayController() override;
|
~IDisplayController() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void GetCallerAppletCaptureImageEx(HLERequestContext& ctx);
|
||||||
void TakeScreenShotOfOwnLayer(HLERequestContext& ctx);
|
void TakeScreenShotOfOwnLayer(HLERequestContext& ctx);
|
||||||
|
void AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx);
|
||||||
|
void ReleaseCallerAppletCaptureSharedBuffer(HLERequestContext& ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
class IDebugFunctions final : public ServiceFramework<IDebugFunctions> {
|
class IDebugFunctions final : public ServiceFramework<IDebugFunctions> {
|
||||||
@ -150,9 +153,13 @@ private:
|
|||||||
void SetRestartMessageEnabled(HLERequestContext& ctx);
|
void SetRestartMessageEnabled(HLERequestContext& ctx);
|
||||||
void SetOutOfFocusSuspendingEnabled(HLERequestContext& ctx);
|
void SetOutOfFocusSuspendingEnabled(HLERequestContext& ctx);
|
||||||
void SetAlbumImageOrientation(HLERequestContext& ctx);
|
void SetAlbumImageOrientation(HLERequestContext& ctx);
|
||||||
|
void IsSystemBufferSharingEnabled(HLERequestContext& ctx);
|
||||||
|
void GetSystemSharedBufferHandle(HLERequestContext& ctx);
|
||||||
|
void GetSystemSharedLayerHandle(HLERequestContext& ctx);
|
||||||
void CreateManagedDisplayLayer(HLERequestContext& ctx);
|
void CreateManagedDisplayLayer(HLERequestContext& ctx);
|
||||||
void CreateManagedDisplaySeparableLayer(HLERequestContext& ctx);
|
void CreateManagedDisplaySeparableLayer(HLERequestContext& ctx);
|
||||||
void SetHandlesRequestToDisplay(HLERequestContext& ctx);
|
void SetHandlesRequestToDisplay(HLERequestContext& ctx);
|
||||||
|
void ApproveToDisplay(HLERequestContext& ctx);
|
||||||
void SetIdleTimeDetectionExtension(HLERequestContext& ctx);
|
void SetIdleTimeDetectionExtension(HLERequestContext& ctx);
|
||||||
void GetIdleTimeDetectionExtension(HLERequestContext& ctx);
|
void GetIdleTimeDetectionExtension(HLERequestContext& ctx);
|
||||||
void ReportUserIsActive(HLERequestContext& ctx);
|
void ReportUserIsActive(HLERequestContext& ctx);
|
||||||
@ -164,6 +171,8 @@ private:
|
|||||||
void SaveCurrentScreenshot(HLERequestContext& ctx);
|
void SaveCurrentScreenshot(HLERequestContext& ctx);
|
||||||
void SetRecordVolumeMuted(HLERequestContext& ctx);
|
void SetRecordVolumeMuted(HLERequestContext& ctx);
|
||||||
|
|
||||||
|
Result EnsureBufferSharingEnabled();
|
||||||
|
|
||||||
enum class ScreenshotPermission : u32 {
|
enum class ScreenshotPermission : u32 {
|
||||||
Inherit = 0,
|
Inherit = 0,
|
||||||
Enable = 1,
|
Enable = 1,
|
||||||
@ -179,7 +188,10 @@ private:
|
|||||||
|
|
||||||
u32 idle_time_detection_extension = 0;
|
u32 idle_time_detection_extension = 0;
|
||||||
u64 num_fatal_sections_entered = 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 is_auto_sleep_disabled = false;
|
||||||
|
bool buffer_sharing_enabled = false;
|
||||||
ScreenshotPermission screenshot_permission = ScreenshotPermission::Inherit;
|
ScreenshotPermission screenshot_permission = ScreenshotPermission::Inherit;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -223,6 +235,8 @@ private:
|
|||||||
void GetEventHandle(HLERequestContext& ctx);
|
void GetEventHandle(HLERequestContext& ctx);
|
||||||
void ReceiveMessage(HLERequestContext& ctx);
|
void ReceiveMessage(HLERequestContext& ctx);
|
||||||
void GetCurrentFocusState(HLERequestContext& ctx);
|
void GetCurrentFocusState(HLERequestContext& ctx);
|
||||||
|
void RequestToAcquireSleepLock(HLERequestContext& ctx);
|
||||||
|
void GetAcquiredSleepLockEvent(HLERequestContext& ctx);
|
||||||
void GetDefaultDisplayResolutionChangeEvent(HLERequestContext& ctx);
|
void GetDefaultDisplayResolutionChangeEvent(HLERequestContext& ctx);
|
||||||
void GetOperationMode(HLERequestContext& ctx);
|
void GetOperationMode(HLERequestContext& ctx);
|
||||||
void GetPerformanceMode(HLERequestContext& ctx);
|
void GetPerformanceMode(HLERequestContext& ctx);
|
||||||
@ -240,6 +254,8 @@ private:
|
|||||||
|
|
||||||
std::shared_ptr<AppletMessageQueue> msg_queue;
|
std::shared_ptr<AppletMessageQueue> msg_queue;
|
||||||
bool vr_mode_state{};
|
bool vr_mode_state{};
|
||||||
|
Kernel::KEvent* sleep_lock_event;
|
||||||
|
KernelHelpers::ServiceContext service_context;
|
||||||
};
|
};
|
||||||
|
|
||||||
class IStorageImpl {
|
class IStorageImpl {
|
||||||
|
Loading…
Reference in New Issue
Block a user