From e49ae3bf92fdc3f925ceaff03c4f2b9e90f94448 Mon Sep 17 00:00:00 2001
From: David Marcec <dmarcecguzman@gmail.com>
Date: Mon, 24 Jun 2019 12:26:45 +1000
Subject: [PATCH] Implemented INotificationService

---
 src/core/CMakeLists.txt                   |   1 +
 src/core/hle/service/friend/errors.h      |  12 +++
 src/core/hle/service/friend/friend.cpp    | 112 ++++++++++++++++++++++
 src/core/hle/service/friend/friend.h      |   1 +
 src/core/hle/service/friend/interface.cpp |   2 +-
 5 files changed, 127 insertions(+), 1 deletion(-)
 create mode 100644 src/core/hle/service/friend/errors.h

diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index cdb3bf6abe..2ccd4a7796 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -272,6 +272,7 @@ add_library(core STATIC
     hle/service/filesystem/fsp_srv.h
     hle/service/fgm/fgm.cpp
     hle/service/fgm/fgm.h
+    hle/service/friend/errors.h
     hle/service/friend/friend.cpp
     hle/service/friend/friend.h
     hle/service/friend/interface.cpp
diff --git a/src/core/hle/service/friend/errors.h b/src/core/hle/service/friend/errors.h
new file mode 100644
index 0000000000..72d96b5558
--- /dev/null
+++ b/src/core/hle/service/friend/errors.h
@@ -0,0 +1,12 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/result.h"
+
+namespace Service::Friend {
+
+constexpr ResultCode ERR_NO_NOTIFICATIONS{ErrorModule::Account, 15};
+}
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index 5100e376c6..9752f1a8dd 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -2,8 +2,13 @@
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
+#include <queue>
 #include "common/logging/log.h"
+#include "common/uuid.h"
 #include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/writable_event.h"
+#include "core/hle/service/friend/errors.h"
 #include "core/hle/service/friend/friend.h"
 #include "core/hle/service/friend/interface.h"
 
@@ -109,6 +114,103 @@ private:
     }
 };
 
+class INotificationService final : public ServiceFramework<INotificationService> {
+public:
+    INotificationService(Common::UUID uuid) : ServiceFramework("INotificationService"), uuid(uuid) {
+        // clang-format off
+        static const FunctionInfo functions[] = {
+            {0, &INotificationService::GetEvent, "GetEvent"},
+            {1, &INotificationService::Clear, "Clear"},
+            {2, &INotificationService::Pop, "Pop"}
+        };
+        // clang-format on
+
+        RegisterHandlers(functions);
+    }
+
+private:
+    void GetEvent(Kernel::HLERequestContext& ctx) {
+        LOG_DEBUG(Service_ACC, "called");
+
+        IPC::ResponseBuilder rb{ctx, 2, 1};
+        rb.Push(RESULT_SUCCESS);
+
+        if (is_event_created) {
+            rb.PushCopyObjects(notification_event.readable);
+        } else {
+            auto& kernel = Core::System::GetInstance().Kernel();
+            notification_event = Kernel::WritableEvent::CreateEventPair(
+                kernel, Kernel::ResetType::Manual, "INotificationService:NotifyEvent");
+            is_event_created = true;
+            rb.PushCopyObjects(notification_event.readable);
+        }
+    }
+
+    void Clear(Kernel::HLERequestContext& ctx) {
+        LOG_DEBUG(Service_ACC, "called");
+        while (!notifications.empty()) {
+            notifications.pop();
+        }
+        states.has_recieved_friend_request = false;
+        states.has_updated_friends = false;
+
+        IPC::ResponseBuilder rb{ctx, 2};
+        rb.Push(RESULT_SUCCESS);
+    }
+
+    void Pop(Kernel::HLERequestContext& ctx) {
+        LOG_DEBUG(Service_ACC, "called");
+        IPC::ResponseBuilder rb{ctx, 2};
+
+        if (notifications.empty()) {
+            LOG_ERROR(Service_ACC, "No notifications in queue!");
+            rb.Push(ERR_NO_NOTIFICATIONS);
+            return;
+        }
+
+        auto notification = notifications.front();
+        notifications.pop();
+
+        switch (notification.notification_type) {
+        case NotificationTypes::HasUpdatedFriendsList:
+            states.has_updated_friends = false;
+            break;
+        case NotificationTypes::HasRecievedFriendRequest:
+            states.has_recieved_friend_request = false;
+            break;
+        default:
+            // HOS seems not have an error case for an unknown notification
+            LOG_WARNING(Service_ACC, "Unknown notification {:08X}",
+                        static_cast<u32>(notification.notification_type));
+            break;
+        }
+        rb.Push(RESULT_SUCCESS);
+    }
+
+    enum class NotificationTypes : u32_le {
+        HasUpdatedFriendsList = 0x65,
+        HasRecievedFriendRequest = 0x1
+    };
+
+    struct SizedNotificationInfo {
+        NotificationTypes notification_type;
+        INSERT_PADDING_WORDS(
+            1); // TODO(ogniK): This doesn't seem to be used within any IPC returns as of now
+        Common::UUID user_uuid;
+    };
+
+    struct States {
+        bool has_updated_friends;
+        bool has_recieved_friend_request;
+    };
+
+    Common::UUID uuid{};
+    bool is_event_created = false;
+    Kernel::EventPair notification_event;
+    std::queue<SizedNotificationInfo> notifications{};
+    States states{};
+};
+
 void Module::Interface::CreateFriendService(Kernel::HLERequestContext& ctx) {
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     rb.Push(RESULT_SUCCESS);
@@ -116,6 +218,16 @@ void Module::Interface::CreateFriendService(Kernel::HLERequestContext& ctx) {
     LOG_DEBUG(Service_ACC, "called");
 }
 
+void Module::Interface::CreateNotificationService(Kernel::HLERequestContext& ctx) {
+    IPC::RequestParser rp{ctx};
+    auto uuid = rp.PopRaw<Common::UUID>();
+
+    IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+    rb.Push(RESULT_SUCCESS);
+    rb.PushIpcInterface<INotificationService>(uuid);
+    LOG_DEBUG(Service_ACC, "called");
+}
+
 Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
     : ServiceFramework(name), module(std::move(module)) {}
 
diff --git a/src/core/hle/service/friend/friend.h b/src/core/hle/service/friend/friend.h
index e762840cb6..38d05fa8e1 100644
--- a/src/core/hle/service/friend/friend.h
+++ b/src/core/hle/service/friend/friend.h
@@ -16,6 +16,7 @@ public:
         ~Interface() override;
 
         void CreateFriendService(Kernel::HLERequestContext& ctx);
+        void CreateNotificationService(Kernel::HLERequestContext& ctx);
 
     protected:
         std::shared_ptr<Module> module;
diff --git a/src/core/hle/service/friend/interface.cpp b/src/core/hle/service/friend/interface.cpp
index 5a6840af5b..5b384f7333 100644
--- a/src/core/hle/service/friend/interface.cpp
+++ b/src/core/hle/service/friend/interface.cpp
@@ -10,7 +10,7 @@ Friend::Friend(std::shared_ptr<Module> module, const char* name)
     : Interface(std::move(module), name) {
     static const FunctionInfo functions[] = {
         {0, &Friend::CreateFriendService, "CreateFriendService"},
-        {1, nullptr, "CreateNotificationService"},
+        {1, &Friend::CreateNotificationService, "CreateNotificationService"},
         {2, nullptr, "CreateDaemonSuspendSessionService"},
     };
     RegisterHandlers(functions);
-- 
GitLab