Line data Source code
1 : /*
2 : * If not stated otherwise in this file or this component's LICENSE file the
3 : * following copyright and licenses apply:
4 : *
5 : * Copyright 2022 Sky UK
6 : *
7 : * Licensed under the Apache License, Version 2.0 (the "License");
8 : * you may not use this file except in compliance with the License.
9 : * You may obtain a copy of the License at
10 : *
11 : * http://www.apache.org/licenses/LICENSE-2.0
12 : *
13 : * Unless required by applicable law or agreed to in writing, software
14 : * distributed under the License is distributed on an "AS IS" BASIS,
15 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 : * See the License for the specific language governing permissions and
17 : * limitations under the License.
18 : */
19 :
20 : #include "SessionServerManager.h"
21 : #include "IApplicationManagementServer.h"
22 : #include "IIpcFactory.h"
23 : #include "ISessionManagementServer.h"
24 : #include "RialtoServerLogging.h"
25 :
26 : #include <algorithm>
27 : #include <stdexcept>
28 : #include <utility>
29 :
30 : namespace
31 : {
32 5 : inline bool isNumber(const std::string &text)
33 : {
34 10 : return std::find_if(text.begin(), text.end(), [](const auto &letter) { return !std::isdigit(letter); }) == text.end();
35 : }
36 : } // namespace
37 :
38 : namespace firebolt::rialto::server::service
39 : {
40 28 : SessionServerManager::SessionServerManager(const ipc::IIpcFactory &ipcFactory, IPlaybackService &playbackService,
41 : ICdmService &cdmService, IControlService &controlService,
42 28 : std::unique_ptr<IHeartbeatProcedureFactory> &&heartbeatProcedureFactory)
43 28 : : m_playbackService{playbackService}, m_cdmService{cdmService}, m_controlService{controlService},
44 28 : m_heartbeatProcedureFactory{std::move(heartbeatProcedureFactory)},
45 28 : m_applicationManagementServer{ipcFactory.createApplicationManagementServer(*this)},
46 28 : m_sessionManagementServer{ipcFactory.createSessionManagementServer(playbackService, cdmService, controlService)},
47 56 : m_isServiceRunning{true}, m_currentState{common::SessionServerState::UNINITIALIZED}
48 : {
49 28 : RIALTO_SERVER_LOG_INFO("Starting Rialto Server Service");
50 : }
51 :
52 56 : SessionServerManager::~SessionServerManager()
53 : {
54 28 : RIALTO_SERVER_LOG_INFO("Stopping Rialto Server Service");
55 :
56 : // The following reset() will ensure that the thread ApplicationManagementServer::m_ipcServerThread
57 : // isn't currently calling any methods within this class (while it is being destructed)
58 : // Particularly, the mentioned thread is responsible for calling the method
59 : // SessionServerManager::teardownService() which then triggers a call to this destuctor
60 : // after it calls stopService()
61 28 : m_applicationManagementServer.reset();
62 :
63 28 : stopService();
64 56 : }
65 :
66 7 : bool SessionServerManager::initialize(int argc, char *argv[])
67 : try
68 : {
69 7 : if (argc != 2)
70 : {
71 2 : RIALTO_SERVER_LOG_ERROR("Wrong number of arguments. Rialto Server Service will close now.");
72 2 : return false;
73 : }
74 10 : std::string socketStr{argv[1]};
75 5 : if (!isNumber(socketStr))
76 : {
77 1 : RIALTO_SERVER_LOG_ERROR("Rialto App Management socket is not a number.");
78 1 : return false;
79 : }
80 4 : if (!m_applicationManagementServer->initialize(std::stoi(socketStr)))
81 : {
82 1 : RIALTO_SERVER_LOG_ERROR("Initialization of Application Management server failed.");
83 1 : return false;
84 : }
85 2 : m_applicationManagementServer->start();
86 2 : return m_applicationManagementServer->sendStateChangedEvent(common::SessionServerState::UNINITIALIZED);
87 5 : }
88 1 : catch (const std::exception &e)
89 : {
90 1 : RIALTO_SERVER_LOG_ERROR("Exception caught during service initialization: %s", e.what());
91 1 : return false;
92 : }
93 :
94 1 : void SessionServerManager::startService()
95 : {
96 1 : std::unique_lock<std::mutex> lock{m_serviceMutex};
97 3 : m_serviceCv.wait(lock, [this]() { return !m_isServiceRunning; });
98 1 : }
99 :
100 32 : void SessionServerManager::stopService()
101 : {
102 32 : std::unique_lock<std::mutex> lock{m_serviceMutex};
103 32 : if (m_isServiceRunning)
104 : {
105 28 : m_isServiceRunning = false;
106 28 : m_serviceCv.notify_one();
107 : }
108 32 : }
109 :
110 3 : bool SessionServerManager::configureIpc(const std::string &socketName, unsigned int socketPermissions,
111 : const std::string &socketOwner, const std::string &socketGroup)
112 : {
113 3 : if (!m_sessionManagementServer->initialize(socketName, socketPermissions, socketOwner, socketGroup))
114 : {
115 1 : RIALTO_SERVER_LOG_ERROR("configureIpc failed - SessionManagementServer failed to initialize");
116 1 : return false;
117 : }
118 2 : return true;
119 : }
120 :
121 2 : bool SessionServerManager::configureIpc(int32_t socketFd)
122 : {
123 2 : if (!m_sessionManagementServer->initialize(socketFd))
124 : {
125 1 : RIALTO_SERVER_LOG_ERROR("configureIpc failed - SessionManagementServer failed to initialize");
126 1 : return false;
127 : }
128 1 : return true;
129 : }
130 :
131 3 : bool SessionServerManager::configureServices(const common::SessionServerState &state,
132 : const common::MaxResourceCapabilitites &maxResource,
133 : const std::string &clientDisplayName, const std::string &appName)
134 : {
135 3 : m_sessionManagementServer->start();
136 3 : m_playbackService.setMaxPlaybacks(maxResource.maxPlaybacks);
137 3 : m_playbackService.setMaxWebAudioPlayers(maxResource.maxWebAudioPlayers);
138 3 : m_playbackService.setClientDisplayName(clientDisplayName);
139 3 : m_playbackService.setResourceManagerAppName(appName);
140 3 : return setState(state);
141 : }
142 :
143 22 : bool SessionServerManager::setState(const common::SessionServerState &state)
144 : {
145 22 : switch (state)
146 : {
147 7 : case common::SessionServerState::ACTIVE:
148 : {
149 7 : return switchToActive();
150 : }
151 8 : case common::SessionServerState::INACTIVE:
152 : {
153 8 : return switchToInactive();
154 : }
155 5 : case common::SessionServerState::NOT_RUNNING:
156 : case common::SessionServerState::SUSPENDED:
157 : {
158 5 : return teardownService(state);
159 : }
160 2 : default:
161 : {
162 2 : RIALTO_SERVER_LOG_ERROR("SetState failed - unsupported state");
163 2 : m_applicationManagementServer->sendStateChangedEvent(common::SessionServerState::ERROR);
164 : }
165 : }
166 2 : return false;
167 : }
168 :
169 1 : void SessionServerManager::setLogLevels(RIALTO_DEBUG_LEVEL defaultLogLevels, RIALTO_DEBUG_LEVEL clientLogLevels,
170 : RIALTO_DEBUG_LEVEL sessionServerLogLevels, RIALTO_DEBUG_LEVEL ipcLogLevels,
171 : RIALTO_DEBUG_LEVEL serverManagerLogLevels, RIALTO_DEBUG_LEVEL commonLogLevels)
172 : {
173 1 : firebolt::rialto::logging::setLogLevels(RIALTO_COMPONENT_DEFAULT, defaultLogLevels);
174 1 : firebolt::rialto::logging::setLogLevels(RIALTO_COMPONENT_CLIENT, clientLogLevels);
175 1 : firebolt::rialto::logging::setLogLevels(RIALTO_COMPONENT_SERVER, sessionServerLogLevels);
176 1 : firebolt::rialto::logging::setLogLevels(RIALTO_COMPONENT_IPC, ipcLogLevels);
177 1 : firebolt::rialto::logging::setLogLevels(RIALTO_COMPONENT_SERVER_MANAGER, serverManagerLogLevels);
178 1 : firebolt::rialto::logging::setLogLevels(RIALTO_COMPONENT_COMMON, commonLogLevels);
179 1 : m_sessionManagementServer->setLogLevels(defaultLogLevels, clientLogLevels, ipcLogLevels, commonLogLevels);
180 : }
181 :
182 2 : bool SessionServerManager::ping(std::int32_t id, const std::shared_ptr<IAckSender> &ackSender)
183 : {
184 2 : auto heartbeatProcedure{m_heartbeatProcedureFactory->createHeartbeatProcedure(ackSender, id)};
185 :
186 : // Check all rialto server internal threads
187 2 : m_cdmService.ping(heartbeatProcedure);
188 2 : m_playbackService.ping(heartbeatProcedure);
189 :
190 : // Check all Rialto Clients
191 4 : return m_controlService.ping(heartbeatProcedure);
192 2 : }
193 :
194 0 : void SessionServerManager::setPreloadedCapabilities(const std::optional<common::AudioDecoderCapabilities> &audioCaps,
195 : const std::optional<common::VideoDecoderCapabilities> &videoCaps)
196 : {
197 0 : RIALTO_SERVER_LOG_DEBUG("setPreloadedCapabilities called with audio: %s, video: %s",
198 : audioCaps.has_value() ? "yes" : "no", videoCaps.has_value() ? "yes" : "no");
199 :
200 : // Forward preloaded capabilities to MediaPipelineService
201 : // (Capabilities are stored in MediaCapabilities, not in this service layer)
202 0 : m_playbackService.getMediaPipelineService().setPreloadedCapabilities(audioCaps, videoCaps);
203 : }
204 :
205 7 : bool SessionServerManager::switchToActive()
206 : {
207 7 : const auto currentState{m_currentState.load()};
208 7 : if (currentState == common::SessionServerState::ACTIVE)
209 : {
210 1 : RIALTO_SERVER_LOG_DEBUG("Session server already in Active state.");
211 1 : return true;
212 : }
213 6 : if (!m_playbackService.switchToActive())
214 : {
215 1 : RIALTO_SERVER_LOG_ERROR("Player service failed to switch to active state");
216 1 : return false;
217 : }
218 5 : if (!m_cdmService.switchToActive())
219 : {
220 1 : RIALTO_SERVER_LOG_ERROR("Cdm service failed to switch to active state");
221 1 : m_playbackService.switchToInactive();
222 1 : return false;
223 : }
224 4 : if (m_applicationManagementServer->sendStateChangedEvent(common::SessionServerState::ACTIVE))
225 : {
226 3 : m_controlService.setApplicationState(ApplicationState::RUNNING);
227 3 : m_sessionManagementServer->notifyApplicationStateChanged(ApplicationState::RUNNING);
228 3 : m_currentState.store(common::SessionServerState::ACTIVE);
229 3 : RIALTO_SERVER_LOG_MIL("RialtoServer state is ACTIVE now");
230 3 : return true;
231 : }
232 1 : m_playbackService.switchToInactive();
233 1 : m_cdmService.switchToInactive();
234 1 : return false;
235 : }
236 :
237 8 : bool SessionServerManager::switchToInactive()
238 : {
239 8 : const auto currentState{m_currentState.load()};
240 8 : if (currentState == common::SessionServerState::INACTIVE)
241 : {
242 1 : RIALTO_SERVER_LOG_DEBUG("Session server already in Inactive state.");
243 1 : return true;
244 : }
245 7 : m_playbackService.switchToInactive();
246 7 : m_cdmService.switchToInactive();
247 : // Record INACTIVE memory snapshot immediately after resource teardown,
248 : // before the manager ACK — ensures we capture it even if the socket breaks.
249 7 : m_sessionManagementServer->notifyApplicationStateChanged(ApplicationState::INACTIVE);
250 7 : if (m_applicationManagementServer->sendStateChangedEvent(common::SessionServerState::INACTIVE))
251 : {
252 4 : m_controlService.setApplicationState(ApplicationState::INACTIVE);
253 4 : m_currentState.store(common::SessionServerState::INACTIVE);
254 4 : RIALTO_SERVER_LOG_MIL("RialtoServer state is INACTIVE now");
255 4 : return true;
256 : }
257 3 : if (currentState == common::SessionServerState::ACTIVE)
258 : {
259 1 : if (!m_playbackService.switchToActive())
260 : {
261 1 : RIALTO_SERVER_LOG_WARN("Player service failed to switch to active state");
262 : }
263 1 : if (!m_cdmService.switchToActive())
264 : {
265 1 : RIALTO_SERVER_LOG_WARN("Cdm service failed to switch to active state");
266 : }
267 : }
268 3 : return false;
269 : }
270 :
271 5 : bool SessionServerManager::teardownService(const common::SessionServerState &state)
272 : {
273 5 : RIALTO_SERVER_LOG_MIL("RialtoServer tear down requested");
274 9 : if (m_currentState.load() == common::SessionServerState::NOT_RUNNING ||
275 4 : m_currentState.load() == common::SessionServerState::SUSPENDED)
276 : {
277 1 : RIALTO_SERVER_LOG_DEBUG("Session server already in NotRunning or Suspended state.");
278 1 : return true;
279 : }
280 : // Free resources before sending notification to ServerManager
281 4 : m_playbackService.switchToInactive();
282 4 : m_cdmService.switchToInactive();
283 4 : m_controlService.setApplicationState(ApplicationState::UNKNOWN);
284 :
285 4 : bool result{true};
286 4 : if (m_applicationManagementServer->sendStateChangedEvent(state))
287 : {
288 3 : m_currentState.store(state);
289 : }
290 : else
291 : {
292 1 : result = false;
293 : }
294 :
295 : // stopService() needs to be the last command of this method.
296 : // It triggers destruction of SessionServerManager and therefore
297 : // some member variables may not be valid after this command
298 4 : stopService(); // This HAS TO BE LAST (see above)
299 4 : return result;
300 : }
301 : } // namespace firebolt::rialto::server::service
|