1 /**
2 * Copyright (C) 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <TextManager.h>
18 #include <ImsMediaTrace.h>
19 #include <ImsMediaNetworkUtil.h>
20
21 using namespace android;
22 TextManager* TextManager::sManager;
23
TextManager()24 TextManager::TextManager()
25 {
26 mRequestHandler.Init("TEXT_REQUEST_EVENT");
27 mResponseHandler.Init("TEXT_RESPONSE_EVENT");
28 }
29
~TextManager()30 TextManager::~TextManager()
31 {
32 mRequestHandler.Deinit();
33 mResponseHandler.Deinit();
34 sManager = nullptr;
35 }
36
getInstance()37 TextManager* TextManager::getInstance()
38 {
39 if (sManager == nullptr)
40 {
41 sManager = new TextManager();
42 }
43
44 return sManager;
45 }
46
getState(int sessionId)47 int TextManager::getState(int sessionId)
48 {
49 auto session = mSessions.find(sessionId);
50
51 if (session != mSessions.end())
52 {
53 return (session->second)->getState();
54 }
55 else
56 {
57 return kSessionStateClosed;
58 }
59 }
60
openSession(const int sessionId,const int rtpFd,const int rtcpFd,TextConfig * config)61 ImsMediaResult TextManager::openSession(
62 const int sessionId, const int rtpFd, const int rtcpFd, TextConfig* config)
63 {
64 IMLOGI1("[openSession] sessionId[%d]", sessionId);
65
66 if (rtpFd == -1 || rtcpFd == -1)
67 {
68 return RESULT_INVALID_PARAM;
69 }
70
71 if (!mSessions.count(sessionId))
72 {
73 std::unique_ptr<TextSession> session(new TextSession());
74 session->setSessionId(sessionId);
75 session->setLocalEndPoint(rtpFd, rtcpFd);
76
77 if (session->startGraph(config) != RESULT_SUCCESS)
78 {
79 IMLOGI0("[openSession] startGraph failed");
80 }
81
82 mSessions.insert(std::make_pair(sessionId, std::move(session)));
83 }
84 else
85 {
86 return RESULT_INVALID_PARAM;
87 }
88
89 return RESULT_SUCCESS;
90 }
91
closeSession(const int sessionId)92 ImsMediaResult TextManager::closeSession(const int sessionId)
93 {
94 IMLOGI1("[closeSession] sessionId[%d]", sessionId);
95
96 if (mSessions.count(sessionId))
97 {
98 mSessions.erase(sessionId);
99 return RESULT_SUCCESS;
100 }
101
102 return RESULT_INVALID_PARAM;
103 }
104
modifySession(const int sessionId,TextConfig * config)105 ImsMediaResult TextManager::modifySession(const int sessionId, TextConfig* config)
106 {
107 auto session = mSessions.find(sessionId);
108 IMLOGI1("[modifySession] sessionId[%d]", sessionId);
109
110 if (session != mSessions.end())
111 {
112 if ((config->getMediaDirection() == RtpConfig::MEDIA_DIRECTION_SEND_RECEIVE ||
113 config->getMediaDirection() == RtpConfig::MEDIA_DIRECTION_RECEIVE_ONLY ||
114 config->getMediaDirection() == RtpConfig::MEDIA_DIRECTION_SEND_ONLY))
115 {
116 if (!deactivateOtherSessionIfActive(sessionId))
117 {
118 return RESULT_NO_RESOURCES;
119 }
120 }
121
122 return (session->second)->startGraph(config);
123 }
124 else
125 {
126 IMLOGE1("[modifySession] no session id[%d]", sessionId);
127 return RESULT_INVALID_PARAM;
128 }
129 }
130
setMediaQualityThreshold(const int sessionId,MediaQualityThreshold * threshold)131 void TextManager::setMediaQualityThreshold(const int sessionId, MediaQualityThreshold* threshold)
132 {
133 auto session = mSessions.find(sessionId);
134 IMLOGI1("[setMediaQualityThreshold] sessionId[%d]", sessionId);
135
136 if (session != mSessions.end())
137 {
138 (session->second)->setMediaQualityThreshold(*threshold);
139 }
140 else
141 {
142 IMLOGE1("[setMediaQualityThreshold] no session id[%d]", sessionId);
143 }
144 }
145
sendRtt(const int sessionId,const android::String8 * text)146 ImsMediaResult TextManager::sendRtt(const int sessionId, const android::String8* text)
147 {
148 auto session = mSessions.find(sessionId);
149 IMLOGI1("[sendRtt] sessionId[%d]", sessionId);
150
151 if (session != mSessions.end())
152 {
153 return (session->second)->sendRtt(text);
154 }
155
156 return RESULT_INVALID_PARAM;
157 }
158
sendMessage(const int sessionId,const android::Parcel & parcel)159 void TextManager::sendMessage(const int sessionId, const android::Parcel& parcel)
160 {
161 int nMsg = parcel.readInt32();
162 status_t err = NO_ERROR;
163
164 switch (nMsg)
165 {
166 case kTextOpenSession:
167 {
168 int rtpFd = parcel.readInt32();
169 int rtcpFd = parcel.readInt32();
170 TextConfig* config = new TextConfig();
171 err = config->readFromParcel(&parcel);
172
173 if (err != NO_ERROR && err != -ENODATA)
174 {
175 IMLOGE1("[sendMessage] error readFromParcel[%d]", err);
176 }
177
178 EventParamOpenSession* param = new EventParamOpenSession(rtpFd, rtcpFd, config);
179 ImsMediaEventHandler::SendEvent(
180 "TEXT_REQUEST_EVENT", nMsg, sessionId, reinterpret_cast<uint64_t>(param));
181 }
182 break;
183 case kTextCloseSession:
184 ImsMediaEventHandler::SendEvent("TEXT_REQUEST_EVENT", nMsg, sessionId);
185 break;
186 case kTextModifySession:
187 {
188 TextConfig* config = new TextConfig();
189 config->readFromParcel(&parcel);
190
191 if (err != NO_ERROR)
192 {
193 IMLOGE1("[sendMessage] error readFromParcel[%d]", err);
194 }
195
196 ImsMediaEventHandler::SendEvent(
197 "TEXT_REQUEST_EVENT", nMsg, sessionId, reinterpret_cast<uint64_t>(config));
198 }
199 break;
200 case kTextSetMediaQualityThreshold:
201 {
202 MediaQualityThreshold* threshold = new MediaQualityThreshold();
203 threshold->readFromParcel(&parcel);
204 ImsMediaEventHandler::SendEvent(
205 "TEXT_REQUEST_EVENT", nMsg, sessionId, reinterpret_cast<uint64_t>(threshold));
206 }
207 break;
208 case kTextSendRtt:
209 {
210 android::String16 text;
211 parcel.readString16(&text);
212 android::String8* rttText = new String8(text.c_str());
213 ImsMediaEventHandler::SendEvent(
214 "TEXT_REQUEST_EVENT", nMsg, sessionId, reinterpret_cast<uint64_t>(rttText));
215 }
216 break;
217 default:
218 break;
219 }
220 }
221
processEvent(uint32_t event,uint64_t sessionId,uint64_t paramA,uint64_t paramB)222 void TextManager::RequestHandler::processEvent(
223 uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB)
224 {
225 IMLOGI4("[processEvent] event[%d], sessionId[%d], paramA[%d], paramB[%d]", event, sessionId,
226 paramA, paramB);
227
228 if (sManager == nullptr)
229 {
230 return;
231 }
232
233 ImsMediaResult result = RESULT_SUCCESS;
234
235 switch (event)
236 {
237 case kTextOpenSession:
238 {
239 EventParamOpenSession* param = reinterpret_cast<EventParamOpenSession*>(paramA);
240 if (param != nullptr)
241 {
242 TextConfig* pConfig = reinterpret_cast<TextConfig*>(param->mConfig);
243 result = sManager->openSession(
244 static_cast<int>(sessionId), param->rtpFd, param->rtcpFd, pConfig);
245
246 if (result == RESULT_SUCCESS)
247 {
248 ImsMediaEventHandler::SendEvent(
249 "TEXT_RESPONSE_EVENT", kTextOpenSessionSuccess, sessionId);
250 }
251 else
252 {
253 ImsMediaEventHandler::SendEvent(
254 "TEXT_RESPONSE_EVENT", kTextOpenSessionFailure, sessionId, result);
255 }
256
257 delete param;
258
259 if (pConfig != nullptr)
260 {
261 delete pConfig;
262 }
263 }
264 else
265 {
266 ImsMediaEventHandler::SendEvent("TEXT_RESPONSE_EVENT", kTextOpenSessionFailure,
267 sessionId, RESULT_INVALID_PARAM);
268 }
269 }
270 break;
271 case kTextCloseSession:
272 if (sManager->closeSession(static_cast<int>(sessionId)) == RESULT_SUCCESS)
273 {
274 ImsMediaEventHandler::SendEvent(
275 "TEXT_RESPONSE_EVENT", kTextSessionClosed, sessionId, 0, 0);
276 }
277 break;
278 case kTextModifySession:
279 {
280 TextConfig* config = reinterpret_cast<TextConfig*>(paramA);
281 result = sManager->modifySession(static_cast<int>(sessionId), config);
282 ImsMediaEventHandler::SendEvent(
283 "TEXT_RESPONSE_EVENT", kTextModifySessionResponse, sessionId, result, paramA);
284 }
285 break;
286 case kTextSetMediaQualityThreshold:
287 {
288 MediaQualityThreshold* threshold = reinterpret_cast<MediaQualityThreshold*>(paramA);
289
290 if (threshold != nullptr)
291 {
292 sManager->setMediaQualityThreshold(static_cast<int>(sessionId), threshold);
293 delete threshold;
294 }
295 }
296 break;
297 case kTextSendRtt:
298 {
299 android::String8* text = reinterpret_cast<android::String8*>(paramA);
300
301 if (text != nullptr)
302 {
303 sManager->sendRtt(static_cast<int>(sessionId), text);
304 delete text;
305 }
306 }
307 break;
308 default:
309 break;
310 }
311 }
312
processEvent(uint32_t event,uint64_t sessionId,uint64_t paramA,uint64_t paramB)313 void TextManager::ResponseHandler::processEvent(
314 uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB)
315 {
316 IMLOGI4("[processEvent] event[%d], sessionId[%d], paramA[%d], paramB[%d]", event, sessionId,
317 paramA, paramB);
318
319 if (sManager == nullptr)
320 {
321 return;
322 }
323
324 android::Parcel parcel;
325 switch (event)
326 {
327 case kTextOpenSessionSuccess:
328 case kTextOpenSessionFailure:
329 parcel.writeInt32(event);
330 parcel.writeInt32(static_cast<int>(sessionId));
331
332 if (event == kTextOpenSessionFailure)
333 {
334 // fail reason
335 parcel.writeInt32(static_cast<int>(paramA));
336 }
337
338 sManager->sendResponse(sessionId, parcel);
339 break;
340 case kTextModifySessionResponse: // fall through
341 {
342 parcel.writeInt32(event);
343 parcel.writeInt32(paramA); // result
344 TextConfig* config = reinterpret_cast<TextConfig*>(paramB);
345
346 if (config != nullptr)
347 {
348 config->writeToParcel(&parcel);
349 sManager->sendResponse(sessionId, parcel);
350 delete config;
351 }
352 }
353 break;
354 case kTextMediaInactivityInd:
355 parcel.writeInt32(event);
356 parcel.writeInt32(static_cast<int>(paramA)); // type
357 parcel.writeInt32(static_cast<int>(paramB)); // duration
358 sManager->sendResponse(sessionId, parcel);
359 break;
360 case kTextRttReceived:
361 {
362 parcel.writeInt32(event);
363 android::String8* text = reinterpret_cast<String8*>(paramA);
364
365 if (text != nullptr)
366 {
367 String16 rttText(*text);
368 parcel.writeString16(rttText);
369 sManager->sendResponse(sessionId, parcel);
370 delete text;
371 }
372 }
373 break;
374 case kTextSessionClosed:
375 parcel.writeInt32(event);
376 parcel.writeInt32(static_cast<int>(sessionId));
377 sManager->sendResponse(sessionId, parcel);
378 break;
379 default:
380 break;
381 }
382 }
383
deactivateOtherSessionIfActive(const int sessionId)384 bool TextManager::deactivateOtherSessionIfActive(const int sessionId)
385 {
386 for (auto const& session : mSessions)
387 {
388 if (session.first != sessionId)
389 {
390 SessionState state = (session.second)->getState();
391 if (state == kSessionStateActive)
392 {
393 IMLOGE1("[modifySession] Another session id[%d] is active", session.first);
394 if ((session.second)->deactivate())
395 {
396 IMLOGI1("[modifySession] Moved session id[%d] to inactive", session.first);
397 return true;
398 }
399 IMLOGE1("[modifySession] Failed to move session id[%d] to inactive", session.first);
400 return false;
401 }
402 }
403 }
404 return true;
405 }
406