• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 #include "canprototools.h"
17 
18 #include <aidl/android/hardware/automotive/can/IndexedInterface.h>
19 #include <aidl/android/hardware/automotive/can/NativeInterface.h>
20 #include <aidl/android/hardware/automotive/can/SlcanInterface.h>
21 #include <aidl/android/hardware/automotive/can/VirtualInterface.h>
22 
23 #include <android-base/logging.h>
24 #include <google/protobuf/io/zero_copy_stream_impl.h>
25 #include <google/protobuf/text_format.h>
26 
27 #include <fstream>
28 
29 namespace android::hardware::automotive::can::config {
30 
31 using ::aidl::android::hardware::automotive::can::BusConfig;
32 using ::aidl::android::hardware::automotive::can::IndexedInterface;
33 using ::aidl::android::hardware::automotive::can::InterfaceType;
34 using ::aidl::android::hardware::automotive::can::NativeInterface;
35 using ::aidl::android::hardware::automotive::can::Result;
36 using ::aidl::android::hardware::automotive::can::SlcanInterface;
37 using ::aidl::android::hardware::automotive::can::VirtualInterface;
38 
39 /**
40  * Helper function for parseConfigFile. readString is used to get the fist n characters (n) from an
41  * istream object (s) and return it as a string object.
42  *
43  * \param s istream of the file you intend to read.
44  * \param n streamsize object of the number of characters you'd like.
45  * \return optional string containing up to n characters from the stream(s) you provided.
46  */
readString(std::istream & s,std::streamsize n)47 static std::optional<std::string> readString(std::istream& s, std::streamsize n) {
48     char buff[n];
49     auto got = s.read(buff, n).gcount();
50     if (!s.good() && !s.eof()) return std::nullopt;
51     return std::string(buff, 0, std::min(n, got));
52 }
53 
54 /*
55   parseConfigFile *used to* contain the body of parseConfigStream. However, it seems there's some
56   sort of odd behavior with IstreamInputStream and/or TextFormat::Parse, which causes HW Address
57   Sanitizer to flag a "tag-mismatch" in this function. Having the ifstream defined in a wrapper
58   function seems to solve this problem. The exact cause of this problem is yet unknown, but probably
59   lies somewhere in the protobuf implementation.
60 */
parseConfigStream(std::ifstream & cfg_stream)61 static __attribute__((noinline)) std::optional<CanBusConfig> parseConfigStream(
62         std::ifstream& cfg_stream) {
63     static const std::array<std::string, 3> text_headers = {"buses", "#", "controller"};
64     auto cfg_file_snippet = readString(cfg_stream, 10);
65 
66     if (!cfg_file_snippet.has_value()) {
67         LOG(ERROR) << "Can't read config from stream (maybe failed to open file?)";
68         return std::nullopt;
69     }
70     cfg_stream.seekg(0);
71 
72     // check if any of the textHeaders are at the start of the config file.
73     bool text_format = false;
74     for (auto const& header : text_headers) {
75         if (cfg_file_snippet->compare(0, header.length(), header) == 0) {
76             text_format = true;
77             break;
78         }
79     }
80 
81     CanBusConfig config;
82     if (text_format) {
83         google::protobuf::io::IstreamInputStream pb_stream(&cfg_stream);
84         if (!google::protobuf::TextFormat::Parse(&pb_stream, &config)) {
85             LOG(ERROR) << "Parsing text format config failed";
86             return std::nullopt;
87         }
88     } else if (!config.ParseFromIstream(&cfg_stream)) {
89         LOG(ERROR) << "Parsing binary format config failed";
90         return std::nullopt;
91     }
92     return config;
93 }
94 
parseConfigFile(const std::string & filepath)95 std::optional<CanBusConfig> parseConfigFile(const std::string& filepath) {
96     std::ifstream cfg_stream(filepath);
97     auto cfg_maybe = parseConfigStream(cfg_stream);
98     if (!cfg_maybe.has_value()) {
99         LOG(ERROR) << "Failed to parse " << filepath;
100     }
101     return cfg_maybe;
102 }
103 
fromPbBus(const Bus & pb_bus)104 std::optional<BusConfig> fromPbBus(const Bus& pb_bus) {
105     BusConfig bus_cfg = {};
106     bus_cfg.name = pb_bus.name();
107 
108     switch (pb_bus.iface_type_case()) {
109         case Bus::kNative: {
110             const std::string ifname = pb_bus.native().ifname();
111             const std::vector<std::string> serials = {pb_bus.native().serialno().begin(),
112                                                       pb_bus.native().serialno().end()};
113             if (ifname.empty() == serials.empty()) {
114                 LOG(ERROR) << "Invalid config: native type bus must have an iface name xor a "
115                            << "serial number";
116                 return std::nullopt;
117             }
118             bus_cfg.bitrate = pb_bus.bitrate();
119             NativeInterface nativeif = {};
120             if (!ifname.empty())
121                 nativeif.interfaceId.set<NativeInterface::InterfaceId::Tag::ifname>(ifname);
122             if (!serials.empty())
123                 nativeif.interfaceId.set<NativeInterface::InterfaceId::Tag::serialno>(serials);
124             bus_cfg.interfaceId.set<BusConfig::InterfaceId::Tag::nativeif>(nativeif);
125             break;
126         }
127         case Bus::kSlcan: {
128             const std::string ttyname = pb_bus.slcan().ttyname();
129             const std::vector<std::string> serials = {pb_bus.slcan().serialno().begin(),
130                                                       pb_bus.slcan().serialno().end()};
131             if (ttyname.empty() == serials.empty()) {
132                 LOG(ERROR) << "Invalid config: slcan type bus must have a tty name xor a serial "
133                            << "number";
134                 return std::nullopt;
135             }
136             bus_cfg.bitrate = pb_bus.bitrate();
137             SlcanInterface slcan = {};
138             if (!ttyname.empty())
139                 slcan.interfaceId.set<SlcanInterface::InterfaceId::Tag::ttyname>(ttyname);
140             if (!serials.empty())
141                 slcan.interfaceId.set<SlcanInterface::InterfaceId::Tag::serialno>(serials);
142             bus_cfg.interfaceId.set<BusConfig::InterfaceId::Tag::slcan>(slcan);
143             break;
144         }
145         case Bus::kVirtual: {
146             // Theoretically, we could just create the next available vcan iface.
147             const std::string ifname = pb_bus.virtual_().ifname();
148             if (ifname.empty()) {
149                 LOG(ERROR) << "Invalid config: native type bus must have an iface name";
150                 return std::nullopt;
151             }
152             VirtualInterface virtualif = {};
153             virtualif.ifname = ifname;
154             bus_cfg.interfaceId.set<BusConfig::InterfaceId::Tag::virtualif>(virtualif);
155             break;
156         }
157         case Bus::kIndexed: {
158             const uint8_t index = pb_bus.indexed().index();
159             if (index > UINT8_MAX) {
160                 LOG(ERROR) << "Interface index out of range: " << index;
161                 return std::nullopt;
162             }
163             IndexedInterface indexedif = {};
164             indexedif.index = index;
165             bus_cfg.interfaceId.set<BusConfig::InterfaceId::Tag::indexed>(indexedif);
166             break;
167         }
168         default:
169             LOG(ERROR) << "Invalid config: bad interface type for " << bus_cfg.name;
170             return std::nullopt;
171     }
172     return bus_cfg;
173 }
174 
getHalIftype(const Bus & pb_bus)175 std::optional<InterfaceType> getHalIftype(const Bus& pb_bus) {
176     switch (pb_bus.iface_type_case()) {
177         case Bus::kNative:
178             return InterfaceType::NATIVE;
179         case Bus::kSlcan:
180             return InterfaceType::SLCAN;
181         case Bus::kVirtual:
182             return InterfaceType::VIRTUAL;
183         case Bus::kIndexed:
184             return InterfaceType::INDEXED;
185         default:
186             return std::nullopt;
187     }
188 }
189 
resultStringFromStatus(const ndk::ScopedAStatus & status)190 std::string resultStringFromStatus(const ndk::ScopedAStatus& status) {
191     const auto res = static_cast<Result>(status.getServiceSpecificError());
192     switch (res) {
193         case Result::OK:
194             return "OK";
195         case Result::UNKNOWN_ERROR:
196             return "UNKNOWN_ERROR";
197         case Result::INVALID_STATE:
198             return "INVALID_STATE";
199         case Result::NOT_SUPPORTED:
200             return "NOT_SUPPORTED";
201         case Result::BAD_INTERFACE_ID:
202             return "BAD_INTERFACE_ID";
203         case Result::BAD_BITRATE:
204             return "BAD_BITRATE";
205         case Result::BAD_BUS_NAME:
206             return "BAD_BUS_NAME";
207         case Result::INTERFACE_DOWN:
208             return "INTERFACE_DOWN";
209         default:
210             return "Invalid Result!";
211     }
212 }
213 
214 }  // namespace android::hardware::automotive::can::config
215