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