1 //
2 // Copyright (C) 2020 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 #include "host/commands/modem_simulator/call_service.h"
17
18 #include <android-base/logging.h>
19
20 #include <chrono>
21 #include <iostream>
22 #include <thread>
23
24 #include "host/commands/modem_simulator/nvram_config.h"
25
26 namespace cuttlefish {
27
CallService(int32_t service_id,ChannelMonitor * channel_monitor,ThreadLooper * thread_looper)28 CallService::CallService(int32_t service_id, ChannelMonitor* channel_monitor,
29 ThreadLooper* thread_looper)
30 : ModemService(service_id, this->InitializeCommandHandlers(),
31 channel_monitor, thread_looper) {
32 InitializeServiceState();
33 }
34
InitializeServiceState()35 void CallService::InitializeServiceState() {
36 auto nvram_config = NvramConfig::Get();
37 auto instance = nvram_config->ForInstance(service_id_);
38 in_emergency_mode_ = instance.emergency_mode();
39
40 mute_on_ = false;
41 }
42
SetupDependency(SimService * sim,NetworkService * net)43 void CallService::SetupDependency(SimService* sim, NetworkService* net) {
44 sim_service_ = sim;
45 network_service_ = net;
46 }
47
InitializeCommandHandlers()48 std::vector<CommandHandler> CallService::InitializeCommandHandlers() {
49 std::vector<CommandHandler> command_handlers = {
50 CommandHandler("D",
51 [this](const Client& client, std::string& cmd) {
52 this->HandleDial(client, cmd);
53 }),
54 CommandHandler(
55 "A",
56 [this](const Client& client) { this->HandleAcceptCall(client); }),
57 CommandHandler(
58 "H",
59 [this](const Client& client) { this->HandleRejectCall(client); }),
60 CommandHandler(
61 "+CLCC",
62 [this](const Client& client) { this->HandleCurrentCalls(client); }),
63 CommandHandler("+CHLD=",
64 [this](const Client& client, std::string& cmd) {
65 this->HandleHangup(client, cmd);
66 }),
67 CommandHandler("+CMUT",
68 [this](const Client& client, std::string& cmd) {
69 this->HandleMute(client, cmd);
70 }),
71 CommandHandler("+VTS=",
72 [this](const Client& client, std::string& cmd) {
73 this->HandleSendDtmf(client, cmd);
74 }),
75 CommandHandler("+CUSD=",
76 [this](const Client& client, std::string& cmd) {
77 this->HandleCancelUssd(client, cmd);
78 }),
79 CommandHandler("+WSOS=0",
80 [this](const Client& client, std::string& cmd) {
81 this->HandleEmergencyMode(client, cmd);
82 }),
83 CommandHandler("+REMOTECALL",
84 [this](const Client& client, std::string& cmd) {
85 this->HandleRemoteCall(client, cmd);
86 }),
87 };
88 return (command_handlers);
89 }
90
91 // This also resumes held calls
SimulatePendingCallsAnswered()92 void CallService::SimulatePendingCallsAnswered() {
93 for (auto& iter : active_calls_) {
94 if (iter.second.isCallDialing()) {
95 iter.second.SetCallActive();
96 }
97 }
98 }
99
TimerWaitingRemoteCallResponse(CallToken call_token)100 void CallService::TimerWaitingRemoteCallResponse(CallToken call_token) {
101 LOG(DEBUG) << "Dialing id: " << call_token.first
102 << ", number: " << call_token.second << "timeout, cancel";
103 auto iter = active_calls_.find(call_token.first);
104 if (iter != active_calls_.end() && iter->second.number == call_token.second) {
105 if (iter->second.remote_client != std::nullopt) {
106 CloseRemoteConnection(*(iter->second.remote_client));
107 }
108 active_calls_.erase(iter); // match
109 CallStateUpdate();
110 } // else not match, ignore
111 }
112
113 /* ATD */
HandleDial(const Client & client,const std::string & command)114 void CallService::HandleDial(const Client& client, const std::string& command) {
115 // Check the network registration state
116 auto registration_state = NetworkService::NET_REGISTRATION_UNKNOWN;
117 if (network_service_) {
118 registration_state = network_service_->GetVoiceRegistrationState();
119 }
120
121 bool emergency_only = false;
122 if (registration_state == NetworkService::NET_REGISTRATION_HOME ||
123 registration_state == NetworkService::NET_REGISTRATION_ROAMING) {
124 emergency_only = false;
125 } else if (registration_state == NetworkService::NET_REGISTRATION_EMERGENCY) {
126 emergency_only = true;
127 } else {
128 client.SendCommandResponse(kCmeErrorNoNetworkService);
129 return;
130 }
131
132 CommandParser cmd(command);
133 cmd.SkipPrefixAT();
134
135 std::string number;
136 bool emergency_number = false;
137 /**
138 * Normal dial: ATDnumber[clir];
139 * Emergency dial: ATDnumber@[category],#[clir];
140 */
141 auto pos = cmd->find_last_of('@');
142 if (pos != std::string_view::npos) {
143 emergency_number = true;
144 number = cmd->substr(1, pos -1); // Skip 'D' and ignore category, clir
145 } else { // Remove 'i' or 'I' or ';'
146 pos = cmd->find_last_of('i');
147 if (pos == std::string_view::npos) {
148 pos = cmd->find_last_of('I');
149 if (pos == std::string_view::npos) {
150 pos = cmd->find_last_of(';');
151 }
152 }
153 if (pos == std::string_view::npos) {
154 number = cmd->substr(1);
155 } else {
156 number = cmd->substr(1, pos -1);
157 }
158 }
159
160 // Check the number is valid digits or not
161 if (strspn(number.c_str(), "1234567890") != number.size()) {
162 client.SendCommandResponse(kCmeErrorInCorrectParameters);
163 return;
164 }
165
166 if (emergency_only && !emergency_number) {
167 client.SendCommandResponse(kCmeErrorNetworkNotAllowedEmergencyCallsOnly);
168 return;
169 }
170
171 // If the number is not emergency number, FDN enabled and the number is not in
172 // the fdn list, return kCmeErrorFixedDialNumberOnlyAllowed.
173 if (!emergency_number && sim_service_->IsFDNEnabled() &&
174 !sim_service_->IsFixedDialNumber(number)) {
175 client.SendCommandResponse(kCmeErrorFixedDialNumberOnlyAllowed);
176 return;
177 }
178
179 int port = 0;
180 if (number.length() == 11) {
181 port = std::stoi(number.substr(7));
182 } else if (number.length() == 4) {
183 port = std::stoi(number);
184 }
185
186 if (port >= kRemotePortRange.first &&
187 port <= kRemotePortRange.second) { // May be a remote call
188 std::stringstream ss;
189 ss << port;
190 auto remote_port = ss.str();
191 auto remote_client = ConnectToRemoteCvd(remote_port);
192 auto client_id = ClientId();
193 if (!remote_client->IsOpen()) {
194 client.SendCommandResponse(kCmeErrorNoNetworkService);
195 return;
196 }
197 auto local_host_port = GetHostId();
198 if (local_host_port == remote_port) {
199 client.SendCommandResponse(kCmeErrorOperationNotAllowed);
200 return;
201 }
202
203 if (channel_monitor_) {
204 client_id = channel_monitor_->SetRemoteClient(remote_client, false);
205 }
206
207 ss.clear();
208 ss.str("");
209 ss << "AT+REMOTECALL=4,0,0,\"" << local_host_port << "\",129";
210
211 SendCommandToRemote(client_id, "REM0");
212 SendCommandToRemote(client_id, ss.str());
213
214 CallStatus call_status(remote_port);
215 call_status.is_remote_call = true;
216 call_status.is_mobile_terminated = false;
217 call_status.call_state = CallStatus::CALL_STATE_DIALING;
218 call_status.remote_client = client_id;
219 auto index = FindFreeCallIndex();
220
221 auto call_token = std::make_pair(index, call_status.number);
222 call_status.timeout_serial = thread_looper_->Post(
223 makeSafeCallback<CallService>(this,
224 [call_token](CallService* me) {
225 me->TimerWaitingRemoteCallResponse(
226 call_token);
227 }),
228 std::chrono::minutes(1));
229
230 active_calls_[index] = call_status;
231 } else {
232 CallStatus call_status(number);
233 call_status.is_mobile_terminated = false;
234 call_status.call_state = CallStatus::CALL_STATE_DIALING;
235 auto index = FindFreeCallIndex();
236 active_calls_[index] = call_status;
237
238 if (emergency_number) {
239 in_emergency_mode_ = true;
240 SendUnsolicitedCommand("+WSOS: 1");
241 }
242 thread_looper_->Post(
243 makeSafeCallback(this, &CallService::SimulatePendingCallsAnswered),
244 std::chrono::seconds(1));
245 }
246
247 client.SendCommandResponse("OK");
248 std::this_thread::sleep_for(std::chrono::seconds(2));
249 }
250
SendCallStatusToRemote(CallStatus & call,CallStatus::CallState state)251 void CallService::SendCallStatusToRemote(CallStatus& call,
252 CallStatus::CallState state) {
253 if (call.is_remote_call && call.remote_client != std::nullopt) {
254 std::stringstream ss;
255 ss << "AT+REMOTECALL=" << state << "," << call.is_voice_mode << ","
256 << call.is_multi_party << ",\"" << GetHostId() << "\","
257 << call.is_international;
258
259 SendCommandToRemote(*(call.remote_client), ss.str());
260 if (state == CallStatus::CALL_STATE_HANGUP) {
261 CloseRemoteConnection(*(call.remote_client));
262 }
263 }
264 }
265
266 /* ATA */
HandleAcceptCall(const Client & client)267 void CallService::HandleAcceptCall(const Client& client) {
268 for (auto& iter : active_calls_) {
269 if (iter.second.isCallIncoming()) {
270 iter.second.SetCallActive();
271 SendCallStatusToRemote(iter.second, CallStatus::CALL_STATE_ACTIVE);
272 } else if (iter.second.isCallActive()) {
273 iter.second.SetCallBackground();
274 SendCallStatusToRemote(iter.second, CallStatus::CALL_STATE_HELD);
275 }
276 }
277
278 client.SendCommandResponse("OK");
279 }
280
281 /* ATH */
HandleRejectCall(const Client & client)282 void CallService::HandleRejectCall(const Client& client) {
283 for (auto iter = active_calls_.begin(); iter != active_calls_.end();) {
284 /* ATH: hangup, since user is busy */
285 if (iter->second.isCallIncoming()) {
286 SendCallStatusToRemote(iter->second, CallStatus::CALL_STATE_HANGUP);
287 iter = active_calls_.erase(iter);
288 } else {
289 ++iter;
290 }
291 }
292
293 client.SendCommandResponse("OK");
294 }
295
296 /**
297 * AT+CLCC
298 * Returns list of current calls of MT. If command succeeds but no
299 * calls are available, no information response is sent to TE.
300 *
301 * command Possible response(s)
302 * AT+CLCC [+CLCC: <ccid1>,<dir>,<stat>,<mode>,<mpty>
303 * [,<number>,<type>[,<alpha>[,<priority>
304 * [,<CLI validity>]]]][<CR><LF>
305 * +CLCC: <ccid2>,<dir>,<stat>,<mode>,<mpty>
306 * [,<number>,<type>[,<alpha>[,<priority>[,<CLI validity>]]]]
307 * +CME ERROR: <err>
308 *
309 * <ccidx>: integer type. This number can be used in +CHLD command
310 * operations. Value range is from 1 to N. N, the maximum number of
311 * simultaneous call control processes is implementation specific.
312 * <dir>: integer type
313 * 0 mobile originated (MO) call
314 1 mobile terminated (MT) call
315 * <stat>: integer type (state of the call)
316 * 0 active
317 * 1 held
318 * 2 dialing (MO call)
319 * 3 alerting (MO call)
320 * 4 incoming (MT call)
321 * 5 waiting (MT call)
322 * <mode>: integer type (bearer/teleservice)
323 * 0 voice
324 * 1 data
325 * 2 fax
326 * 3 voice followed by data, voice mode
327 * 4 alternating voice/data, voice mode
328 * 5 alternating voice/fax, voice mode
329 * 6 voice followed by data, data mode
330 * 7 alternating voice/data, data mode
331 * 8 alternating voice/fax, fax mode
332 * 9 unknown
333 * <mpty>: integer type
334 * 0 call is not one of multiparty (conference) call parties
335 * 1 call is one of multiparty (conference) call parties
336 * <number>: string type phone number in format specified by <type>.
337 * <type>: type of address octet in integer format
338 *
339 *see RIL_REQUEST_GET_CURRENT_CALLS in RIL
340 */
HandleCurrentCalls(const Client & client)341 void CallService::HandleCurrentCalls(const Client& client) {
342 std::vector<std::string> responses;
343 std::stringstream ss;
344
345 // AT+CLCC
346 // [+CLCC: <ccid1>,<dir>,<stat>,<mode>,<mpty>[,<number>,<type>[,<alpha>[,<priority>[,<CLI validity>]]]]
347 // [+CLCC: <ccid2>,<dir>,<stat>,<mode>,<mpty>[,<number>,<type>[,<alpha>[,<priority>[,<CLI validity>]]]]
348 // [...]]]
349 for (auto iter = active_calls_.begin(); iter != active_calls_.end(); ++iter) {
350 int index = iter->first;
351 int dir = iter->second.is_mobile_terminated;
352 CallStatus::CallState call_state = iter->second.call_state;
353 int mode = iter->second.is_voice_mode;
354 int mpty = iter->second.is_multi_party;
355 int type = iter->second.is_international ? 145 : 129;
356 std::string number = iter->second.number;
357
358 ss.clear();
359 ss << "+CLCC: " << index << "," << dir << "," << call_state << ","
360 << mode << "," << mpty << "," << number<< "," << type;
361 responses.push_back(ss.str());
362 ss.str("");
363 }
364
365 responses.push_back("OK");
366 client.SendCommandResponse(responses);
367 }
368
369 /**
370 * AT+CHLD
371 * This command allows the control of the following call related services:
372 * 1) a call can be temporarily disconnected from the MT but the connection
373 * is retained by the network;
374 * 2) multiparty conversation (conference calls);
375 * 3) the served subscriber who has two calls (one held and the other
376 * either active or alerting) can connect the other parties and release
377 * the served subscriber's own connection.
378 *
379 * Calls can be put on hold, recovered, released, added to conversation,
380 * and transferred similarly.
381 *
382 * command Possible response(s)
383 * +CHLD=<n> +CME ERROR: <err>
384 *
385 * +CHLD=? +CHLD: (list of supported <n>s)
386 * e.g. +CHLD: (0,1,1x,2,2x,3,4)
387 *
388 *
389 * see RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND
390 * RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND
391 * RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE
392 * RIL_REQUEST_CONFERENCE
393 * RIL_REQUEST_SEPARATE_CONNECTION
394 * RIL_REQUEST_HANGUP
395 * RIL_REQUEST_UDUB in RIL
396 */
HandleHangup(const Client & client,const std::string & command)397 void CallService::HandleHangup(const Client& client,
398 const std::string& command) {
399 std::vector<std::string> responses;
400 CommandParser cmd(command);
401 cmd.SkipPrefix();
402
403 std::string action(*cmd);
404 int n = std::stoi(action.substr(0, 1));
405 int index = -1;
406 if (cmd->length() > 1) {
407 index = std::stoi(action.substr(1));
408 }
409
410 switch (n) {
411 case 0: // Release all held calls or set User Determined User Busy(UDUB) for a waiting call
412 for (auto iter = active_calls_.begin(); iter != active_calls_.end();) {
413 if (iter->second.isCallIncoming() ||
414 iter->second.isCallBackground() ||
415 iter->second.isCallWaiting()) {
416 SendCallStatusToRemote(iter->second, CallStatus::CALL_STATE_HANGUP);
417 iter = active_calls_.erase(iter);
418 } else {
419 ++iter;
420 }
421 }
422 break;
423 case 1:
424 if (index == -1) { // Release all active calls and accepts the other(hold or waiting) call
425 for (auto iter = active_calls_.begin(); iter != active_calls_.end();) {
426 if (iter->second.isCallActive()) {
427 SendCallStatusToRemote(iter->second, CallStatus::CALL_STATE_HANGUP);
428 iter = active_calls_.erase(iter);
429 continue;
430 } else if (iter->second.isCallBackground() ||
431 iter->second.isCallWaiting()) {
432 iter->second.SetCallActive();
433 SendCallStatusToRemote(iter->second, CallStatus::CALL_STATE_ACTIVE);
434 }
435 ++iter;
436 }
437 } else { // Release a specific active call
438 auto iter = active_calls_.find(index);
439 if (iter != active_calls_.end()) {
440 SendCallStatusToRemote(iter->second, CallStatus::CALL_STATE_HANGUP);
441 active_calls_.erase(iter);
442 }
443 }
444 break;
445 case 2:
446 if (index == -1) { // Place all active calls and the waiting calls, activates all held calls
447 for (auto& iter : active_calls_) {
448 if (iter.second.isCallActive() || iter.second.isCallWaiting()) {
449 iter.second.SetCallBackground();
450 SendCallStatusToRemote(iter.second, CallStatus::CALL_STATE_HELD);
451 } else if (iter.second.isCallBackground()) {
452 iter.second.SetCallActive();
453 SendCallStatusToRemote(iter.second, CallStatus::CALL_STATE_ACTIVE);
454 }
455 }
456 } else { // Disconnect a call from the conversation
457 auto iter = active_calls_.find(index);
458 if (iter != active_calls_.end()) {
459 SendCallStatusToRemote(iter->second, CallStatus::CALL_STATE_HANGUP);
460 active_calls_.erase(iter);
461 }
462 }
463 break;
464 case 3: // Adds an held call to the conversation
465 for (auto iter = active_calls_.begin(); iter != active_calls_.end(); ++iter) {
466 if (iter->second.isCallBackground()) {
467 iter->second.SetCallActive();
468 SendCallStatusToRemote(iter->second, CallStatus::CALL_STATE_ACTIVE);
469 }
470 }
471 break;
472 case 4: // Connect the two calls
473 for (auto iter = active_calls_.begin(); iter != active_calls_.end(); ++iter) {
474 if (iter->second.isCallBackground()) {
475 iter->second.SetCallActive();
476 SendCallStatusToRemote(iter->second, CallStatus::CALL_STATE_ACTIVE);
477 }
478 }
479 break;
480 default:
481 client.SendCommandResponse(kCmeErrorOperationNotAllowed);
482 return;
483 }
484 client.SendCommandResponse("OK");
485 }
486
487 /**
488 * AT+CMUT
489 * This command is used to enable and disable the uplink voice muting
490 * during a voice call.
491 * Read command returns the current value of <n>.
492 *
493 * Command Possible response(s)
494 * +CMUT=[<n>] +CME ERROR: <err>
495 * +CMUT? +CMUT: <n>
496 * +CME ERROR: <err>
497 *
498 * <n>: integer type
499 * 0 mute off
500 * 1 mute on
501 *
502 * see RIL_REQUEST_SET_MUTE or RIL_REQUEST_GET_MUTE in RIL
503 */
HandleMute(const Client & client,const std::string & command)504 void CallService::HandleMute(const Client& client, const std::string& command) {
505 std::vector<std::string> responses;
506 std::stringstream ss;
507
508 CommandParser cmd(command);
509 cmd.SkipPrefix(); // If AT+CMUT?, it remains AT+CMUT?
510
511 if (cmd == "AT+CMUT?") {
512 ss << "+CMUT: " << mute_on_;
513 responses.push_back(ss.str());
514 } else { // AT+CMUT = <n>
515 int n = cmd.GetNextInt();
516 switch (n) {
517 case 0: // Mute off
518 mute_on_ = false;
519 break;
520 case 1: // Mute on
521 mute_on_ = true;
522 break;
523 default:
524 client.SendCommandResponse(kCmeErrorInCorrectParameters);
525 return;
526 }
527 }
528 responses.push_back("OK");
529 client.SendCommandResponse(responses);
530 }
531
532 /**
533 * AT+VTS
534 * This command transmits DTMF, after a successful call connection.
535 * Setting Command is used to send one or more ASCII characters which make
536 * MSC (Mobile Switching Center) send DTMF tone to remote User.
537 *
538 * Command Possible response(s)
539 * AT+VTS=<dtmf>[,<duration>] +CME ERROR: <err>
540 *
541 * <dtmf>
542 * A single ASCII character in the set { 0 -9, #, *, A – D}.
543 * <duration>
544 * Refer to duration value range of +VTD command
545 *
546 * see RIL_REQUEST_DTMF in RIL
547 */
HandleSendDtmf(const Client & client,const std::string &)548 void CallService::HandleSendDtmf(const Client& client,
549 const std::string& /*command*/) {
550 client.SendCommandResponse("OK");
551 }
552
HandleCancelUssd(const Client & client,const std::string &)553 void CallService::HandleCancelUssd(const Client& client,
554 const std::string& /*command*/) {
555 client.SendCommandResponse("OK");
556 }
557
558 /**
559 * AT+WSOS
560 *
561 * Command Possible response(s)
562 * +WSOS=[<n>] +CME ERROR: <err>
563 * +WSOS? +WSOS: <n>
564 * +CME ERROR: <err>
565 *
566 * <n>: integer type
567 * 0 enter emergency mode
568 * 1 exit emergency mode
569 *
570 * see RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE
571 * RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE
572 * RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE in RIL
573 */
HandleEmergencyMode(const Client & client,const std::string & command)574 void CallService::HandleEmergencyMode(const Client& client,
575 const std::string& command) {
576 std::vector<std::string> responses;
577 CommandParser cmd(command);
578 cmd.SkipPrefix();
579
580 if (cmd == "AT+WSOS?") {
581 std::stringstream ss;
582 ss << "+WSOS: " << in_emergency_mode_;
583 responses.push_back(ss.str());
584 } else {
585 int n = cmd.GetNextInt();
586 switch (n) {
587 case 0: // Exit
588 in_emergency_mode_ = false;
589 break;
590 case 1: // Enter
591 in_emergency_mode_ = true;
592 break;
593 default:
594 client.SendCommandResponse(kCmeErrorInCorrectParameters);
595 return;
596 }
597 auto nvram_config = NvramConfig::Get();
598 auto instance = nvram_config->ForInstance(service_id_);
599 instance.set_emergency_mode(in_emergency_mode_);
600 NvramConfig::SaveToFile();
601 }
602 client.SendCommandResponse("OK");
603 }
604
CallStateUpdate()605 void CallService::CallStateUpdate() {
606 SendUnsolicitedCommand("RING");
607 }
608
609 /**
610 * AT+REMOTECALL=<dir>,<stat>,<mode>,<mpty>,<number>,<num_type>
611 * This command allows to dial a remote voice call with another cuttlefish
612 * emulator. If request is successful, the remote emulator can simulate hold on,
613 * hang up, reject and so on.
614 *
615 * e.g. AT+REMOTECALL=4,0,0,6521,129
616 *
617 * <stat>: integer type (state of the call)
618 * 0 active
619 * 1 held
620 * 2 dialing (MO call)
621 * 3 alerting (MO call)
622 * 4 incoming (MT call)
623 * 5 waiting (MT call)
624 * <mode>: integer type
625 * 0 voice
626 * 1 data
627 * 2 fax
628 * 3 voice followed by data, voice mode
629 * 4 alternating voice/data, voice mode
630 * 5 alternating voice/fax, voice mode
631 * 6 voice followed by data, data mode
632 * 7 alternating voice/data, data mode
633 * 8 alternating voice/fax, fax mode
634 * 9 unknown
635 * <mpty>: integer type
636 * 0 call is not one of multiparty (conference) call parties
637 * 1 call is one of multiparty (conference) call parties
638 * <number>: string here maybe remote port
639 * <num_type>: type of address octet in integer format
640 *
641 * Note: reason should be added to indicate why hang up. Since not realizing
642 * RIL_LAST_CALL_FAIL_CAUSE, delay to be implemented.
643 */
HandleRemoteCall(const Client & client,const std::string & command)644 void CallService::HandleRemoteCall(const Client& client,
645 const std::string& command) {
646 CommandParser cmd(command);
647 cmd.SkipPrefix();
648
649 int state = cmd.GetNextInt();
650 int mode = cmd.GetNextInt();
651 int mpty = cmd.GetNextInt();
652 auto number = cmd.GetNextStr();
653 int num_type = cmd.GetNextInt();
654
655 // According to the number to determine whether it is a existing call
656 auto iter = active_calls_.begin();
657 for (; iter != active_calls_.end(); ++iter) {
658 if (iter->second.number == number) {
659 break;
660 }
661 }
662
663 switch (state) {
664 case CallStatus::CALL_STATE_ACTIVE: {
665 if (iter != active_calls_.end()) {
666 iter->second.SetCallActive();
667 if (iter->second.timeout_serial != std::nullopt) {
668 thread_looper_->CancelSerial(*(iter->second.timeout_serial));
669 }
670 }
671 break;
672 }
673 case CallStatus::CALL_STATE_HELD:
674 if (iter != active_calls_.end()) {
675 iter->second.SetCallBackground();
676 if (iter->second.timeout_serial != std::nullopt) {
677 thread_looper_->CancelSerial(*(iter->second.timeout_serial));
678 }
679 }
680 break;
681 case CallStatus::CALL_STATE_HANGUP:
682 if (iter != active_calls_.end()) {
683 auto client = iter->second.remote_client;
684 if (client != std::nullopt) {
685 CloseRemoteConnection(*client);
686 }
687 if (iter->second.timeout_serial != std::nullopt) {
688 thread_looper_->CancelSerial(*(iter->second.timeout_serial));
689 }
690 active_calls_.erase(iter);
691 }
692 break;
693 case CallStatus::CALL_STATE_INCOMING: {
694 if (network_service_) {
695 if (network_service_->isRadioOff()) {
696 LOG(DEBUG) << " radio is off, reject incoming call from: " << number;
697 network_service_->CloseRemoteConnection(client.Id());
698 return;
699 }
700 }
701 CallStatus call_status(number);
702 call_status.is_remote_call = true;
703 call_status.is_voice_mode = mode;
704 call_status.is_multi_party = mpty;
705 call_status.is_mobile_terminated = true;
706 call_status.is_international = (num_type == 145);
707 call_status.remote_client = client.Id();
708 call_status.call_state = CallStatus::CALL_STATE_INCOMING;
709
710 auto index = FindFreeCallIndex();
711 active_calls_[index] = call_status;
712 break;
713 }
714 default: // Unsupported call state
715 return;
716 }
717 thread_looper_->Post(makeSafeCallback(this, &CallService::CallStateUpdate));
718 }
719
720 } // namespace cuttlefish
721