1 //
2 // Copyright (C) 2021 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 #pragma once
17 
18 #include <algorithm>
19 #include <cstdint>
20 #include <optional>
21 #include <string>
22 #include <tuple>
23 #include <type_traits>
24 #include <vector>
25 
26 #include <android-base/logging.h>
27 #include <android-base/strings.h>
28 
29 #include "common/libs/confui/packet_types.h"
30 #include "common/libs/confui/utils.h"
31 #include "common/libs/fs/shared_buf.h"
32 #include "common/libs/fs/shared_fd.h"
33 
34 /**
35  * @file packet.h
36  *
37  * @brief lowest-level packet for communication between host & guest
38  *
39  * Each packet has three fields
40  *  1. session_id_: the name of the currently active confirmation UI session
41  *  2. type_: the type of command/response. E.g. start, stop, ack, abort, etc
42  *  3. additional_info_: all the other additional information
43  *
44  * The binary representation of each packet is as follows:
45  *  n:L[1]:L[2]:...:L[n]:data[1]data[2]data[3]...data[n]
46  *
47  * The additional_info_ is in general a variable number of items, each
48  * is either a byte vector (e.g. std::vector<uint8_t>) or a string.
49  *
50  * n is the number of items. L[i] is the length of i th item. data[i]
51  * is the binary representation of the i th item
52  *
53  */
54 namespace cuttlefish {
55 namespace confui {
56 namespace packet {
57 
58 /*
59  * methods in namespace impl is not intended for public use
60  *
61  * For exposed APIs, skip to "start of public APIs
62  * or, skip the namespace impl
63  */
64 namespace impl {
65 template <typename Buffer, typename... Args>
AppendToBuffer(Buffer & buffer,Args &&...args)66 void AppendToBuffer(Buffer& buffer, Args&&... args) {
67   (buffer.insert(buffer.end(), std::begin(std::forward<Args>(args)),
68                  std::end(std::forward<Args>(args))),
69    ...);
70 }
71 
72 template <typename... Args>
MakeSizeHeader(Args &&...args)73 std::vector<int> MakeSizeHeader(Args&&... args) {
74   std::vector<int> lengths;
75   (lengths.push_back(std::distance(std::begin(args), std::end(args))), ...);
76   return lengths;
77 }
78 
79 // Use only this function to make a packet to send over the confirmation
80 // ui packet layer
81 template <typename... Args>
ToPayload(const std::string & cmd_str,const std::string & session_id,Args &&...args)82 Payload ToPayload(const std::string& cmd_str, const std::string& session_id,
83                   Args&&... args) {
84   using namespace cuttlefish::confui::packet::impl;
85   constexpr auto n_args = sizeof...(Args);
86   std::stringstream ss;
87   ss << ArgsToString(session_id, ":", cmd_str, ":", n_args, ":");
88   // create size header
89   std::vector<int> size_info =
90       impl::MakeSizeHeader(std::forward<Args>(args)...);
91   for (const auto sz : size_info) {
92     ss << sz << ":";
93   }
94   std::string header = ss.str();
95   std::vector<std::uint8_t> payload_buffer{header.begin(), header.end()};
96   impl::AppendToBuffer(payload_buffer, std::forward<Args>(args)...);
97 
98   PayloadHeader ph;
99   ph.payload_length_ = payload_buffer.size();
100   return {ph, payload_buffer};
101 }
102 }  // namespace impl
103 
104 /*
105  * start of public methods
106  */
107 std::optional<ParsedPacket> ReadPayload(SharedFD s);
108 
109 template <typename... Args>
WritePayload(SharedFD d,const std::string & cmd_str,const std::string & session_id,Args &&...args)110 bool WritePayload(SharedFD d, const std::string& cmd_str,
111                   const std::string& session_id, Args&&... args) {
112   // TODO(kwstephenkim): type check Args... so that they are either
113   // kind of std::string or std::vector<1 byte>
114   if (!d->IsOpen()) {
115     ConfUiLog(ERROR) << "file, socket, etc, is not open to write";
116     return false;
117   }
118   auto [payload_header, data_to_send] =
119       impl::ToPayload(cmd_str, session_id, std::forward<Args>(args)...);
120   const std::string data_in_str(data_to_send.cbegin(), data_to_send.cend());
121 
122   auto nwrite = WriteAll(d, reinterpret_cast<const char*>(&payload_header),
123                          sizeof(payload_header));
124   if (nwrite != sizeof(payload_header)) {
125     return false;
126   }
127   nwrite = WriteAll(d, reinterpret_cast<const char*>(data_to_send.data()),
128                     data_to_send.size());
129   if (nwrite != data_to_send.size()) {
130     return false;
131   }
132   return true;
133 }
134 
135 }  // end of namespace packet
136 }  // end of namespace confui
137 }  // end of namespace cuttlefish
138