1 // Copyright 2022 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "frontend/frontend_server.h"
16
17 #include <google/protobuf/util/json_util.h>
18
19 #include <iostream>
20 #include <memory>
21 #include <string>
22 #include <utility>
23
24 #include "controller/scene_controller.h"
25 #include "frontend.grpc.pb.h"
26 #include "frontend.pb.h"
27 #include "google/protobuf/empty.pb.h"
28 #include "grpcpp/server_context.h"
29 #include "grpcpp/support/status.h"
30 #include "netsim-cxx/src/lib.rs.h"
31
32 namespace netsim {
33 namespace {
34
35 /// The C++ implementation of the CxxServerResponseWriter interface. This is
36 /// used by the gRPC server to invoke the Rust pcap handler and process a
37 /// responses.
38 class CxxServerResponseWritable : public frontend::CxxServerResponseWriter {
39 public:
CxxServerResponseWritable()40 CxxServerResponseWritable()
41 : grpc_writer_(nullptr), err(""), is_ok(false), body(""), length(0){};
CxxServerResponseWritable(grpc::ServerWriter<netsim::frontend::GetCaptureResponse> * grpc_writer)42 CxxServerResponseWritable(
43 grpc::ServerWriter<netsim::frontend::GetCaptureResponse> *grpc_writer)
44 : grpc_writer_(grpc_writer), err(""), is_ok(false), body(""), length(0){};
45
put_error(unsigned int error_code,const std::string & response) const46 void put_error(unsigned int error_code,
47 const std::string &response) const override {
48 err = std::to_string(error_code) + ": " + response;
49 is_ok = false;
50 }
51
put_ok_with_length(const std::string & mime_type,std::size_t length) const52 void put_ok_with_length(const std::string &mime_type,
53 std::size_t length) const override {
54 this->length = length;
55 is_ok = true;
56 }
57
put_chunk(rust::Slice<const uint8_t> chunk) const58 void put_chunk(rust::Slice<const uint8_t> chunk) const override {
59 netsim::frontend::GetCaptureResponse response;
60 response.set_capture_stream(std::string(chunk.begin(), chunk.end()));
61 is_ok = grpc_writer_->Write(response);
62 }
63
put_ok(const std::string & mime_type,const std::string & body) const64 void put_ok(const std::string &mime_type,
65 const std::string &body) const override {
66 this->body = body;
67 is_ok = true;
68 }
69
70 mutable grpc::ServerWriter<netsim::frontend::GetCaptureResponse>
71 *grpc_writer_;
72 mutable std::string err;
73 mutable bool is_ok;
74 mutable std::string body;
75 mutable std::size_t length;
76 };
77
78 class FrontendServer final : public frontend::FrontendService::Service {
79 public:
GetVersion(grpc::ServerContext * context,const google::protobuf::Empty * empty,frontend::VersionResponse * reply)80 grpc::Status GetVersion(grpc::ServerContext *context,
81 const google::protobuf::Empty *empty,
82 frontend::VersionResponse *reply) {
83 reply->set_version(std::string(netsim::GetVersion()));
84 return grpc::Status::OK;
85 }
86
GetDevices(grpc::ServerContext * context,const google::protobuf::Empty * empty,frontend::GetDevicesResponse * reply)87 grpc::Status GetDevices(grpc::ServerContext *context,
88 const google::protobuf::Empty *empty,
89 frontend::GetDevicesResponse *reply) {
90 const auto scene = netsim::controller::SceneController::Singleton().Get();
91 for (const auto &device : scene.devices())
92 reply->add_devices()->CopyFrom(device);
93 return grpc::Status::OK;
94 }
95
PatchDevice(grpc::ServerContext * context,const frontend::PatchDeviceRequest * request,google::protobuf::Empty * response)96 grpc::Status PatchDevice(grpc::ServerContext *context,
97 const frontend::PatchDeviceRequest *request,
98 google::protobuf::Empty *response) {
99 auto status = netsim::controller::SceneController::Singleton().PatchDevice(
100 request->device());
101 if (!status)
102 return grpc::Status(grpc::StatusCode::NOT_FOUND,
103 "device " + request->device().name() + " not found.");
104 return grpc::Status::OK;
105 }
106
SetPacketCapture(grpc::ServerContext * context,const frontend::SetPacketCaptureRequest * request,google::protobuf::Empty * empty)107 grpc::Status SetPacketCapture(
108 grpc::ServerContext *context,
109 const frontend::SetPacketCaptureRequest *request,
110 google::protobuf::Empty *empty) {
111 model::Device device;
112 model::Chip chip;
113 // Turn on bt packet capture
114 chip.set_capture(request->capture() ? model::State::ON : model::State::OFF);
115 chip.mutable_bt();
116 device.mutable_chips()->Add()->CopyFrom(chip);
117 controller::SceneController::Singleton().PatchDevice(device);
118 return grpc::Status::OK;
119 }
120
Reset(grpc::ServerContext * context,const google::protobuf::Empty * request,google::protobuf::Empty * empty)121 grpc::Status Reset(grpc::ServerContext *context,
122 const google::protobuf::Empty *request,
123 google::protobuf::Empty *empty) {
124 netsim::controller::SceneController::Singleton().Reset();
125 return grpc::Status::OK;
126 }
127
ListCapture(grpc::ServerContext * context,const google::protobuf::Empty * empty,frontend::ListCaptureResponse * reply)128 grpc::Status ListCapture(grpc::ServerContext *context,
129 const google::protobuf::Empty *empty,
130 frontend::ListCaptureResponse *reply) {
131 CxxServerResponseWritable writer;
132 HandleCaptureCxx(writer, "GET", "", "");
133 if (writer.is_ok) {
134 google::protobuf::util::JsonStringToMessage(writer.body, reply);
135 return grpc::Status::OK;
136 }
137 return grpc::Status(grpc::StatusCode::UNKNOWN, writer.err);
138 }
139
PatchCapture(grpc::ServerContext * context,const frontend::PatchCaptureRequest * request,google::protobuf::Empty * response)140 grpc::Status PatchCapture(grpc::ServerContext *context,
141 const frontend::PatchCaptureRequest *request,
142 google::protobuf::Empty *response) {
143 CxxServerResponseWritable writer;
144 HandleCaptureCxx(writer, "PATCH", std::to_string(request->id()),
145 std::to_string(request->patch().state()));
146 if (writer.is_ok) {
147 return grpc::Status::OK;
148 }
149 return grpc::Status(grpc::StatusCode::UNKNOWN, writer.err);
150 }
GetCapture(grpc::ServerContext * context,const netsim::frontend::GetCaptureRequest * request,grpc::ServerWriter<netsim::frontend::GetCaptureResponse> * grpc_writer)151 grpc::Status GetCapture(
152 grpc::ServerContext *context,
153 const netsim::frontend::GetCaptureRequest *request,
154 grpc::ServerWriter<netsim::frontend::GetCaptureResponse> *grpc_writer) {
155 CxxServerResponseWritable writer(grpc_writer);
156 HandleCaptureCxx(writer, "GET", std::to_string(request->id()), "");
157 if (writer.is_ok) {
158 return grpc::Status::OK;
159 }
160 return grpc::Status(grpc::StatusCode::UNKNOWN, writer.err);
161 }
162 };
163 } // namespace
164
GetFrontendService()165 std::unique_ptr<frontend::FrontendService::Service> GetFrontendService() {
166 return std::make_unique<FrontendServer>();
167 }
168
169 } // namespace netsim
170