1 
2 //
3 // Copyright (C) 2020 The Android Open Source Project
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //      http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 
17 #include "host/commands/modem_simulator/channel_monitor.h"
18 
19 #include <android-base/strings.h>
20 
21 #include <algorithm>
22 
23 #include "host/commands/modem_simulator/modem_simulator.h"
24 
25 namespace cuttlefish {
26 
27 constexpr int32_t kMaxCommandLength = 4096;
28 
Client(cuttlefish::SharedFD fd)29 Client::Client(cuttlefish::SharedFD fd) : client_fd(fd) {}
30 
Client(cuttlefish::SharedFD fd,ClientType client_type)31 Client::Client(cuttlefish::SharedFD fd, ClientType client_type)
32     : type(client_type), client_fd(fd) {}
33 
operator ==(const Client & other) const34 bool Client::operator==(const Client& other) const {
35   return client_fd == other.client_fd;
36 }
37 
SendCommandResponse(std::string response) const38 void Client::SendCommandResponse(std::string response) const {
39   if (response.empty()) {
40     LOG(DEBUG) << "Invalid response, ignore!";
41     return;
42   }
43 
44   if (response.back() != '\r') {
45     response += '\r';
46   }
47   LOG(DEBUG) << " AT< " << response;
48 
49   std::lock_guard<std::mutex> autolock(const_cast<Client*>(this)->write_mutex);
50   client_fd->Write(response.data(), response.size());
51 }
52 
SendCommandResponse(const std::vector<std::string> & responses) const53 void Client::SendCommandResponse(
54     const std::vector<std::string>& responses) const {
55   for (auto& response : responses) {
56     SendCommandResponse(response);
57   }
58 }
59 
ChannelMonitor(ModemSimulator * modem,cuttlefish::SharedFD server)60 ChannelMonitor::ChannelMonitor(ModemSimulator* modem,
61                                cuttlefish::SharedFD server)
62     : modem_(modem), server_(server) {
63   if (!cuttlefish::SharedFD::Pipe(&read_pipe_, &write_pipe_)) {
64     LOG(ERROR) << "Unable to create pipe, ignore";
65   }
66 
67   if (server_->IsOpen())
68     monitor_thread_ = std::thread([this]() { MonitorLoop(); });
69 }
70 
SetRemoteClient(cuttlefish::SharedFD client,bool is_accepted)71 void ChannelMonitor::SetRemoteClient(cuttlefish::SharedFD client, bool is_accepted) {
72   auto remote_client = std::make_unique<Client>(client, Client::REMOTE);
73 
74   if (is_accepted) {
75     // There may be new data from remote client before select.
76     remote_client->first_read_command_ = true;
77     ReadCommand(*remote_client);
78   }
79 
80   remote_client->first_read_command_ = false;
81   remote_clients_.push_back(std::move(remote_client));
82   LOG(DEBUG) << "added one remote client";
83 
84   // Trigger monitor loop
85   if (write_pipe_->IsOpen()) {
86     write_pipe_->Write("OK", sizeof("OK"));
87   } else {
88     LOG(ERROR) << "Pipe created fail, can't trigger monitor loop";
89   }
90 }
91 
AcceptIncomingConnection()92 void ChannelMonitor::AcceptIncomingConnection() {
93   auto client_fd  = cuttlefish::SharedFD::Accept(*server_);
94   if (!client_fd->IsOpen()) {
95     LOG(ERROR) << "Error accepting connection on socket: " << client_fd->StrError();
96   } else {
97     auto client = std::make_unique<Client>(client_fd);
98     LOG(DEBUG) << "added one RIL client";
99     clients_.push_back(std::move(client));
100     if (clients_.size() == 1) {
101       // The first connected client default to be the unsolicited commands channel
102       modem_->OnFirstClientConnected();
103     }
104   }
105 }
106 
ReadCommand(Client & client)107 void ChannelMonitor::ReadCommand(Client& client) {
108   std::vector<char> buffer(kMaxCommandLength);
109   auto bytes_read = client.client_fd->Read(buffer.data(), buffer.size());
110   if (bytes_read <= 0) {
111     if (errno == EAGAIN && client.type == Client::REMOTE &&
112         client.first_read_command_) {
113       LOG(ERROR) << "After read 'REM' from remote client, and before select "
114           "no new data come.";
115       return;
116     }
117     LOG(ERROR) << "Error reading from client fd: " << client.client_fd->StrError();
118     client.client_fd->Close();  // Ignore errors here
119     // Erase client from the vector clients
120     auto& clients = client.type == Client::REMOTE ? remote_clients_ : clients_;
121     auto iter = std::find_if(
122         clients.begin(), clients.end(),
123         [&](std::unique_ptr<Client>& other) { return *other == client; });
124     if (iter != clients.end()) {
125       clients.erase(iter);
126     }
127     return;
128   }
129 
130   std::string& incomplete_command = client.incomplete_command;
131 
132   // Add the incomplete command from the last read
133   auto commands = std::string{incomplete_command.data()};
134   commands.append(buffer.data());
135 
136   incomplete_command.clear();
137 
138   // Replacing '\n' with '\r'
139   commands = android::base::StringReplace(commands, "\n", "\r", true);
140 
141   // Split into commands and dispatch
142   size_t pos = 0, r_pos = 0;  // '\r' or '\n'
143   while (r_pos != std::string::npos) {
144     if (modem_->IsWaitingSmsPdu()) {
145       r_pos = commands.find('\032', pos);  // In sms, find ctrl-z
146     } else {
147       r_pos = commands.find('\r', pos);
148     }
149     if (r_pos != std::string::npos) {
150       auto command = commands.substr(pos, r_pos - pos);
151       if (command.size() > 0) {  // "\r\r" ?
152         LOG(DEBUG) << "AT> " << command;
153         modem_->DispatchCommand(client, command);
154       }
155       pos = r_pos + 1;  // Skip '\r'
156     } else if (pos < commands.length()) {  // Incomplete command
157       incomplete_command = commands.substr(pos);
158       LOG(DEBUG) << "incomplete command: " << incomplete_command;
159     }
160   }
161 }
162 
SendUnsolicitedCommand(std::string & response)163 void ChannelMonitor::SendUnsolicitedCommand(std::string& response) {
164   // The first accepted client default to be unsolicited command channel?
165   auto iter = clients_.begin();
166   if (iter != clients_.end()) {
167     iter->get()->SendCommandResponse(response);
168   } else {
169     LOG(DEBUG) << "No client connected yet.";
170   }
171 }
172 
SendRemoteCommand(cuttlefish::SharedFD client,std::string & response)173 void ChannelMonitor::SendRemoteCommand(cuttlefish::SharedFD client, std::string& response) {
174   auto iter = remote_clients_.begin();
175   for (; iter != remote_clients_.end(); ++iter) {
176     if (iter->get()->client_fd == client) {
177       iter->get()->SendCommandResponse(response);
178       return;
179     }
180   }
181   LOG(DEBUG) << "Remote client has closed.";
182 }
183 
CloseRemoteConnection(cuttlefish::SharedFD client)184 void ChannelMonitor::CloseRemoteConnection(cuttlefish::SharedFD client) {
185   auto iter = remote_clients_.begin();
186   for (; iter != remote_clients_.end(); ++iter) {
187     if (iter->get()->client_fd == client) {
188       iter->get()->client_fd->Close();
189       iter->get()->is_valid = false;
190 
191       // Trigger monitor loop
192       if (write_pipe_->IsOpen()) {
193         write_pipe_->Write("OK", sizeof("OK"));
194         LOG(DEBUG) << "asking to remove clients";
195       } else {
196         LOG(ERROR) << "Pipe created fail, can't trigger monitor loop";
197       }
198       return;
199     }
200   }
201   LOG(DEBUG) << "Remote client has been erased.";
202 }
203 
~ChannelMonitor()204 ChannelMonitor::~ChannelMonitor() {
205   if (write_pipe_->IsOpen()) {
206     write_pipe_->Write("KO", sizeof("KO"));
207   }
208 
209   if (monitor_thread_.joinable()) {
210     LOG(DEBUG) << "waiting for monitor thread to join";
211     monitor_thread_.join();
212   }
213 }
214 
removeInvalidClients(std::vector<std::unique_ptr<Client>> & clients)215 static void removeInvalidClients(std::vector<std::unique_ptr<Client>>& clients) {
216   auto iter = clients.begin();
217   for (; iter != clients.end();) {
218     if (iter->get()->is_valid) {
219       ++iter;
220     } else {
221       LOG(DEBUG) << "removed 1 client";
222       iter = clients.erase(iter);
223     }
224   }
225 }
226 
MonitorLoop()227 void ChannelMonitor::MonitorLoop() {
228   do {
229     cuttlefish::SharedFDSet read_set;
230     read_set.Set(server_);
231     read_set.Set(read_pipe_);
232     for (auto& client: clients_) {
233       if (client->is_valid) read_set.Set(client->client_fd);
234     }
235     for (auto& client: remote_clients_) {
236       if (client->is_valid) read_set.Set(client->client_fd);
237     }
238     int num_fds = cuttlefish::Select(&read_set, nullptr, nullptr, nullptr);
239     if (num_fds < 0) {
240       LOG(ERROR) << "Select call returned error : " << strerror(errno);
241       // std::exit(kSelectError);
242       break;
243     } else if (num_fds > 0) {
244       if (read_set.IsSet(server_)) {
245         AcceptIncomingConnection();
246       }
247       if (read_set.IsSet(read_pipe_)) {
248         std::string buf(2, ' ');
249         read_pipe_->Read(buf.data(), buf.size());  // Empty pipe
250         if (buf == std::string("KO")) {
251           LOG(DEBUG) << "requested to exit now";
252           break;
253         }
254         // clean the lists
255         removeInvalidClients(clients_);
256         removeInvalidClients(remote_clients_);
257       }
258       for (auto& client : clients_) {
259         if (read_set.IsSet(client->client_fd)) {
260           ReadCommand(*client);
261         }
262       }
263       for (auto& client : remote_clients_) {
264         if (read_set.IsSet(client->client_fd)) {
265           ReadCommand(*client);
266         }
267       }
268     } else {
269       // Ignore errors here
270       LOG(ERROR) << "Select call returned error : " << strerror(errno);
271     }
272   } while (true);
273 }
274 
275 }  // namespace cuttlefish
276