// // Copyright (C) 2021 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #pragma once #include #include #include #include #include #include #include #include #include #include "common/libs/confui/packet_types.h" #include "common/libs/confui/utils.h" #include "common/libs/fs/shared_buf.h" #include "common/libs/fs/shared_fd.h" /** * @file packet.h * * @brief lowest-level packet for communication between host & guest * * Each packet has three fields * 1. session_id_: the name of the currently active confirmation UI session * 2. type_: the type of command/response. E.g. start, stop, ack, abort, etc * 3. additional_info_: all the other additional information * * The binary representation of each packet is as follows: * n:L[1]:L[2]:...:L[n]:data[1]data[2]data[3]...data[n] * * The additional_info_ is in general a variable number of items, each * is either a byte vector (e.g. std::vector) or a string. * * n is the number of items. L[i] is the length of i th item. data[i] * is the binary representation of the i th item * */ namespace cuttlefish { namespace confui { namespace packet { /* * methods in namespace impl is not intended for public use * * For exposed APIs, skip to "start of public APIs * or, skip the namespace impl */ namespace impl { template void AppendToBuffer(Buffer& buffer, Args&&... args) { (buffer.insert(buffer.end(), std::begin(std::forward(args)), std::end(std::forward(args))), ...); } template std::vector MakeSizeHeader(Args&&... args) { std::vector lengths; (lengths.push_back(std::distance(std::begin(args), std::end(args))), ...); return lengths; } // Use only this function to make a packet to send over the confirmation // ui packet layer template Payload ToPayload(const std::string& cmd_str, const std::string& session_id, Args&&... args) { using namespace cuttlefish::confui::packet::impl; constexpr auto n_args = sizeof...(Args); std::stringstream ss; ss << ArgsToString(session_id, ":", cmd_str, ":", n_args, ":"); // create size header std::vector size_info = impl::MakeSizeHeader(std::forward(args)...); for (const auto sz : size_info) { ss << sz << ":"; } std::string header = ss.str(); std::vector payload_buffer{header.begin(), header.end()}; impl::AppendToBuffer(payload_buffer, std::forward(args)...); PayloadHeader ph; ph.payload_length_ = payload_buffer.size(); return {ph, payload_buffer}; } } // namespace impl /* * start of public methods */ std::optional ReadPayload(SharedFD s); template bool WritePayload(SharedFD d, const std::string& cmd_str, const std::string& session_id, Args&&... args) { // TODO(kwstephenkim): type check Args... so that they are either // kind of std::string or std::vector<1 byte> if (!d->IsOpen()) { ConfUiLog(ERROR) << "file, socket, etc, is not open to write"; return false; } auto [payload_header, data_to_send] = impl::ToPayload(cmd_str, session_id, std::forward(args)...); const std::string data_in_str(data_to_send.cbegin(), data_to_send.cend()); auto nwrite = WriteAll(d, reinterpret_cast(&payload_header), sizeof(payload_header)); if (nwrite != sizeof(payload_header)) { return false; } nwrite = WriteAll(d, reinterpret_cast(data_to_send.data()), data_to_send.size()); if (nwrite != data_to_send.size()) { return false; } return true; } } // end of namespace packet } // end of namespace confui } // end of namespace cuttlefish