• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 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 "bundler.h"
17 
18 #include <assert.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 
23 #include <cassert>
24 #include <list>
25 #include <map>
26 #include <vector>
27 
28 #include "bundler_schema_generated.h"
29 #include "flatbuffers/idl.h"
30 #include "flatbuffers/util.h"
31 
32 using namespace bluetooth;
33 using namespace dumpsys;
34 
35 struct Opts opts;
36 
37 /**
38  * Load a binary schema from persistent store using flatbuffer API.
39  *
40  * @param filename; Name of file to open and read.
41  * @param binary_schema: Backing store for flatbuffer binary schema data.
42  *
43  * @return: True if operation successful, false otherwise.
44  */
LoadBinarySchema(const char * filename,std::string * binary_schema)45 bool LoadBinarySchema(const char* filename, std::string* binary_schema) {
46   assert(filename != nullptr);
47   assert(binary_schema != nullptr);
48   if (!flatbuffers::LoadFile(filename, helper::AsBinaryFile, binary_schema)) {
49     fprintf(stderr, "Unable to open binary flatbuffer schema file:%s\n", filename);
50     return false;
51   };
52   return true;
53 }
54 
55 /**
56  * Verify a binary schema using flatbuffer API.
57  *
58  * @param schema: Raw binary schema to verify
59  *
60  * @return: True if operation successful, false otherwise.
61  */
VerifyBinarySchema(const std::vector<uint8_t> & raw_schema)62 bool VerifyBinarySchema(const std::vector<uint8_t>& raw_schema) {
63   flatbuffers::Verifier verifier(raw_schema.data(), raw_schema.size());
64   if (!reflection::VerifySchemaBuffer(verifier)) {
65     return false;
66   }
67   return true;
68 }
69 
70 /**
71  * Bundle a set of binary flatbuffer schema into the bundler database.
72  *
73  * @param builder; Flatbuffer builder
74  * @param filenames: Set of filenames to include in bundle
75  * @param vector_map: Filename to filedata mapping
76  *
77  * @return: True if operation successful, false otherwise.
78  */
CreateBinarySchemaBundle(flatbuffers::FlatBufferBuilder * builder,const std::vector<std::string> & filenames,std::vector<flatbuffers::Offset<BundledSchemaMap>> * vector_map,std::list<std::string> * bundled_names)79 bool CreateBinarySchemaBundle(
80     flatbuffers::FlatBufferBuilder* builder,
81     const std::vector<std::string>& filenames,
82     std::vector<flatbuffers::Offset<BundledSchemaMap>>* vector_map,
83     std::list<std::string>* bundled_names) {
84   assert(builder != nullptr);
85   assert(vector_map != nullptr);
86   assert(bundled_names != nullptr);
87 
88   for (auto filename : filenames) {
89     std::string string_schema;
90     if (!LoadBinarySchema(filename.c_str(), &string_schema)) {
91       fprintf(stderr, "Unable to load binary schema from filename:%s\n", filename.c_str());
92       return false;
93     }
94     std::vector<uint8_t> raw_schema(string_schema.begin(), string_schema.end());
95     if (!VerifyBinarySchema(raw_schema)) {
96       fprintf(stderr, "Failed verification on binary schema filename:%s\n", filename.c_str());
97       return false;
98     }
99 
100     const reflection::Schema* schema = reflection::GetSchema(raw_schema.data());
101     if (schema->root_table() == nullptr) {
102       fprintf(stderr, "Unable to find root table for binary flatbuffer schema:%s\n", filename.c_str());
103       return false;
104     }
105 
106     bundled_names->push_back(schema->root_table()->name()->str());
107     auto name = builder->CreateString(schema->root_table()->name()->str());
108     auto data = builder->CreateVector<uint8_t>(raw_schema.data(), raw_schema.size());
109     vector_map->push_back(CreateBundledSchemaMap(*builder, name, data));
110 
111     if (opts.verbose) {
112       fprintf(stdout, "Bundled binary schema file:%s\n", schema->root_table()->name()->c_str());
113     }
114   }
115   return true;
116 }
117 
118 /**
119  * Write generated header file containing the bundled binary schema
120  * data and meta data
121  *
122  * @param data: Source file data.
123  * @param data_len: length of data
124  */
WriteHeaderFile(FILE * fp,const uint8_t * data,size_t data_len)125 void WriteHeaderFile(FILE* fp, const uint8_t* data, size_t data_len) {
126   assert(fp != nullptr);
127   std::string delim(kDefaultNamespaceDelim);
128   std::string ns_string(opts.ns_name);
129   std::vector<std::string> namespaces;
130 
131   size_t start = 0;
132   size_t end = ns_string.find(delim);
133   while (end != std::string::npos) {
134     namespaces.push_back(ns_string.substr(start, end - start));
135     start = end + delim.size();
136     end = ns_string.find(delim, start);
137   }
138   if (start != 0 && start != std::string::npos) {
139     namespaces.push_back(ns_string.substr(start));
140   } else if (!ns_string.empty()) {
141     namespaces.push_back(ns_string);
142   }
143 
144   std::string namespace_prefix;
145   for (const auto& name : namespaces) namespace_prefix += (name + '_');
146 
147   fprintf(
148       fp,
149       "// Generated file by bluetooth_flatbuffer bundler\n"
150       "#include <string>\n");
151   for_each(
152       namespaces.begin(), namespaces.end(), [fp](const std::string& s) { fprintf(fp, "namespace %s {\n", s.c_str()); });
153   fprintf(fp, "extern const std::string& GetBundledSchemaData();\n");
154   fprintf(fp, "const unsigned char %sdata_[%zu] = {\n", namespace_prefix.c_str(), data_len);
155 
156   for (size_t i = 0; i < data_len; i++) {
157     fprintf(fp, " 0x%02x", data[i]);
158     if (i != data_len - 1) {
159       fprintf(fp, ",");
160     }
161     if ((i + 1) % 16 == 0) {
162       fprintf(fp, "\n");
163     }
164   }
165   fprintf(fp, " };\n");
166   fprintf(
167       fp,
168       "const std::string %sstring_data_(%sdata_, %sdata_ + sizeof(%sdata_));\n",
169       namespace_prefix.c_str(),
170       namespace_prefix.c_str(),
171       namespace_prefix.c_str(),
172       namespace_prefix.c_str());
173   fprintf(fp, "const std::string& GetBundledSchemaData() { return %sstring_data_; }\n", namespace_prefix.c_str());
174 
175   for_each(namespaces.crbegin(), namespaces.crend(), [fp](const std::string& s) {
176     fprintf(fp, "}  // namespace %s\n", s.c_str());
177   });
178 }
179 
ReadBundledSchema()180 int ReadBundledSchema() {
181   const char* filename = opts.filename;
182   assert(filename != nullptr);
183 
184   std::string flatfile_data;
185   if (!flatbuffers::LoadFile(filename, helper::AsBinaryFile, &flatfile_data)) {
186     fprintf(stderr, "Unable to load schema data file:%s\n", filename);
187     return -5;
188   }
189 
190   auto bundle_schema = flatbuffers::GetRoot<BundledSchema>(flatfile_data.c_str());
191   const flatbuffers::Vector<flatbuffers::Offset<BundledSchemaMap>>* map = bundle_schema->map();
192 
193   fprintf(stdout, "Bundle schema title:%s\n", bundle_schema->title()->c_str());
194   fprintf(stdout, "Bundle schema root_name:%s\n", bundle_schema->root_name()->c_str());
195   int cnt = 0;
196   for (auto it = map->cbegin(); it != map->cend(); ++it, cnt++) {
197     fprintf(stdout, "   %d name:%s schema:%s\n", cnt, it->name()->c_str(), "schema");
198   }
199   return EXIT_SUCCESS;
200 }
201 
WriteBundledSchema()202 int WriteBundledSchema() {
203   const char* filename = opts.filename;
204   assert(filename != nullptr);
205 
206   const char* main_root_name = opts.main_root_name;
207   if (main_root_name == nullptr) {
208     fprintf(stderr, "Must specify the name of the main root name for this bundle\n");
209     return EXIT_FAILURE;
210   }
211 
212   std::vector<std::string> bfbs_filenames;
213   for (int i = 0; i < opts.arg.c; i++) {
214     bfbs_filenames.push_back(std::string(opts.arg.v[i]));
215   }
216   if (bfbs_filenames.empty()) {
217     fprintf(stderr, "No bfbs files are specified to bundle\n");
218     return EXIT_FAILURE;
219   }
220 
221   flatbuffers::FlatBufferBuilder builder(1024);
222 
223   std::list<std::string> bundled_names;
224   std::vector<flatbuffers::Offset<BundledSchemaMap>> vector_map;
225   if (!CreateBinarySchemaBundle(&builder, bfbs_filenames, &vector_map, &bundled_names)) {
226     fprintf(stderr, "Unable to bundle schema bfbs files\n");
227     return EXIT_FAILURE;
228   }
229 
230   if (std::find(bundled_names.begin(), bundled_names.end(), main_root_name) == bundled_names.end()) {
231     fprintf(stderr, "The main root name must match one of the bundled schema names\n");
232     fprintf(stderr, "  main root name:%s\n", main_root_name);
233     for (auto name : bundled_names) {
234       fprintf(stderr, "  bundled schema name:%s\n", name.c_str());
235     }
236     return EXIT_FAILURE;
237   }
238 
239   const char* title = opts.title;
240   auto schema_offset = CreateBundledSchemaDirect(builder, title, main_root_name, &vector_map);
241   builder.Finish(schema_offset);
242 
243   std::string final_filename(opts.gen);
244   final_filename.append("/");
245   final_filename.append(filename);
246   if (!flatbuffers::SaveFile(
247           final_filename.c_str(), (const char*)builder.GetBufferPointer(), builder.GetSize(), helper::AsBinaryFile)) {
248     fprintf(stderr, "Unable to save file:%s\n", final_filename.c_str());
249     return EXIT_FAILURE;
250   }
251 
252   std::string header(opts.gen);
253   header += ("/" + std::string(opts.filename) + ".cc");
254   FILE* fp = fopen(header.c_str(), "w+");
255   if (fp == nullptr) {
256     fprintf(stdout, "Unable to open for writing header file:%s\n", header.c_str());
257     return EXIT_FAILURE;
258   }
259   WriteHeaderFile(fp, builder.GetBufferPointer(), builder.GetSize());
260   fclose(fp);
261   return EXIT_SUCCESS;
262 }
263 
Usage(int argc,char ** argv)264 int Usage(int argc, char** argv) {
265   fprintf(
266       stderr,
267       "Usage: %s [-r | -w] [-f <filename>] [-g <gen_out_path>] [-n <namespace> ] [-v] -m <main_root_name> <file.bfbs "
268       "...>\n",
269       argv[0]);
270   fprintf(stderr, " -r|-w : Read or write a dumpsys file\n");
271   fprintf(stderr, " -f : Filename bundled schema to read or write (default:%s)\n", kDefaultBundleDataFile);
272   fprintf(stderr, " -g : Generated file output path\n");
273   fprintf(stderr, " -n : Namespace to embed binary output bundle data source\n");
274   fprintf(stderr, " -m : Name of the main root of this bundle\n");
275   fprintf(stderr, " -v : Verbose printing mode\n");
276   return EXIT_FAILURE;
277 }
278 
ParseArgs(int argc,char ** argv)279 void ParseArgs(int argc, char** argv) {
280   int opt;
281   int parsed_cnt = 1;
282   while ((opt = getopt(argc, argv, "f:g:m:n:rt:vw")) != -1) {
283     parsed_cnt++;
284     switch (opt) {
285       case 'f':
286         opts.filename = optarg;
287         parsed_cnt++;
288         break;
289       case 'g':
290         opts.gen = optarg;
291         parsed_cnt++;
292         break;
293       case 'm':
294         opts.main_root_name = optarg;
295         parsed_cnt++;
296         break;
297       case 'n':
298         opts.ns_name = optarg;
299         parsed_cnt++;
300         break;
301       case 'r':
302         opts.read = true;
303         break;
304       case 'w':
305         opts.write = true;
306         break;
307       case 't':
308         opts.title = optarg;
309         parsed_cnt++;
310         break;
311       case 'v':
312         opts.verbose = true;
313         break;
314       default:
315         exit(Usage(argc, argv));
316         break;
317     }
318   }
319   opts.arg.c = argc - parsed_cnt;
320   opts.arg.v = &argv[parsed_cnt];
321 }
322