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