pctl_module.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
  1. // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "common/logging/log.h"
  4. #include "core/core.h"
  5. #include "core/file_sys/control_metadata.h"
  6. #include "core/file_sys/patch_manager.h"
  7. #include "core/hle/service/ipc_helpers.h"
  8. #include "core/hle/service/kernel_helpers.h"
  9. #include "core/hle/service/pctl/pctl.h"
  10. #include "core/hle/service/pctl/pctl_module.h"
  11. #include "core/hle/service/server_manager.h"
  12. namespace Service::PCTL {
  13. namespace Error {
  14. constexpr Result ResultNoFreeCommunication{ErrorModule::PCTL, 101};
  15. constexpr Result ResultStereoVisionRestricted{ErrorModule::PCTL, 104};
  16. constexpr Result ResultNoCapability{ErrorModule::PCTL, 131};
  17. constexpr Result ResultNoRestrictionEnabled{ErrorModule::PCTL, 181};
  18. } // namespace Error
  19. class IParentalControlService final : public ServiceFramework<IParentalControlService> {
  20. public:
  21. explicit IParentalControlService(Core::System& system_, Capability capability_)
  22. : ServiceFramework{system_, "IParentalControlService"}, capability{capability_},
  23. service_context{system_, "IParentalControlService"} {
  24. // clang-format off
  25. static const FunctionInfo functions[] = {
  26. {1, &IParentalControlService::Initialize, "Initialize"},
  27. {1001, &IParentalControlService::CheckFreeCommunicationPermission, "CheckFreeCommunicationPermission"},
  28. {1002, nullptr, "ConfirmLaunchApplicationPermission"},
  29. {1003, nullptr, "ConfirmResumeApplicationPermission"},
  30. {1004, nullptr, "ConfirmSnsPostPermission"},
  31. {1005, nullptr, "ConfirmSystemSettingsPermission"},
  32. {1006, &IParentalControlService::IsRestrictionTemporaryUnlocked, "IsRestrictionTemporaryUnlocked"},
  33. {1007, nullptr, "RevertRestrictionTemporaryUnlocked"},
  34. {1008, nullptr, "EnterRestrictedSystemSettings"},
  35. {1009, nullptr, "LeaveRestrictedSystemSettings"},
  36. {1010, nullptr, "IsRestrictedSystemSettingsEntered"},
  37. {1011, nullptr, "RevertRestrictedSystemSettingsEntered"},
  38. {1012, nullptr, "GetRestrictedFeatures"},
  39. {1013, &IParentalControlService::ConfirmStereoVisionPermission, "ConfirmStereoVisionPermission"},
  40. {1014, nullptr, "ConfirmPlayableApplicationVideoOld"},
  41. {1015, nullptr, "ConfirmPlayableApplicationVideo"},
  42. {1016, nullptr, "ConfirmShowNewsPermission"},
  43. {1017, &IParentalControlService::EndFreeCommunication, "EndFreeCommunication"},
  44. {1018, &IParentalControlService::IsFreeCommunicationAvailable, "IsFreeCommunicationAvailable"},
  45. {1031, &IParentalControlService::IsRestrictionEnabled, "IsRestrictionEnabled"},
  46. {1032, &IParentalControlService::GetSafetyLevel, "GetSafetyLevel"},
  47. {1033, nullptr, "SetSafetyLevel"},
  48. {1034, nullptr, "GetSafetyLevelSettings"},
  49. {1035, &IParentalControlService::GetCurrentSettings, "GetCurrentSettings"},
  50. {1036, nullptr, "SetCustomSafetyLevelSettings"},
  51. {1037, nullptr, "GetDefaultRatingOrganization"},
  52. {1038, nullptr, "SetDefaultRatingOrganization"},
  53. {1039, &IParentalControlService::GetFreeCommunicationApplicationListCount, "GetFreeCommunicationApplicationListCount"},
  54. {1042, nullptr, "AddToFreeCommunicationApplicationList"},
  55. {1043, nullptr, "DeleteSettings"},
  56. {1044, nullptr, "GetFreeCommunicationApplicationList"},
  57. {1045, nullptr, "UpdateFreeCommunicationApplicationList"},
  58. {1046, nullptr, "DisableFeaturesForReset"},
  59. {1047, nullptr, "NotifyApplicationDownloadStarted"},
  60. {1048, nullptr, "NotifyNetworkProfileCreated"},
  61. {1049, nullptr, "ResetFreeCommunicationApplicationList"},
  62. {1061, &IParentalControlService::ConfirmStereoVisionRestrictionConfigurable, "ConfirmStereoVisionRestrictionConfigurable"},
  63. {1062, &IParentalControlService::GetStereoVisionRestriction, "GetStereoVisionRestriction"},
  64. {1063, &IParentalControlService::SetStereoVisionRestriction, "SetStereoVisionRestriction"},
  65. {1064, &IParentalControlService::ResetConfirmedStereoVisionPermission, "ResetConfirmedStereoVisionPermission"},
  66. {1065, &IParentalControlService::IsStereoVisionPermitted, "IsStereoVisionPermitted"},
  67. {1201, nullptr, "UnlockRestrictionTemporarily"},
  68. {1202, nullptr, "UnlockSystemSettingsRestriction"},
  69. {1203, nullptr, "SetPinCode"},
  70. {1204, nullptr, "GenerateInquiryCode"},
  71. {1205, nullptr, "CheckMasterKey"},
  72. {1206, nullptr, "GetPinCodeLength"},
  73. {1207, nullptr, "GetPinCodeChangedEvent"},
  74. {1208, nullptr, "GetPinCode"},
  75. {1403, &IParentalControlService::IsPairingActive, "IsPairingActive"},
  76. {1406, nullptr, "GetSettingsLastUpdated"},
  77. {1411, nullptr, "GetPairingAccountInfo"},
  78. {1421, nullptr, "GetAccountNickname"},
  79. {1424, nullptr, "GetAccountState"},
  80. {1425, nullptr, "RequestPostEvents"},
  81. {1426, nullptr, "GetPostEventInterval"},
  82. {1427, nullptr, "SetPostEventInterval"},
  83. {1432, &IParentalControlService::GetSynchronizationEvent, "GetSynchronizationEvent"},
  84. {1451, nullptr, "StartPlayTimer"},
  85. {1452, nullptr, "StopPlayTimer"},
  86. {1453, nullptr, "IsPlayTimerEnabled"},
  87. {1454, nullptr, "GetPlayTimerRemainingTime"},
  88. {1455, nullptr, "IsRestrictedByPlayTimer"},
  89. {1456, &IParentalControlService::GetPlayTimerSettings, "GetPlayTimerSettings"},
  90. {1457, &IParentalControlService::GetPlayTimerEventToRequestSuspension, "GetPlayTimerEventToRequestSuspension"},
  91. {1458, &IParentalControlService::IsPlayTimerAlarmDisabled, "IsPlayTimerAlarmDisabled"},
  92. {1471, nullptr, "NotifyWrongPinCodeInputManyTimes"},
  93. {1472, nullptr, "CancelNetworkRequest"},
  94. {1473, &IParentalControlService::GetUnlinkedEvent, "GetUnlinkedEvent"},
  95. {1474, nullptr, "ClearUnlinkedEvent"},
  96. {1601, nullptr, "DisableAllFeatures"},
  97. {1602, nullptr, "PostEnableAllFeatures"},
  98. {1603, nullptr, "IsAllFeaturesDisabled"},
  99. {1901, nullptr, "DeleteFromFreeCommunicationApplicationListForDebug"},
  100. {1902, nullptr, "ClearFreeCommunicationApplicationListForDebug"},
  101. {1903, nullptr, "GetExemptApplicationListCountForDebug"},
  102. {1904, nullptr, "GetExemptApplicationListForDebug"},
  103. {1905, nullptr, "UpdateExemptApplicationListForDebug"},
  104. {1906, nullptr, "AddToExemptApplicationListForDebug"},
  105. {1907, nullptr, "DeleteFromExemptApplicationListForDebug"},
  106. {1908, nullptr, "ClearExemptApplicationListForDebug"},
  107. {1941, nullptr, "DeletePairing"},
  108. {1951, nullptr, "SetPlayTimerSettingsForDebug"},
  109. {1952, nullptr, "GetPlayTimerSpentTimeForTest"},
  110. {1953, nullptr, "SetPlayTimerAlarmDisabledForDebug"},
  111. {2001, nullptr, "RequestPairingAsync"},
  112. {2002, nullptr, "FinishRequestPairing"},
  113. {2003, nullptr, "AuthorizePairingAsync"},
  114. {2004, nullptr, "FinishAuthorizePairing"},
  115. {2005, nullptr, "RetrievePairingInfoAsync"},
  116. {2006, nullptr, "FinishRetrievePairingInfo"},
  117. {2007, nullptr, "UnlinkPairingAsync"},
  118. {2008, nullptr, "FinishUnlinkPairing"},
  119. {2009, nullptr, "GetAccountMiiImageAsync"},
  120. {2010, nullptr, "FinishGetAccountMiiImage"},
  121. {2011, nullptr, "GetAccountMiiImageContentTypeAsync"},
  122. {2012, nullptr, "FinishGetAccountMiiImageContentType"},
  123. {2013, nullptr, "SynchronizeParentalControlSettingsAsync"},
  124. {2014, nullptr, "FinishSynchronizeParentalControlSettings"},
  125. {2015, nullptr, "FinishSynchronizeParentalControlSettingsWithLastUpdated"},
  126. {2016, nullptr, "RequestUpdateExemptionListAsync"},
  127. };
  128. // clang-format on
  129. RegisterHandlers(functions);
  130. synchronization_event =
  131. service_context.CreateEvent("IParentalControlService::SynchronizationEvent");
  132. unlinked_event = service_context.CreateEvent("IParentalControlService::UnlinkedEvent");
  133. request_suspension_event =
  134. service_context.CreateEvent("IParentalControlService::RequestSuspensionEvent");
  135. }
  136. private:
  137. bool CheckFreeCommunicationPermissionImpl() const {
  138. if (states.temporary_unlocked) {
  139. return true;
  140. }
  141. if ((states.application_info.parental_control_flag & 1) == 0) {
  142. return true;
  143. }
  144. if (pin_code[0] == '\0') {
  145. return true;
  146. }
  147. if (!settings.is_free_communication_default_on) {
  148. return true;
  149. }
  150. // TODO(ogniK): Check for blacklisted/exempted applications. Return false can happen here
  151. // but as we don't have multiproceses support yet, we can just assume our application is
  152. // valid for the time being
  153. return true;
  154. }
  155. bool ConfirmStereoVisionPermissionImpl() const {
  156. if (states.temporary_unlocked) {
  157. return true;
  158. }
  159. if (pin_code[0] == '\0') {
  160. return true;
  161. }
  162. if (!settings.is_stero_vision_restricted) {
  163. return false;
  164. }
  165. return true;
  166. }
  167. void SetStereoVisionRestrictionImpl(bool is_restricted) {
  168. if (settings.disabled) {
  169. return;
  170. }
  171. if (pin_code[0] == '\0') {
  172. return;
  173. }
  174. settings.is_stero_vision_restricted = is_restricted;
  175. }
  176. void Initialize(HLERequestContext& ctx) {
  177. LOG_DEBUG(Service_PCTL, "called");
  178. IPC::ResponseBuilder rb{ctx, 2};
  179. if (False(capability & (Capability::Application | Capability::System))) {
  180. LOG_ERROR(Service_PCTL, "Invalid capability! capability={:X}", capability);
  181. return;
  182. }
  183. // TODO(ogniK): Recovery flag initialization for pctl:r
  184. const auto tid = system.GetApplicationProcessProgramID();
  185. if (tid != 0) {
  186. const FileSys::PatchManager pm{tid, system.GetFileSystemController(),
  187. system.GetContentProvider()};
  188. const auto control = pm.GetControlMetadata();
  189. if (control.first) {
  190. states.tid_from_event = 0;
  191. states.launch_time_valid = false;
  192. states.is_suspended = false;
  193. states.free_communication = false;
  194. states.stereo_vision = false;
  195. states.application_info = ApplicationInfo{
  196. .tid = tid,
  197. .age_rating = control.first->GetRatingAge(),
  198. .parental_control_flag = control.first->GetParentalControlFlag(),
  199. .capability = capability,
  200. };
  201. if (False(capability & (Capability::System | Capability::Recovery))) {
  202. // TODO(ogniK): Signal application launch event
  203. }
  204. }
  205. }
  206. rb.Push(ResultSuccess);
  207. }
  208. void CheckFreeCommunicationPermission(HLERequestContext& ctx) {
  209. LOG_DEBUG(Service_PCTL, "called");
  210. IPC::ResponseBuilder rb{ctx, 2};
  211. if (!CheckFreeCommunicationPermissionImpl()) {
  212. rb.Push(Error::ResultNoFreeCommunication);
  213. } else {
  214. rb.Push(ResultSuccess);
  215. }
  216. states.free_communication = true;
  217. }
  218. void IsRestrictionTemporaryUnlocked(HLERequestContext& ctx) {
  219. const bool is_temporary_unlocked = false;
  220. LOG_WARNING(Service_PCTL, "(STUBBED) called, is_temporary_unlocked={}",
  221. is_temporary_unlocked);
  222. IPC::ResponseBuilder rb{ctx, 3};
  223. rb.Push(ResultSuccess);
  224. rb.Push<u8>(is_temporary_unlocked);
  225. }
  226. void ConfirmStereoVisionPermission(HLERequestContext& ctx) {
  227. LOG_DEBUG(Service_PCTL, "called");
  228. states.stereo_vision = true;
  229. IPC::ResponseBuilder rb{ctx, 2};
  230. rb.Push(ResultSuccess);
  231. }
  232. void EndFreeCommunication(HLERequestContext& ctx) {
  233. LOG_WARNING(Service_PCTL, "(STUBBED) called");
  234. IPC::ResponseBuilder rb{ctx, 2};
  235. rb.Push(ResultSuccess);
  236. }
  237. void IsFreeCommunicationAvailable(HLERequestContext& ctx) {
  238. LOG_WARNING(Service_PCTL, "(STUBBED) called");
  239. IPC::ResponseBuilder rb{ctx, 2};
  240. if (!CheckFreeCommunicationPermissionImpl()) {
  241. rb.Push(Error::ResultNoFreeCommunication);
  242. } else {
  243. rb.Push(ResultSuccess);
  244. }
  245. }
  246. void IsRestrictionEnabled(HLERequestContext& ctx) {
  247. LOG_DEBUG(Service_PCTL, "called");
  248. IPC::ResponseBuilder rb{ctx, 3};
  249. if (False(capability & (Capability::Status | Capability::Recovery))) {
  250. LOG_ERROR(Service_PCTL, "Application does not have Status or Recovery capabilities!");
  251. rb.Push(Error::ResultNoCapability);
  252. rb.Push(false);
  253. return;
  254. }
  255. rb.Push(pin_code[0] != '\0');
  256. }
  257. void GetSafetyLevel(HLERequestContext& ctx) {
  258. const u32 safety_level = 0;
  259. LOG_WARNING(Service_PCTL, "(STUBBED) called, safety_level={}", safety_level);
  260. IPC::ResponseBuilder rb{ctx, 3};
  261. rb.Push(ResultSuccess);
  262. rb.Push(safety_level);
  263. }
  264. void GetCurrentSettings(HLERequestContext& ctx) {
  265. LOG_INFO(Service_PCTL, "called");
  266. IPC::ResponseBuilder rb{ctx, 3};
  267. rb.Push(ResultSuccess);
  268. rb.PushRaw(restriction_settings);
  269. }
  270. void GetFreeCommunicationApplicationListCount(HLERequestContext& ctx) {
  271. const u32 count = 4;
  272. LOG_WARNING(Service_PCTL, "(STUBBED) called, count={}", count);
  273. IPC::ResponseBuilder rb{ctx, 3};
  274. rb.Push(ResultSuccess);
  275. rb.Push(count);
  276. }
  277. void ConfirmStereoVisionRestrictionConfigurable(HLERequestContext& ctx) {
  278. LOG_DEBUG(Service_PCTL, "called");
  279. IPC::ResponseBuilder rb{ctx, 2};
  280. if (False(capability & Capability::StereoVision)) {
  281. LOG_ERROR(Service_PCTL, "Application does not have StereoVision capability!");
  282. rb.Push(Error::ResultNoCapability);
  283. return;
  284. }
  285. if (pin_code[0] == '\0') {
  286. rb.Push(Error::ResultNoRestrictionEnabled);
  287. return;
  288. }
  289. rb.Push(ResultSuccess);
  290. }
  291. void IsStereoVisionPermitted(HLERequestContext& ctx) {
  292. LOG_DEBUG(Service_PCTL, "called");
  293. IPC::ResponseBuilder rb{ctx, 3};
  294. if (!ConfirmStereoVisionPermissionImpl()) {
  295. rb.Push(Error::ResultStereoVisionRestricted);
  296. rb.Push(false);
  297. } else {
  298. rb.Push(ResultSuccess);
  299. rb.Push(true);
  300. }
  301. }
  302. void IsPairingActive(HLERequestContext& ctx) {
  303. const bool is_pairing_active = false;
  304. LOG_WARNING(Service_PCTL, "(STUBBED) called, is_pairing_active={}", is_pairing_active);
  305. IPC::ResponseBuilder rb{ctx, 3};
  306. rb.Push(ResultSuccess);
  307. rb.Push<u8>(is_pairing_active);
  308. }
  309. void GetSynchronizationEvent(HLERequestContext& ctx) {
  310. LOG_INFO(Service_PCTL, "called");
  311. IPC::ResponseBuilder rb{ctx, 2, 1};
  312. rb.Push(ResultSuccess);
  313. rb.PushCopyObjects(synchronization_event->GetReadableEvent());
  314. }
  315. void GetPlayTimerSettings(HLERequestContext& ctx) {
  316. LOG_WARNING(Service_PCTL, "(STUBBED) called");
  317. const PlayTimerSettings timer_settings{};
  318. IPC::ResponseBuilder rb{ctx, 15};
  319. rb.Push(ResultSuccess);
  320. rb.PushRaw(timer_settings);
  321. }
  322. void GetPlayTimerEventToRequestSuspension(HLERequestContext& ctx) {
  323. LOG_INFO(Service_PCTL, "called");
  324. IPC::ResponseBuilder rb{ctx, 2, 1};
  325. rb.Push(ResultSuccess);
  326. rb.PushCopyObjects(request_suspension_event->GetReadableEvent());
  327. }
  328. void IsPlayTimerAlarmDisabled(HLERequestContext& ctx) {
  329. const bool is_play_timer_alarm_disabled = false;
  330. LOG_INFO(Service_PCTL, "called, is_play_timer_alarm_disabled={}",
  331. is_play_timer_alarm_disabled);
  332. IPC::ResponseBuilder rb{ctx, 3};
  333. rb.Push(ResultSuccess);
  334. rb.Push<u8>(is_play_timer_alarm_disabled);
  335. }
  336. void GetUnlinkedEvent(HLERequestContext& ctx) {
  337. LOG_INFO(Service_PCTL, "called");
  338. IPC::ResponseBuilder rb{ctx, 2, 1};
  339. rb.Push(ResultSuccess);
  340. rb.PushCopyObjects(unlinked_event->GetReadableEvent());
  341. }
  342. void SetStereoVisionRestriction(HLERequestContext& ctx) {
  343. IPC::RequestParser rp{ctx};
  344. const auto can_use = rp.Pop<bool>();
  345. LOG_DEBUG(Service_PCTL, "called, can_use={}", can_use);
  346. IPC::ResponseBuilder rb{ctx, 2};
  347. if (False(capability & Capability::StereoVision)) {
  348. LOG_ERROR(Service_PCTL, "Application does not have StereoVision capability!");
  349. rb.Push(Error::ResultNoCapability);
  350. return;
  351. }
  352. SetStereoVisionRestrictionImpl(can_use);
  353. rb.Push(ResultSuccess);
  354. }
  355. void GetStereoVisionRestriction(HLERequestContext& ctx) {
  356. LOG_DEBUG(Service_PCTL, "called");
  357. IPC::ResponseBuilder rb{ctx, 3};
  358. if (False(capability & Capability::StereoVision)) {
  359. LOG_ERROR(Service_PCTL, "Application does not have StereoVision capability!");
  360. rb.Push(Error::ResultNoCapability);
  361. rb.Push(false);
  362. return;
  363. }
  364. rb.Push(ResultSuccess);
  365. rb.Push(settings.is_stero_vision_restricted);
  366. }
  367. void ResetConfirmedStereoVisionPermission(HLERequestContext& ctx) {
  368. LOG_DEBUG(Service_PCTL, "called");
  369. states.stereo_vision = false;
  370. IPC::ResponseBuilder rb{ctx, 2};
  371. rb.Push(ResultSuccess);
  372. }
  373. struct ApplicationInfo {
  374. u64 tid{};
  375. std::array<u8, 32> age_rating{};
  376. u32 parental_control_flag{};
  377. Capability capability{};
  378. };
  379. struct States {
  380. u64 current_tid{};
  381. ApplicationInfo application_info{};
  382. u64 tid_from_event{};
  383. bool launch_time_valid{};
  384. bool is_suspended{};
  385. bool temporary_unlocked{};
  386. bool free_communication{};
  387. bool stereo_vision{};
  388. };
  389. struct ParentalControlSettings {
  390. bool is_stero_vision_restricted{};
  391. bool is_free_communication_default_on{};
  392. bool disabled{};
  393. };
  394. // This is nn::pctl::RestrictionSettings
  395. struct RestrictionSettings {
  396. u8 rating_age;
  397. bool sns_post_restriction;
  398. bool free_communication_restriction;
  399. };
  400. static_assert(sizeof(RestrictionSettings) == 0x3, "RestrictionSettings has incorrect size.");
  401. // This is nn::pctl::PlayTimerSettings
  402. struct PlayTimerSettings {
  403. std::array<u32, 13> settings;
  404. };
  405. static_assert(sizeof(PlayTimerSettings) == 0x34, "PlayTimerSettings has incorrect size.");
  406. States states{};
  407. ParentalControlSettings settings{};
  408. RestrictionSettings restriction_settings{};
  409. std::array<char, 8> pin_code{};
  410. Capability capability{};
  411. Kernel::KEvent* synchronization_event;
  412. Kernel::KEvent* unlinked_event;
  413. Kernel::KEvent* request_suspension_event;
  414. KernelHelpers::ServiceContext service_context;
  415. };
  416. void Module::Interface::CreateService(HLERequestContext& ctx) {
  417. LOG_DEBUG(Service_PCTL, "called");
  418. IPC::ResponseBuilder rb{ctx, 2, 0, 1};
  419. rb.Push(ResultSuccess);
  420. // TODO(ogniK): Get TID from process
  421. rb.PushIpcInterface<IParentalControlService>(system, capability);
  422. }
  423. void Module::Interface::CreateServiceWithoutInitialize(HLERequestContext& ctx) {
  424. LOG_DEBUG(Service_PCTL, "called");
  425. IPC::ResponseBuilder rb{ctx, 2, 0, 1};
  426. rb.Push(ResultSuccess);
  427. rb.PushIpcInterface<IParentalControlService>(system, capability);
  428. }
  429. Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_,
  430. const char* name_, Capability capability_)
  431. : ServiceFramework{system_, name_}, module{std::move(module_)}, capability{capability_} {}
  432. Module::Interface::~Interface() = default;
  433. void LoopProcess(Core::System& system) {
  434. auto server_manager = std::make_unique<ServerManager>(system);
  435. auto module = std::make_shared<Module>();
  436. server_manager->RegisterNamedService(
  437. "pctl", std::make_shared<PCTL>(system, module, "pctl",
  438. Capability::Application | Capability::SnsPost |
  439. Capability::Status | Capability::StereoVision));
  440. // TODO(ogniK): Implement remaining capabilities
  441. server_manager->RegisterNamedService(
  442. "pctl:a", std::make_shared<PCTL>(system, module, "pctl:a", Capability::None));
  443. server_manager->RegisterNamedService(
  444. "pctl:r", std::make_shared<PCTL>(system, module, "pctl:r", Capability::None));
  445. server_manager->RegisterNamedService(
  446. "pctl:s", std::make_shared<PCTL>(system, module, "pctl:s", Capability::None));
  447. ServerManager::RunServer(std::move(server_manager));
  448. }
  449. } // namespace Service::PCTL