You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
336 lines
11 KiB
336 lines
11 KiB
/*
|
|
* Copyright 2020 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.
|
|
*/
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <cassert>
|
|
#include <list>
|
|
#include <map>
|
|
#include <vector>
|
|
|
|
#include "bundler.h"
|
|
#include "bundler_generated.h"
|
|
#include "flatbuffers/idl.h"
|
|
#include "flatbuffers/util.h"
|
|
|
|
using namespace bluetooth;
|
|
using namespace dumpsys;
|
|
|
|
struct Opts opts;
|
|
|
|
/**
|
|
* Load a binary schema from persistent store using flatbuffer API.
|
|
*
|
|
* @param filename; Name of file to open and read.
|
|
* @param binary_schema: Backing store for flatbuffer binary schema data.
|
|
*
|
|
* @return: True if operation successful, false otherwise.
|
|
*/
|
|
bool LoadBinarySchema(const char* filename, std::string* binary_schema) {
|
|
assert(filename != nullptr);
|
|
assert(binary_schema != nullptr);
|
|
if (!flatbuffers::LoadFile(filename, helper::AsBinaryFile, binary_schema)) {
|
|
fprintf(stderr, "Unable to open binary flatbuffer schema file:%s\n", filename);
|
|
return false;
|
|
};
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Verify a binary schema using flatbuffer API.
|
|
*
|
|
* @param schema: Raw binary schema to verify
|
|
*
|
|
* @return: True if operation successful, false otherwise.
|
|
*/
|
|
bool VerifyBinarySchema(const std::vector<uint8_t>& raw_schema) {
|
|
flatbuffers::Verifier verifier(raw_schema.data(), raw_schema.size());
|
|
if (!reflection::VerifySchemaBuffer(verifier)) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Bundle a set of binary flatbuffer schema into the bundler database.
|
|
*
|
|
* @param builder; Flatbuffer builder
|
|
* @param filenames: Set of filenames to include in bundle
|
|
* @param vector_map: Filename to filedata mapping
|
|
*
|
|
* @return: True if operation successful, false otherwise.
|
|
*/
|
|
bool CreateBinarySchemaBundle(
|
|
flatbuffers::FlatBufferBuilder* builder,
|
|
const std::vector<std::string>& filenames,
|
|
std::vector<flatbuffers::Offset<BundledSchemaMap>>* vector_map,
|
|
std::list<std::string>* bundled_names) {
|
|
assert(builder != nullptr);
|
|
assert(vector_map != nullptr);
|
|
assert(bundled_names != nullptr);
|
|
|
|
for (auto filename : filenames) {
|
|
std::string string_schema;
|
|
if (!LoadBinarySchema(filename.c_str(), &string_schema)) {
|
|
fprintf(stderr, "Unable to load binary schema from filename:%s\n", filename.c_str());
|
|
return false;
|
|
}
|
|
std::vector<uint8_t> raw_schema(string_schema.begin(), string_schema.end());
|
|
if (!VerifyBinarySchema(raw_schema)) {
|
|
fprintf(stderr, "Failed verification on binary schema filename:%s\n", filename.c_str());
|
|
return false;
|
|
}
|
|
|
|
const reflection::Schema* schema = reflection::GetSchema(raw_schema.data());
|
|
if (schema->root_table() == nullptr) {
|
|
fprintf(stderr, "Unable to find root table for binary flatbuffer schema:%s\n", filename.c_str());
|
|
return false;
|
|
}
|
|
|
|
bundled_names->push_back(schema->root_table()->name()->str());
|
|
auto name = builder->CreateString(schema->root_table()->name()->str());
|
|
auto data = builder->CreateVector<uint8_t>(raw_schema.data(), raw_schema.size());
|
|
vector_map->push_back(CreateBundledSchemaMap(*builder, name, data));
|
|
|
|
if (opts.verbose) {
|
|
fprintf(stdout, "Bundled binary schema file:%s\n", schema->root_table()->name()->c_str());
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Write generated header file containing the bundled binary schema
|
|
* data and meta data
|
|
*
|
|
* @param data: Source file data.
|
|
* @param data_len: length of data
|
|
*/
|
|
void WriteHeaderFile(FILE* fp, const uint8_t* data, size_t data_len) {
|
|
assert(fp != nullptr);
|
|
std::string delim(kDefaultNamespaceDelim);
|
|
std::string ns_string(opts.ns_name);
|
|
std::vector<std::string> namespaces;
|
|
|
|
size_t start = 0;
|
|
size_t end = ns_string.find(delim);
|
|
while (end != std::string::npos) {
|
|
namespaces.push_back(ns_string.substr(start, end - start));
|
|
start = end + delim.size();
|
|
end = ns_string.find(delim, start);
|
|
}
|
|
if (start != 0 && start != std::string::npos) {
|
|
namespaces.push_back(ns_string.substr(start));
|
|
} else if (!ns_string.empty()) {
|
|
namespaces.push_back(ns_string);
|
|
}
|
|
|
|
std::string namespace_prefix;
|
|
for (const auto& name : namespaces) namespace_prefix += (name + '_');
|
|
|
|
fprintf(
|
|
fp,
|
|
"// Generated file by bluetooth_flatbuffer bundler\n"
|
|
"#pragma once\n"
|
|
"#include <sys/types.h>\n"
|
|
"#include <string>\n");
|
|
for_each(
|
|
namespaces.begin(), namespaces.end(), [fp](const std::string& s) { fprintf(fp, "namespace %s {\n", s.c_str()); });
|
|
fprintf(
|
|
fp,
|
|
"extern const unsigned char* data;\n"
|
|
"extern const size_t data_size;\n"
|
|
"const std::string& GetBundledSchemaData();\n");
|
|
for_each(namespaces.crbegin(), namespaces.crend(), [fp](const std::string& s) {
|
|
fprintf(fp, "} // namespace %s\n", s.c_str());
|
|
});
|
|
fprintf(
|
|
fp,
|
|
"namespace {\n"
|
|
"const unsigned char %sdata_[] = {\n",
|
|
namespace_prefix.c_str());
|
|
|
|
for (auto i = 0; i < data_len; i++) {
|
|
fprintf(fp, " 0x%02x", data[i]);
|
|
if (i != data_len - 1) {
|
|
fprintf(fp, ",");
|
|
}
|
|
if ((i + 1) % 16 == 0) {
|
|
fprintf(fp, "\n");
|
|
}
|
|
}
|
|
fprintf(fp, " };\n");
|
|
fprintf(
|
|
fp,
|
|
"const std::string %sstring_data_(%sdata_, %sdata_ + sizeof(%sdata_));\n",
|
|
namespace_prefix.c_str(),
|
|
namespace_prefix.c_str(),
|
|
namespace_prefix.c_str(),
|
|
namespace_prefix.c_str());
|
|
fprintf(fp, "} // namespace\n");
|
|
fprintf(fp, "const unsigned char* %s::data = %sdata_;\n", opts.ns_name, namespace_prefix.c_str());
|
|
fprintf(fp, "const size_t %s::data_size = %zu;\n", opts.ns_name, data_len);
|
|
fprintf(
|
|
fp,
|
|
"const std::string& %s::GetBundledSchemaData() { return %sstring_data_; }\n",
|
|
opts.ns_name,
|
|
namespace_prefix.c_str());
|
|
}
|
|
|
|
int ReadBundledSchema() {
|
|
const char* filename = opts.filename;
|
|
assert(filename != nullptr);
|
|
|
|
std::string flatfile_data;
|
|
if (!flatbuffers::LoadFile(filename, helper::AsBinaryFile, &flatfile_data)) {
|
|
fprintf(stderr, "Unable to load schema data file:%s\n", filename);
|
|
return -5;
|
|
}
|
|
|
|
auto bundle_schema = flatbuffers::GetRoot<BundledSchema>(flatfile_data.c_str());
|
|
const flatbuffers::Vector<flatbuffers::Offset<BundledSchemaMap>>* map = bundle_schema->map();
|
|
|
|
fprintf(stdout, "Bundle schema title:%s\n", bundle_schema->title()->c_str());
|
|
fprintf(stdout, "Bundle schema root_name:%s\n", bundle_schema->root_name()->c_str());
|
|
int cnt = 0;
|
|
for (auto it = map->cbegin(); it != map->cend(); ++it, cnt++) {
|
|
fprintf(stdout, " %d name:%s schema:%s\n", cnt, it->name()->c_str(), "schema");
|
|
}
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
int WriteBundledSchema() {
|
|
const char* filename = opts.filename;
|
|
assert(filename != nullptr);
|
|
|
|
const char* main_root_name = opts.main_root_name;
|
|
if (main_root_name == nullptr) {
|
|
fprintf(stderr, "Must specify the name of the main root name for this bundle\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
std::vector<std::string> bfbs_filenames;
|
|
for (int i = 0; i < opts.arg.c; i++) {
|
|
bfbs_filenames.push_back(std::string(opts.arg.v[i]));
|
|
}
|
|
if (bfbs_filenames.empty()) {
|
|
fprintf(stderr, "No bfbs files are specified to bundle\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
flatbuffers::FlatBufferBuilder builder(1024);
|
|
|
|
std::list<std::string> bundled_names;
|
|
std::vector<flatbuffers::Offset<BundledSchemaMap>> vector_map;
|
|
if (!CreateBinarySchemaBundle(&builder, bfbs_filenames, &vector_map, &bundled_names)) {
|
|
fprintf(stderr, "Unable to bundle schema bfbs files\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
if (std::find(bundled_names.begin(), bundled_names.end(), main_root_name) == bundled_names.end()) {
|
|
fprintf(stderr, "The main root name must match one of the bundled schema names\n");
|
|
fprintf(stderr, " main root name:%s\n", main_root_name);
|
|
for (auto name : bundled_names) {
|
|
fprintf(stderr, " bundled schema name:%s\n", name.c_str());
|
|
}
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
const char* title = opts.title;
|
|
auto schema_offset = CreateBundledSchemaDirect(builder, title, main_root_name, &vector_map);
|
|
builder.Finish(schema_offset);
|
|
|
|
std::string final_filename(opts.gen);
|
|
final_filename.append("/");
|
|
final_filename.append(filename);
|
|
if (!flatbuffers::SaveFile(
|
|
final_filename.c_str(), (const char*)builder.GetBufferPointer(), builder.GetSize(), helper::AsBinaryFile)) {
|
|
fprintf(stderr, "Unable to save file:%s\n", final_filename.c_str());
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
std::string header(opts.gen);
|
|
header += ("/" + std::string(opts.filename) + ".h");
|
|
FILE* fp = fopen(header.c_str(), "w+");
|
|
if (fp == nullptr) {
|
|
fprintf(stdout, "Unable to open for writing header file:%s\n", header.c_str());
|
|
return EXIT_FAILURE;
|
|
}
|
|
WriteHeaderFile(fp, builder.GetBufferPointer(), builder.GetSize());
|
|
fclose(fp);
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
int Usage(int argc, char** argv) {
|
|
fprintf(
|
|
stderr,
|
|
"Usage: %s [-r | -w] [-f <filename>] [-g <gen_out_path>] [-n <namespace> ] [-v] -m <main_root_name> <file.bfbs "
|
|
"...>\n",
|
|
argv[0]);
|
|
fprintf(stderr, " -r|-w : Read or write a dumpsys file\n");
|
|
fprintf(stderr, " -f : Filename bundled schema to read or write (default:%s)\n", kDefaultBundleDataFile);
|
|
fprintf(stderr, " -g : Generated file output path\n");
|
|
fprintf(stderr, " -n : Namespace to embed binary output bundle data source\n");
|
|
fprintf(stderr, " -m : Name of the main root of this bundle\n");
|
|
fprintf(stderr, " -v : Verbose printing mode\n");
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
void ParseArgs(int argc, char** argv) {
|
|
int opt;
|
|
int parsed_cnt = 1;
|
|
while ((opt = getopt(argc, argv, "f:g:m:n:rt:vw")) != -1) {
|
|
parsed_cnt++;
|
|
switch (opt) {
|
|
case 'f':
|
|
opts.filename = optarg;
|
|
parsed_cnt++;
|
|
break;
|
|
case 'g':
|
|
opts.gen = optarg;
|
|
parsed_cnt++;
|
|
break;
|
|
case 'm':
|
|
opts.main_root_name = optarg;
|
|
parsed_cnt++;
|
|
break;
|
|
case 'n':
|
|
opts.ns_name = optarg;
|
|
parsed_cnt++;
|
|
break;
|
|
case 'r':
|
|
opts.read = true;
|
|
break;
|
|
case 'w':
|
|
opts.write = true;
|
|
break;
|
|
case 't':
|
|
opts.title = optarg;
|
|
parsed_cnt++;
|
|
break;
|
|
case 'v':
|
|
opts.verbose = true;
|
|
break;
|
|
default:
|
|
exit(Usage(argc, argv));
|
|
break;
|
|
}
|
|
}
|
|
opts.arg.c = argc - parsed_cnt;
|
|
opts.arg.v = &argv[parsed_cnt];
|
|
}
|