Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 71ee51f9 authored by Chris Manton's avatar Chris Manton
Browse files

Initial Entry for bundler

Bug: 153703013
Test: atest --host bluetooth_test_gd
Tag: #gd-refactor
Change-Id: Idd6a2b864c7452f1f150918bd53b527b903353b7
parent bdc23836
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -206,8 +206,8 @@ cc_binary {
        ":BluetoothFacade_security_layer",
    ],
    generated_headers: [
        "BluetoothGeneratedPackets_h",
        "BluetoothFacadeGeneratedStub_h",
        "BluetoothGeneratedPackets_h",
        // Needed here to guarantee that generated zip file is created before
        // bluetooth_cert_tests.zip is packaged
        "BluetoothFacadeAndCertGeneratedStub_py",
@@ -221,11 +221,11 @@ cc_binary {
        "breakpad_client",
    ],
    shared_libs: [
        "libbacktrace",
        "libchrome",
        "libcrypto",
        "libgrpc++_unsecure",
        "libprotobuf-cpp-full",
        "libbacktrace",
    ],
    target: {
        android: {
+77 −0
Original line number Diff line number Diff line
filegroup {
    name: "BluetoothFlatbufferBundlerSources",
    srcs: [
        "bundler.cc",
        "main.cc",
    ],
}

filegroup {
    name: "BluetoothFlatbufferBundlerTestSources",
    srcs: [
        "bundler.cc",
        "test.cc",
    ],
}

genrule {
    name: "BluetoothGeneratedBundler_h",
    tools: [
        "flatc",
    ],
    cmd: "$(location flatc) -I packages/modules/Bluetooth/system/gd -b --schema -o $(genDir) --cpp $(in) ",
    srcs: [
        "bundler.fbs",
    ],
    out: [
        "bundler_generated.h", "bundler.bfbs",
    ],
}

cc_binary {
    name: "bluetooth_flatbuffer_bundler",
    host_supported: true,
    cflags: [
        "-Wall",
        "-Werror",
        "-Wno-unused-parameter",
        "-Wno-unused-variable",
    ],
    srcs: [
        ":BluetoothFlatbufferBundlerSources",
    ],
    generated_headers: [
        "BluetoothGeneratedBundler_h",
    ],
    shared_libs: [
        "libflatbuffers-cpp",
    ],
    sanitize: {
        address: true,
    },
}

cc_test {
    name: "bluetooth_flatbuffer_bundler_test",
    host_supported: true,
    cflags: [
        "-Wall",
        "-Werror",
        "-Wno-unused-parameter",
        "-Wno-unused-variable",
        "-DUSE_TEST_GENERATED",
    ],
    srcs: [
        ":BluetoothFlatbufferBundlerTestSources",
    ],
    shared_libs: [
        "libflatbuffers-cpp",
    ],
    sanitize: {
        address: true,
    },
    data: [
        "test.bfbs",
    ],
}
+309 −0
Original line number Diff line number Diff line
/*
 * 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 <map>
#include <vector>

#include "bundler.h"
#include "flatbuffers/idl.h"
#include "flatbuffers/util.h"

#ifdef USE_TEST_GENERATED
#include "test_generated.h"
#else
#include "bundler_generated.h"  // Production
#endif

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<const 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<BundleSchemaMap>>* vector_map) {
  assert(builder != nullptr);
  assert(vector_map != 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<const 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;
    }

    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(CreateBundleSchemaMap(*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));
  }

  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 data_[] = {\n");

  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 string_data_(data_, data_ + sizeof(data_));\n"
      "}  // namespace\n");
  fprintf(fp, "const unsigned char* %s::data = data_;\n", opts.ns_name);
  fprintf(fp, "const size_t %s::data_size = %zd;\n", opts.ns_name, data_len);
  fprintf(fp, "const std::string& %s::GetBundledSchemaData() { return string_data_; }\n", opts.ns_name);
}

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<BundleSchema>(flatfile_data.c_str());
  const flatbuffers::Vector<flatbuffers::Offset<BundleSchemaMap>>* map = bundle_schema->map();

  fprintf(stdout, "Bundle schema title:%s\n", bundle_schema->title()->c_str());
  fprintf(stdout, "Bundle schema root:%s\n", bundle_schema->root()->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 = opts.main_root;
  if (main_root == nullptr) {
    fprintf(stderr, "Must specify the name of the main root 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::vector<flatbuffers::Offset<BundleSchemaMap>> vector_map;
  if (!CreateBinarySchemaBundle(&builder, bfbs_filenames, &vector_map)) {
    fprintf(stderr, "Unable to bundle schema bfbs files\n");
    return EXIT_FAILURE;
  }

  auto title = "Bundled schema tables";
  auto schema_offset = CreateBundleSchemaDirect(builder, title, main_root, &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;
  }
  fprintf(
      stdout, "Successfully bundled %zd bfbs files into filename:%s\n", bfbs_filenames.size(), final_filename.c_str());

  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> <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:rvw")) != -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 = 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 'v':
        opts.verbose = true;
        break;
      default:
        exit(Usage(argc, argv));
        break;
    }
  }
  opts.arg.c = argc - parsed_cnt;
  opts.arg.v = &argv[parsed_cnt];
}
+19 −0
Original line number Diff line number Diff line
// Bundle Schema
//
// Describes a collection of binary flatbuffer schema.
//

namespace bluetooth.dumpsys;

table BundleSchemaMap {
    name:string;
    data:[ubyte];
}

table BundleSchema {
    title:string;
    root:string;
    map:[BundleSchemaMap];
}

root_type BundleSchema;
+68 −0
Original line number Diff line number Diff line
/*
 * 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.
 */
#pragma once

namespace {
constexpr char kDefaultBundleDataFile[] = "bundle_bfbs.bin";
constexpr char kDefaultNamespace[] = "";
constexpr char kDefaultNamespaceDelim[] = "::";
}  // namespace

struct Opts {
  bool verbose{false};
  bool read{false};
  bool write{false};
  const char* filename{kDefaultBundleDataFile};
  const char* gen{nullptr};
  const char* main_root{nullptr};
  const char* ns_name{kDefaultNamespace};
  struct {
    int c{0};
    char** v{nullptr};
  } arg;
};
extern Opts opts;

namespace {
namespace helper {  // Part of flatbuffers API
constexpr bool AsBinaryFile = true;
constexpr bool AsTextFile = false;
}  // namespace helper

}  // namespace

/**
 * Read and parse a previously generated bundle data file
 *
 **/
int ReadBundledSchema();

/**
 * Generate a bundle data file from the binary flatbuffer schema
 * files provided as input
 *
 **/
int WriteBundledSchema();

/**
 * Print tool usage options
 */
int Usage(int argc, char** argv);

/**
 * Parse tool usage options
 */
void ParseArgs(int argc, char** argv);
Loading