1*cf78ab8cSAndroid Build Coastguard Worker // Copyright 2022 The Android Open Source Project
2*cf78ab8cSAndroid Build Coastguard Worker //
3*cf78ab8cSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*cf78ab8cSAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*cf78ab8cSAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*cf78ab8cSAndroid Build Coastguard Worker //
7*cf78ab8cSAndroid Build Coastguard Worker // http://www.apache.org/licenses/LICENSE-2.0
8*cf78ab8cSAndroid Build Coastguard Worker //
9*cf78ab8cSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*cf78ab8cSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*cf78ab8cSAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*cf78ab8cSAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*cf78ab8cSAndroid Build Coastguard Worker // limitations under the License.
14*cf78ab8cSAndroid Build Coastguard Worker
15*cf78ab8cSAndroid Build Coastguard Worker // Frontend client
16*cf78ab8cSAndroid Build Coastguard Worker #include "frontend/frontend_client.h"
17*cf78ab8cSAndroid Build Coastguard Worker
18*cf78ab8cSAndroid Build Coastguard Worker #include <google/protobuf/util/json_util.h>
19*cf78ab8cSAndroid Build Coastguard Worker #include <grpcpp/support/status.h>
20*cf78ab8cSAndroid Build Coastguard Worker
21*cf78ab8cSAndroid Build Coastguard Worker #include <chrono>
22*cf78ab8cSAndroid Build Coastguard Worker #include <cstdint>
23*cf78ab8cSAndroid Build Coastguard Worker #include <memory>
24*cf78ab8cSAndroid Build Coastguard Worker #include <string>
25*cf78ab8cSAndroid Build Coastguard Worker
26*cf78ab8cSAndroid Build Coastguard Worker #include "google/protobuf/empty.pb.h"
27*cf78ab8cSAndroid Build Coastguard Worker #include "grpcpp/create_channel.h"
28*cf78ab8cSAndroid Build Coastguard Worker #include "grpcpp/security/credentials.h"
29*cf78ab8cSAndroid Build Coastguard Worker #include "grpcpp/support/status_code_enum.h"
30*cf78ab8cSAndroid Build Coastguard Worker #include "netsim-cli/src/ffi.rs.h"
31*cf78ab8cSAndroid Build Coastguard Worker #include "netsim/frontend.grpc.pb.h"
32*cf78ab8cSAndroid Build Coastguard Worker #include "netsim/frontend.pb.h"
33*cf78ab8cSAndroid Build Coastguard Worker #include "netsim/model.pb.h"
34*cf78ab8cSAndroid Build Coastguard Worker #include "util/log.h"
35*cf78ab8cSAndroid Build Coastguard Worker #include "util/os_utils.h"
36*cf78ab8cSAndroid Build Coastguard Worker
37*cf78ab8cSAndroid Build Coastguard Worker namespace netsim {
38*cf78ab8cSAndroid Build Coastguard Worker namespace frontend {
39*cf78ab8cSAndroid Build Coastguard Worker namespace {
40*cf78ab8cSAndroid Build Coastguard Worker const std::chrono::duration kConnectionDeadline = std::chrono::seconds(1);
41*cf78ab8cSAndroid Build Coastguard Worker
NewFrontendStub(std::string server)42*cf78ab8cSAndroid Build Coastguard Worker std::unique_ptr<frontend::FrontendService::Stub> NewFrontendStub(
43*cf78ab8cSAndroid Build Coastguard Worker std::string server) {
44*cf78ab8cSAndroid Build Coastguard Worker if (server == "") {
45*cf78ab8cSAndroid Build Coastguard Worker return {};
46*cf78ab8cSAndroid Build Coastguard Worker }
47*cf78ab8cSAndroid Build Coastguard Worker std::shared_ptr<grpc::Channel> channel =
48*cf78ab8cSAndroid Build Coastguard Worker grpc::CreateChannel(server, grpc::InsecureChannelCredentials());
49*cf78ab8cSAndroid Build Coastguard Worker
50*cf78ab8cSAndroid Build Coastguard Worker auto deadline = std::chrono::system_clock::now() + kConnectionDeadline;
51*cf78ab8cSAndroid Build Coastguard Worker if (!channel->WaitForConnected(deadline)) {
52*cf78ab8cSAndroid Build Coastguard Worker BtsLogWarn("Frontend gRPC channel not connected");
53*cf78ab8cSAndroid Build Coastguard Worker return nullptr;
54*cf78ab8cSAndroid Build Coastguard Worker }
55*cf78ab8cSAndroid Build Coastguard Worker
56*cf78ab8cSAndroid Build Coastguard Worker return frontend::FrontendService::NewStub(channel);
57*cf78ab8cSAndroid Build Coastguard Worker }
58*cf78ab8cSAndroid Build Coastguard Worker
59*cf78ab8cSAndroid Build Coastguard Worker // A synchronous client for the netsim frontend service.
60*cf78ab8cSAndroid Build Coastguard Worker class FrontendClientImpl : public FrontendClient {
61*cf78ab8cSAndroid Build Coastguard Worker public:
FrontendClientImpl(std::unique_ptr<frontend::FrontendService::Stub> stub)62*cf78ab8cSAndroid Build Coastguard Worker FrontendClientImpl(std::unique_ptr<frontend::FrontendService::Stub> stub)
63*cf78ab8cSAndroid Build Coastguard Worker : stub_(std::move(stub)) {}
64*cf78ab8cSAndroid Build Coastguard Worker
make_result(const grpc::Status & status,const google::protobuf::Message & message) const65*cf78ab8cSAndroid Build Coastguard Worker std::unique_ptr<ClientResult> make_result(
66*cf78ab8cSAndroid Build Coastguard Worker const grpc::Status &status,
67*cf78ab8cSAndroid Build Coastguard Worker const google::protobuf::Message &message) const {
68*cf78ab8cSAndroid Build Coastguard Worker std::vector<unsigned char> message_vec(message.ByteSizeLong());
69*cf78ab8cSAndroid Build Coastguard Worker message.SerializeToArray(message_vec.data(), message_vec.size());
70*cf78ab8cSAndroid Build Coastguard Worker if (!status.ok()) {
71*cf78ab8cSAndroid Build Coastguard Worker return std::make_unique<ClientResult>(false, status.error_message(),
72*cf78ab8cSAndroid Build Coastguard Worker message_vec);
73*cf78ab8cSAndroid Build Coastguard Worker }
74*cf78ab8cSAndroid Build Coastguard Worker return std::make_unique<ClientResult>(true, "", message_vec);
75*cf78ab8cSAndroid Build Coastguard Worker }
76*cf78ab8cSAndroid Build Coastguard Worker
77*cf78ab8cSAndroid Build Coastguard Worker // Gets the version of the network simulator service.
GetVersion() const78*cf78ab8cSAndroid Build Coastguard Worker std::unique_ptr<ClientResult> GetVersion() const override {
79*cf78ab8cSAndroid Build Coastguard Worker frontend::VersionResponse response;
80*cf78ab8cSAndroid Build Coastguard Worker grpc::ClientContext context_;
81*cf78ab8cSAndroid Build Coastguard Worker auto status = stub_->GetVersion(&context_, {}, &response);
82*cf78ab8cSAndroid Build Coastguard Worker return make_result(status, response);
83*cf78ab8cSAndroid Build Coastguard Worker }
84*cf78ab8cSAndroid Build Coastguard Worker
85*cf78ab8cSAndroid Build Coastguard Worker // Gets the list of device information
ListDevice() const86*cf78ab8cSAndroid Build Coastguard Worker std::unique_ptr<ClientResult> ListDevice() const override {
87*cf78ab8cSAndroid Build Coastguard Worker frontend::ListDeviceResponse response;
88*cf78ab8cSAndroid Build Coastguard Worker grpc::ClientContext context_;
89*cf78ab8cSAndroid Build Coastguard Worker auto status = stub_->ListDevice(&context_, {}, &response);
90*cf78ab8cSAndroid Build Coastguard Worker return make_result(status, response);
91*cf78ab8cSAndroid Build Coastguard Worker }
92*cf78ab8cSAndroid Build Coastguard Worker
Reset() const93*cf78ab8cSAndroid Build Coastguard Worker std::unique_ptr<ClientResult> Reset() const override {
94*cf78ab8cSAndroid Build Coastguard Worker grpc::ClientContext context_;
95*cf78ab8cSAndroid Build Coastguard Worker google::protobuf::Empty response;
96*cf78ab8cSAndroid Build Coastguard Worker auto status = stub_->Reset(&context_, {}, &response);
97*cf78ab8cSAndroid Build Coastguard Worker return make_result(status, response);
98*cf78ab8cSAndroid Build Coastguard Worker }
99*cf78ab8cSAndroid Build Coastguard Worker
CreateDevice(rust::Vec<::rust::u8> const & request_byte_vec) const100*cf78ab8cSAndroid Build Coastguard Worker std::unique_ptr<ClientResult> CreateDevice(
101*cf78ab8cSAndroid Build Coastguard Worker rust::Vec<::rust::u8> const &request_byte_vec) const {
102*cf78ab8cSAndroid Build Coastguard Worker frontend::CreateDeviceResponse response;
103*cf78ab8cSAndroid Build Coastguard Worker grpc::ClientContext context_;
104*cf78ab8cSAndroid Build Coastguard Worker frontend::CreateDeviceRequest request;
105*cf78ab8cSAndroid Build Coastguard Worker if (!request.ParseFromArray(request_byte_vec.data(),
106*cf78ab8cSAndroid Build Coastguard Worker request_byte_vec.size())) {
107*cf78ab8cSAndroid Build Coastguard Worker return make_result(
108*cf78ab8cSAndroid Build Coastguard Worker grpc::Status(
109*cf78ab8cSAndroid Build Coastguard Worker grpc::StatusCode::INVALID_ARGUMENT,
110*cf78ab8cSAndroid Build Coastguard Worker "Error parsing CreateDevice request protobuf. request size:" +
111*cf78ab8cSAndroid Build Coastguard Worker std::to_string(request_byte_vec.size())),
112*cf78ab8cSAndroid Build Coastguard Worker response);
113*cf78ab8cSAndroid Build Coastguard Worker }
114*cf78ab8cSAndroid Build Coastguard Worker auto status = stub_->CreateDevice(&context_, request, &response);
115*cf78ab8cSAndroid Build Coastguard Worker return make_result(status, response);
116*cf78ab8cSAndroid Build Coastguard Worker }
117*cf78ab8cSAndroid Build Coastguard Worker
118*cf78ab8cSAndroid Build Coastguard Worker // Patchs the information of the device
PatchDevice(rust::Vec<::rust::u8> const & request_byte_vec) const119*cf78ab8cSAndroid Build Coastguard Worker std::unique_ptr<ClientResult> PatchDevice(
120*cf78ab8cSAndroid Build Coastguard Worker rust::Vec<::rust::u8> const &request_byte_vec) const override {
121*cf78ab8cSAndroid Build Coastguard Worker google::protobuf::Empty response;
122*cf78ab8cSAndroid Build Coastguard Worker grpc::ClientContext context_;
123*cf78ab8cSAndroid Build Coastguard Worker frontend::PatchDeviceRequest request;
124*cf78ab8cSAndroid Build Coastguard Worker if (!request.ParseFromArray(request_byte_vec.data(),
125*cf78ab8cSAndroid Build Coastguard Worker request_byte_vec.size())) {
126*cf78ab8cSAndroid Build Coastguard Worker return make_result(
127*cf78ab8cSAndroid Build Coastguard Worker grpc::Status(
128*cf78ab8cSAndroid Build Coastguard Worker grpc::StatusCode::INVALID_ARGUMENT,
129*cf78ab8cSAndroid Build Coastguard Worker "Error parsing PatchDevice request protobuf. request size:" +
130*cf78ab8cSAndroid Build Coastguard Worker std::to_string(request_byte_vec.size())),
131*cf78ab8cSAndroid Build Coastguard Worker response);
132*cf78ab8cSAndroid Build Coastguard Worker };
133*cf78ab8cSAndroid Build Coastguard Worker auto status = stub_->PatchDevice(&context_, request, &response);
134*cf78ab8cSAndroid Build Coastguard Worker return make_result(status, response);
135*cf78ab8cSAndroid Build Coastguard Worker }
136*cf78ab8cSAndroid Build Coastguard Worker
DeleteChip(rust::Vec<::rust::u8> const & request_byte_vec) const137*cf78ab8cSAndroid Build Coastguard Worker std::unique_ptr<ClientResult> DeleteChip(
138*cf78ab8cSAndroid Build Coastguard Worker rust::Vec<::rust::u8> const &request_byte_vec) const {
139*cf78ab8cSAndroid Build Coastguard Worker google::protobuf::Empty response;
140*cf78ab8cSAndroid Build Coastguard Worker grpc::ClientContext context_;
141*cf78ab8cSAndroid Build Coastguard Worker frontend::DeleteChipRequest request;
142*cf78ab8cSAndroid Build Coastguard Worker if (!request.ParseFromArray(request_byte_vec.data(),
143*cf78ab8cSAndroid Build Coastguard Worker request_byte_vec.size())) {
144*cf78ab8cSAndroid Build Coastguard Worker return make_result(
145*cf78ab8cSAndroid Build Coastguard Worker grpc::Status(
146*cf78ab8cSAndroid Build Coastguard Worker grpc::StatusCode::INVALID_ARGUMENT,
147*cf78ab8cSAndroid Build Coastguard Worker "Error parsing DeleteChip request protobuf. request size:" +
148*cf78ab8cSAndroid Build Coastguard Worker std::to_string(request_byte_vec.size())),
149*cf78ab8cSAndroid Build Coastguard Worker response);
150*cf78ab8cSAndroid Build Coastguard Worker }
151*cf78ab8cSAndroid Build Coastguard Worker auto status = stub_->DeleteChip(&context_, request, &response);
152*cf78ab8cSAndroid Build Coastguard Worker return make_result(status, response);
153*cf78ab8cSAndroid Build Coastguard Worker }
154*cf78ab8cSAndroid Build Coastguard Worker
155*cf78ab8cSAndroid Build Coastguard Worker // Get the list of Capture information
ListCapture() const156*cf78ab8cSAndroid Build Coastguard Worker std::unique_ptr<ClientResult> ListCapture() const override {
157*cf78ab8cSAndroid Build Coastguard Worker frontend::ListCaptureResponse response;
158*cf78ab8cSAndroid Build Coastguard Worker grpc::ClientContext context_;
159*cf78ab8cSAndroid Build Coastguard Worker auto status = stub_->ListCapture(&context_, {}, &response);
160*cf78ab8cSAndroid Build Coastguard Worker return make_result(status, response);
161*cf78ab8cSAndroid Build Coastguard Worker }
162*cf78ab8cSAndroid Build Coastguard Worker
163*cf78ab8cSAndroid Build Coastguard Worker // Patch the Capture
PatchCapture(rust::Vec<::rust::u8> const & request_byte_vec) const164*cf78ab8cSAndroid Build Coastguard Worker std::unique_ptr<ClientResult> PatchCapture(
165*cf78ab8cSAndroid Build Coastguard Worker rust::Vec<::rust::u8> const &request_byte_vec) const override {
166*cf78ab8cSAndroid Build Coastguard Worker google::protobuf::Empty response;
167*cf78ab8cSAndroid Build Coastguard Worker grpc::ClientContext context_;
168*cf78ab8cSAndroid Build Coastguard Worker frontend::PatchCaptureRequest request;
169*cf78ab8cSAndroid Build Coastguard Worker if (!request.ParseFromArray(request_byte_vec.data(),
170*cf78ab8cSAndroid Build Coastguard Worker request_byte_vec.size())) {
171*cf78ab8cSAndroid Build Coastguard Worker return make_result(
172*cf78ab8cSAndroid Build Coastguard Worker grpc::Status(
173*cf78ab8cSAndroid Build Coastguard Worker grpc::StatusCode::INVALID_ARGUMENT,
174*cf78ab8cSAndroid Build Coastguard Worker "Error parsing PatchCapture request protobuf. request size:" +
175*cf78ab8cSAndroid Build Coastguard Worker std::to_string(request_byte_vec.size())),
176*cf78ab8cSAndroid Build Coastguard Worker response);
177*cf78ab8cSAndroid Build Coastguard Worker };
178*cf78ab8cSAndroid Build Coastguard Worker auto status = stub_->PatchCapture(&context_, request, &response);
179*cf78ab8cSAndroid Build Coastguard Worker return make_result(status, response);
180*cf78ab8cSAndroid Build Coastguard Worker }
181*cf78ab8cSAndroid Build Coastguard Worker
182*cf78ab8cSAndroid Build Coastguard Worker // Download capture file by using ClientResponseReader to handle streaming
183*cf78ab8cSAndroid Build Coastguard Worker // grpc
GetCapture(rust::Vec<::rust::u8> const & request_byte_vec,ClientResponseReader const & client_reader) const184*cf78ab8cSAndroid Build Coastguard Worker std::unique_ptr<ClientResult> GetCapture(
185*cf78ab8cSAndroid Build Coastguard Worker rust::Vec<::rust::u8> const &request_byte_vec,
186*cf78ab8cSAndroid Build Coastguard Worker ClientResponseReader const &client_reader) const override {
187*cf78ab8cSAndroid Build Coastguard Worker grpc::ClientContext context_;
188*cf78ab8cSAndroid Build Coastguard Worker frontend::GetCaptureRequest request;
189*cf78ab8cSAndroid Build Coastguard Worker if (!request.ParseFromArray(request_byte_vec.data(),
190*cf78ab8cSAndroid Build Coastguard Worker request_byte_vec.size())) {
191*cf78ab8cSAndroid Build Coastguard Worker return make_result(
192*cf78ab8cSAndroid Build Coastguard Worker grpc::Status(
193*cf78ab8cSAndroid Build Coastguard Worker grpc::StatusCode::INVALID_ARGUMENT,
194*cf78ab8cSAndroid Build Coastguard Worker "Error parsing GetCapture request protobuf. request size:" +
195*cf78ab8cSAndroid Build Coastguard Worker std::to_string(request_byte_vec.size())),
196*cf78ab8cSAndroid Build Coastguard Worker google::protobuf::Empty());
197*cf78ab8cSAndroid Build Coastguard Worker };
198*cf78ab8cSAndroid Build Coastguard Worker auto reader = stub_->GetCapture(&context_, request);
199*cf78ab8cSAndroid Build Coastguard Worker frontend::GetCaptureResponse chunk;
200*cf78ab8cSAndroid Build Coastguard Worker // Read every available chunks from grpc reader
201*cf78ab8cSAndroid Build Coastguard Worker while (reader->Read(&chunk)) {
202*cf78ab8cSAndroid Build Coastguard Worker // Using a mutable protobuf here so the move iterator can move
203*cf78ab8cSAndroid Build Coastguard Worker // the capture stream without copying.
204*cf78ab8cSAndroid Build Coastguard Worker auto mut_stream = chunk.mutable_capture_stream();
205*cf78ab8cSAndroid Build Coastguard Worker auto bytes =
206*cf78ab8cSAndroid Build Coastguard Worker std::vector<uint8_t>(std::make_move_iterator(mut_stream->begin()),
207*cf78ab8cSAndroid Build Coastguard Worker std::make_move_iterator(mut_stream->end()));
208*cf78ab8cSAndroid Build Coastguard Worker client_reader.handle_chunk(
209*cf78ab8cSAndroid Build Coastguard Worker rust::Slice<const uint8_t>{bytes.data(), bytes.size()});
210*cf78ab8cSAndroid Build Coastguard Worker }
211*cf78ab8cSAndroid Build Coastguard Worker auto status = reader->Finish();
212*cf78ab8cSAndroid Build Coastguard Worker return make_result(status, google::protobuf::Empty());
213*cf78ab8cSAndroid Build Coastguard Worker }
214*cf78ab8cSAndroid Build Coastguard Worker
215*cf78ab8cSAndroid Build Coastguard Worker // Helper function to redirect to the correct Grpc call
SendGrpc(frontend::GrpcMethod const & grpc_method,rust::Vec<::rust::u8> const & request_byte_vec) const216*cf78ab8cSAndroid Build Coastguard Worker std::unique_ptr<ClientResult> SendGrpc(
217*cf78ab8cSAndroid Build Coastguard Worker frontend::GrpcMethod const &grpc_method,
218*cf78ab8cSAndroid Build Coastguard Worker rust::Vec<::rust::u8> const &request_byte_vec) const override {
219*cf78ab8cSAndroid Build Coastguard Worker switch (grpc_method) {
220*cf78ab8cSAndroid Build Coastguard Worker case frontend::GrpcMethod::GetVersion:
221*cf78ab8cSAndroid Build Coastguard Worker return GetVersion();
222*cf78ab8cSAndroid Build Coastguard Worker case frontend::GrpcMethod::CreateDevice:
223*cf78ab8cSAndroid Build Coastguard Worker return CreateDevice(request_byte_vec);
224*cf78ab8cSAndroid Build Coastguard Worker case frontend::GrpcMethod::DeleteChip:
225*cf78ab8cSAndroid Build Coastguard Worker return DeleteChip(request_byte_vec);
226*cf78ab8cSAndroid Build Coastguard Worker case frontend::GrpcMethod::PatchDevice:
227*cf78ab8cSAndroid Build Coastguard Worker return PatchDevice(request_byte_vec);
228*cf78ab8cSAndroid Build Coastguard Worker case frontend::GrpcMethod::ListDevice:
229*cf78ab8cSAndroid Build Coastguard Worker return ListDevice();
230*cf78ab8cSAndroid Build Coastguard Worker case frontend::GrpcMethod::Reset:
231*cf78ab8cSAndroid Build Coastguard Worker return Reset();
232*cf78ab8cSAndroid Build Coastguard Worker case frontend::GrpcMethod::ListCapture:
233*cf78ab8cSAndroid Build Coastguard Worker return ListCapture();
234*cf78ab8cSAndroid Build Coastguard Worker case frontend::GrpcMethod::PatchCapture:
235*cf78ab8cSAndroid Build Coastguard Worker return PatchCapture(request_byte_vec);
236*cf78ab8cSAndroid Build Coastguard Worker default:
237*cf78ab8cSAndroid Build Coastguard Worker return make_result(grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
238*cf78ab8cSAndroid Build Coastguard Worker "Unknown GrpcMethod found."),
239*cf78ab8cSAndroid Build Coastguard Worker google::protobuf::Empty());
240*cf78ab8cSAndroid Build Coastguard Worker }
241*cf78ab8cSAndroid Build Coastguard Worker }
242*cf78ab8cSAndroid Build Coastguard Worker
243*cf78ab8cSAndroid Build Coastguard Worker private:
244*cf78ab8cSAndroid Build Coastguard Worker std::unique_ptr<frontend::FrontendService::Stub> stub_;
245*cf78ab8cSAndroid Build Coastguard Worker
CheckStatus(const grpc::Status & status,const std::string & message)246*cf78ab8cSAndroid Build Coastguard Worker static bool CheckStatus(const grpc::Status &status,
247*cf78ab8cSAndroid Build Coastguard Worker const std::string &message) {
248*cf78ab8cSAndroid Build Coastguard Worker if (status.ok()) return true;
249*cf78ab8cSAndroid Build Coastguard Worker if (status.error_code() == grpc::StatusCode::UNAVAILABLE)
250*cf78ab8cSAndroid Build Coastguard Worker BtsLogError(
251*cf78ab8cSAndroid Build Coastguard Worker "netsim frontend service is unavailable, "
252*cf78ab8cSAndroid Build Coastguard Worker "please restart.");
253*cf78ab8cSAndroid Build Coastguard Worker else
254*cf78ab8cSAndroid Build Coastguard Worker BtsLogError("request to frontend service failed (%d) - %s",
255*cf78ab8cSAndroid Build Coastguard Worker status.error_code(), status.error_message().c_str());
256*cf78ab8cSAndroid Build Coastguard Worker return false;
257*cf78ab8cSAndroid Build Coastguard Worker }
258*cf78ab8cSAndroid Build Coastguard Worker };
259*cf78ab8cSAndroid Build Coastguard Worker
260*cf78ab8cSAndroid Build Coastguard Worker } // namespace
261*cf78ab8cSAndroid Build Coastguard Worker
NewFrontendClient(const std::string & server)262*cf78ab8cSAndroid Build Coastguard Worker std::unique_ptr<FrontendClient> NewFrontendClient(const std::string &server) {
263*cf78ab8cSAndroid Build Coastguard Worker auto stub = NewFrontendStub(server);
264*cf78ab8cSAndroid Build Coastguard Worker return (stub == nullptr
265*cf78ab8cSAndroid Build Coastguard Worker ? nullptr
266*cf78ab8cSAndroid Build Coastguard Worker : std::make_unique<FrontendClientImpl>(std::move(stub)));
267*cf78ab8cSAndroid Build Coastguard Worker }
268*cf78ab8cSAndroid Build Coastguard Worker
269*cf78ab8cSAndroid Build Coastguard Worker } // namespace frontend
270*cf78ab8cSAndroid Build Coastguard Worker } // namespace netsim