1 /*
2  * Copyright 2023 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 <iostream>
18 #include <memory>
19 #include <string>
20 
21 #include <android-base/hex.h>
22 #include <gflags/gflags.h>
23 #include <grpcpp/ext/proto_server_reflection_plugin.h>
24 #include <grpcpp/grpcpp.h>
25 #include <grpcpp/health_check_service_interface.h>
26 
27 #include "casimir_control.grpc.pb.h"
28 
29 #include "common/libs/utils/result.h"
30 #include "host/commands/casimir_control_server/casimir_controller.h"
31 #include "host/commands/casimir_control_server/hex.h"
32 
33 using casimircontrolserver::CasimirControlService;
34 using casimircontrolserver::PowerLevel;
35 using casimircontrolserver::RadioState;
36 using casimircontrolserver::SendApduReply;
37 using casimircontrolserver::SendApduRequest;
38 using casimircontrolserver::SenderId;
39 using casimircontrolserver::Void;
40 
41 using cuttlefish::CasimirController;
42 
43 using google::protobuf::Empty;
44 using grpc::Server;
45 using grpc::ServerBuilder;
46 using grpc::ServerContext;
47 using grpc::Status;
48 using grpc::StatusCode;
49 using std::string;
50 using std::vector;
51 
52 DEFINE_string(grpc_uds_path, "", "grpc_uds_path");
53 DEFINE_int32(casimir_rf_port, -1, "RF port to control Casimir");
54 DEFINE_string(casimir_rf_path, "", "RF unix server path to control Casimir");
55 
56 namespace cuttlefish {
57 namespace {
58 
ConnectToCasimir()59 Result<CasimirController> ConnectToCasimir() {
60   if (FLAGS_casimir_rf_port >= 0) {
61     return CF_EXPECT(
62         CasimirController::ConnectToTcpPort(FLAGS_casimir_rf_port));
63   } else if (!FLAGS_casimir_rf_path.empty()) {
64     return CF_EXPECT(
65         CasimirController::ConnectToUnixSocket(FLAGS_casimir_rf_path));
66   } else {
67     return CF_ERR("`--casimir_rf_port` or `--casimir_rf_path` must be set");
68   }
69 }
70 
ResultToStatus(Result<void> res)71 Status ResultToStatus(Result<void> res) {
72   if (res.ok()) {
73     return Status::OK;
74   } else {
75     LOG(ERROR) << "RPC failed: " << res.error().FormatForEnv();
76     return Status(StatusCode::INTERNAL,
77                   res.error().FormatForEnv(/* color = */ false));
78   }
79 }
80 
81 class CasimirControlServiceImpl final : public CasimirControlService::Service {
82  private:
SetPowerLevel(ServerContext * context,const PowerLevel * power_level,Void *)83   Status SetPowerLevel(ServerContext* context, const PowerLevel* power_level,
84                        Void*) override {
85     return ResultToStatus(SetPowerLevelResult(power_level));
86   }
87 
SetPowerLevelResult(const PowerLevel * power_level)88   Result<void> SetPowerLevelResult(const PowerLevel* power_level) {
89     if (!device_) {
90       return {};
91     }
92     CF_EXPECT(device_->SetPowerLevel(power_level->power_level()),
93               "Failed to set power level");
94     return {};
95   }
96 
Close(ServerContext * context,const Void *,Void * senderId)97   Status Close(ServerContext* context, const Void*, Void* senderId) override {
98     device_ = std::nullopt;
99     return Status::OK;
100   }
101 
Init(ServerContext *,const Void *,Void *)102   Status Init(ServerContext*, const Void*, Void*) override {
103     return ResultToStatus(Init());
104   }
105 
Init()106   Result<void> Init() {
107     if (device_.has_value()) {
108       return {};
109     }
110     // Step 1: Initialize connection with casimir
111     device_ = CF_EXPECT(ConnectToCasimir());
112     return {};
113   }
114 
Mute()115   Result<void> Mute() {
116     if (!device_.has_value()) {
117       return {};
118     }
119 
120     if (is_radio_on_) {
121       CF_EXPECT(device_->Mute(), "Failed to mute radio");
122       is_radio_on_ = false;
123     }
124     return {};
125   }
126 
Unmute()127   Result<void> Unmute() {
128     if (!is_radio_on_) {
129       CF_EXPECT(device_->Unmute(), "Failed to unmute radio");
130       is_radio_on_ = true;
131     }
132     return {};
133   }
134 
SetRadioState(ServerContext * context,const RadioState * radio_state,Void *)135   Status SetRadioState(ServerContext* context, const RadioState* radio_state,
136                        Void*) override {
137     return ResultToStatus(SetRadioStateResult(radio_state));
138   }
139 
SetRadioStateResult(const RadioState * radio_state)140   Result<void> SetRadioStateResult(const RadioState* radio_state) {
141     if (radio_state->radio_on()) {
142       CF_EXPECT(Init());
143       CF_EXPECT(Unmute());
144       return {};
145     } else {
146       if (!device_.has_value()) {
147         return {};
148       }
149       CF_EXPECT(Mute());
150       return {};
151     }
152   }
153 
PollAResult(SenderId * sender_id)154   Result<void> PollAResult(SenderId* sender_id) {
155     // Step 1: Initialize connection with casimir
156     if (!device_.has_value()) {
157       device_ = CF_EXPECT(ConnectToCasimir(), "Failed to connect with casimir");
158       CF_EXPECT(Unmute(), "failed to unmute the device");
159     }
160     // Step 2: Poll
161     /* Casimir control server seems to be dropping integer values of zero.
162       This works around that issue by translating the 0-based sender IDs to
163       be 1-based.*/
164     sender_id->set_sender_id(
165 
166         CF_EXPECT(device_->Poll(),
167                   "Failed to poll and select NFC-A and ISO-DEP") +
168         1);
169     return {};
170   }
171 
PollA(ServerContext *,const Void *,SenderId * sender_id)172   Status PollA(ServerContext*, const Void*, SenderId* sender_id) override {
173     return ResultToStatus(PollAResult(sender_id));
174   }
175 
SendApduResult(const SendApduRequest * request,SendApduReply * response)176   Result<void> SendApduResult(const SendApduRequest* request,
177                               SendApduReply* response) {
178     // Step 0: Parse input
179     std::vector<std::vector<uint8_t>> apdu_bytes;
180     for (const std::string& apdu_hex_string : request->apdu_hex_strings()) {
181       apdu_bytes.emplace_back(
182           CF_EXPECT(HexToBytes(apdu_hex_string),
183                     "Failed to parse input. Must only contain [0-9a-fA-F]"));
184     }
185     // Step 1: Initialize connection with casimir
186     CF_EXPECT(Init());
187 
188     int16_t id;
189     if (request->has_sender_id()) {
190       /* Casimir control server seems to be dropping integer values of zero.
191         This works around that issue by translating the 0-based sender IDs to
192         be 1-based.*/
193       id = request->sender_id() - 1;
194     } else {
195       // Step 2: Poll
196       SenderId sender_id;
197       CF_EXPECT(PollAResult(&sender_id));
198       id = sender_id.sender_id();
199     }
200 
201     // Step 3: Send APDU bytes
202     response->clear_response_hex_strings();
203     for (int i = 0; i < apdu_bytes.size(); i++) {
204       std::vector<uint8_t> bytes =
205           CF_EXPECT(device_->SendApdu(id, std::move(apdu_bytes[i])),
206                     "Failed to send APDU bytes");
207       std::string resp = android::base::HexString(
208           reinterpret_cast<void*>(bytes.data()), bytes.size());
209       response->add_response_hex_strings(std::move(resp));
210     }
211 
212     // Returns OK although returned bytes is valids if ends with [0x90, 0x00].
213     return {};
214   }
215 
SendApdu(ServerContext *,const SendApduRequest * request,SendApduReply * response)216   Status SendApdu(ServerContext*, const SendApduRequest* request,
217                   SendApduReply* response) override {
218     return ResultToStatus(SendApduResult(request, response));
219   }
220 
221   std::optional<CasimirController> device_;
222   bool is_radio_on_ = false;
223 };
224 
RunServer(int argc,char ** argv)225 void RunServer(int argc, char** argv) {
226   ::gflags::ParseCommandLineFlags(&argc, &argv, true);
227   std::string server_address("unix:" + FLAGS_grpc_uds_path);
228   CasimirControlServiceImpl service;
229 
230   grpc::EnableDefaultHealthCheckService(true);
231   grpc::reflection::InitProtoReflectionServerBuilderPlugin();
232   ServerBuilder builder;
233   // Listen on the given address without any authentication mechanism.
234   builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
235   // Register "service" as the instance through which we'll communicate with
236   // clients. In this case it corresponds to an *synchronous* service.
237   builder.RegisterService(&service);
238   // Finally assemble the server.
239   std::unique_ptr<Server> server(builder.BuildAndStart());
240   std::cout << "Server listening on " << server_address << std::endl;
241 
242   // Wait for the server to shutdown. Note that some other thread must be
243   // responsible for shutting down the server for this call to ever return.
244   server->Wait();
245 }
246 
247 }  // namespace
248 }  // namespace cuttlefish
249 
main(int argc,char ** argv)250 int main(int argc, char** argv) {
251   cuttlefish::RunServer(argc, argv);
252 
253   return 0;
254 }
255