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

Commit 8019a7e2 authored by Chienyuan's avatar Chienyuan Committed by Hansong Zhang
Browse files

HAL: Add facade for fetching incoming HCI packets

Adding the event stream primitives. General event stream request is
defined in facade/common.proto. "grpc/grpc_event_stream.h" provides
helper class templates GrpcEventStreamCallback and GrpcEventStream to
help user handle EventStream request without writing boilerplate code.

Test: cert/run_cert.sh
Change-Id: I00b51fc7b1faefb7c97cc647876f41854872a415
parent 988b802b
Loading
Loading
Loading
Loading
+9 −28
Original line number Diff line number Diff line
@@ -153,34 +153,6 @@ cc_binary {
    },
}

cc_test {
    name: "bluetooth_cert_test",
    defaults: [
        "gd_defaults",
    ],
    host_supported: true,
    srcs: [
        ":BluetoothCertCppClient_hci_hal",
    ],
    static_libs : [
        "libbluetooth_gd",
    ],
    shared_libs: [
        "libgrpc++_unsecure",
        "libprotobuf-cpp-full",
    ],
    generated_headers: [
        "BluetoothGeneratedPackets_h",
        "BluetoothCertFacadeGeneratedStub_h",
    ],
    generated_sources: [
        "BluetoothCertFacadeGeneratedStub_cc",
    ],
    sanitize: {
        address: true,
    },
}

cc_test {
    name: "bluetooth_test_gd",
    test_suites: ["device-tests"],
@@ -285,6 +257,7 @@ genrule {
filegroup {
    name: "BluetoothCertFacadeProto",
    srcs: [
        "facade/common.proto",
        "hal/facade/api.proto",
    ],
}
@@ -300,6 +273,8 @@ genrule {
        ":BluetoothCertFacadeProto",
    ],
    out: [
        "facade/common.grpc.pb.h",
        "facade/common.pb.h",
        "hal/facade/api.grpc.pb.h",
        "hal/facade/api.pb.h",
    ],
@@ -316,6 +291,8 @@ genrule {
        ":BluetoothCertFacadeProto",
    ],
    out: [
        "facade/common.grpc.pb.cc",
        "facade/common.pb.cc",
        "hal/facade/api.grpc.pb.cc",
        "hal/facade/api.pb.cc",
    ],
@@ -329,6 +306,7 @@ genrule {
    ],
    cmd: "$(location aprotoc) -Ipackages/modules/Bluetooth/system/gd -Iexternal/protobuf/src --plugin=protoc-gen-grpc=$(location protoc-gen-grpc-python-plugin) $(in) --grpc_out=$(genDir) --python_out=$(genDir); " +
        "touch $(genDir)/__init__.py; " +
        "touch $(genDir)/facade/__init__.py; " +
        "touch $(genDir)/hal/__init__.py; " +
        "touch $(genDir)/hal/facade/__init__.py; ",
    srcs: [
@@ -336,6 +314,9 @@ genrule {
    ],
    out: [
        "__init__.py",
        "facade/__init__.py",
        "facade/common_pb2_grpc.py",
        "facade/common_pb2.py",
        "hal/__init__.py",
        "hal/facade/__init__.py",
        "hal/facade/api_pb2_grpc.py",
+20 −0
Original line number Diff line number Diff line
syntax = "proto3";

package bluetooth.facade;

enum EventSubscriptionMode {
  UNCHANGED = 0;
  SUBSCRIBE = 1;
  UNSUBSCRIBE = 2;
}

enum EventFetchMode {
  NONE = 0;
  ALL_CURRENT = 1;
  AT_LEAST_ONE = 2;
}

message EventStreamRequest {
  EventSubscriptionMode subscription_mode = 1;
  EventFetchMode fetch_mode = 2;
}
+91 −0
Original line number Diff line number Diff line
/*
 * Copyright 2019 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

#include <grpc++/grpc++.h>

#include "common/blocking_queue.h"
#include "facade/common.pb.h"
#include "os/log.h"

namespace bluetooth {
namespace grpc {

template <typename RES, typename EVENT>
class GrpcEventStreamCallback {
 public:
  virtual ~GrpcEventStreamCallback() = default;
  virtual void OnSubscribe() {}
  virtual void OnUnsubscribe() {}
  virtual void OnWriteResponse(RES* response, const EVENT& event) = 0;
};

template <typename RES, typename EVENT>
class GrpcEventStream {
 public:
  explicit GrpcEventStream(GrpcEventStreamCallback<RES, EVENT>* callback) : callback_(callback) {}

  void OnIncomingEvent(const EVENT& event) {
    if (subscribed_) {
      event_queue_.push(event);
    }
  }

  ::grpc::Status HandleRequest(::grpc::ServerContext* context, const ::bluetooth::facade::EventStreamRequest* request,
                               ::grpc::ServerWriter<RES>* writer) {
    ::bluetooth::facade::EventSubscriptionMode subscription_mode = request->subscription_mode();
    ::bluetooth::facade::EventFetchMode fetch_mode = request->fetch_mode();

    if (subscription_mode == ::bluetooth::facade::SUBSCRIBE) {
      callback_->OnSubscribe();
      subscribed_ = true;
    }

    if (fetch_mode == ::bluetooth::facade::AT_LEAST_ONE) {
      RES response;
      EVENT event = event_queue_.take();
      callback_->OnWriteResponse(&response, event);
      writer->Write(response);
    }

    // fetch all current remaining items and append to AT_LEAST_ONE query if present
    if (fetch_mode == ::bluetooth::facade::ALL_CURRENT || fetch_mode == ::bluetooth::facade::AT_LEAST_ONE) {
      while (!event_queue_.empty()) {
        RES response;
        EVENT event = event_queue_.take();
        callback_->OnWriteResponse(&response, event);
        writer->Write(response);
      }
    }

    if (subscription_mode == ::bluetooth::facade::UNSUBSCRIBE) {
      subscribed_ = false;
      event_queue_.clear();
      callback_->OnUnsubscribe();
    }

    return ::grpc::Status::OK;
  }

 private:
  common::BlockingQueue<EVENT> event_queue_;
  GrpcEventStreamCallback<RES, EVENT>* callback_;
  bool subscribed_ = false;
};

}  // namespace grpc
}  // namespace bluetooth
+0 −7
Original line number Diff line number Diff line
@@ -39,10 +39,3 @@ filegroup {
        "facade/facade.cc",
    ],
}

filegroup {
    name: "BluetoothCertCppClient_hci_hal",
    srcs: [
        "cert/simple_hal_test.cc",
    ],
}
+0 −91
Original line number Diff line number Diff line
/*
 * Copyright 2019 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 <grpc++/grpc++.h>
#include <gtest/gtest.h>

#include <chrono>
#include <string>
#include <thread>

#include "hal/facade/api.grpc.pb.h"
#include "hci/hci_packets.h"
#include "os/log.h"

using grpc::ClientContext;

namespace bluetooth {
namespace hal {
namespace cert {

using ::bluetooth::hal::facade::HciCmdPacket;
using ::bluetooth::hal::facade::HciEvtPacket;
using ::bluetooth::hal::facade::HciTransportation;
using ::bluetooth::hal::facade::LoopbackModeSettings;

class HalAdapterCertTest : public ::testing::Test {
 protected:
  void SetUp() override {
    int port = 8899;
    std::string channel = "localhost:" + std::to_string(port);
    stub_ = HciTransportation::NewStub(grpc::CreateChannel(channel, grpc::InsecureChannelCredentials()));
  }
  void TearDown() override {
    stub_.reset();
  }

  std::unique_ptr<HciTransportation::Stub> stub_;
};

TEST_F(HalAdapterCertTest, enable_loopback_mode) {
  ClientContext set_loopback_mode_context;
  LoopbackModeSettings settings;
  settings.set_enable(true);
  ::google::protobuf::Empty empty;
  ::grpc::Status status = stub_->SetLoopbackMode(&set_loopback_mode_context, settings, &empty);
  EXPECT_EQ(status.ok(), true);

  ClientContext register_hci_evt_context;

  auto reader = stub_->RegisterHciEvt(&register_hci_evt_context, empty);

  auto packet = hci::DisconnectBuilder::Create(2, hci::DisconnectReason::PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED);
  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
  hci::BitInserter it(*packet_bytes);
  packet->Serialize(it);

  std::string payload(packet_bytes->begin(), packet_bytes->end());

  ClientContext send_hci_cmd_context;
  HciCmdPacket cmd;
  cmd.set_payload(payload);
  status = stub_->SendHciCmd(&send_hci_cmd_context, cmd, &empty);
  EXPECT_EQ(status.ok(), true);

  HciEvtPacket received_packet;
  reader->Read(&received_packet);

  ClientContext unregister_hci_evt_context;

  status = stub_->UnregisterHciEvt(&unregister_hci_evt_context, empty, &empty);
  EXPECT_EQ(status.ok(), true);

  //  EXPECT_EQ(reader->Read(&received_packet), false);
}

}  // namespace cert
}  // namespace hal
}  // namespace bluetooth
Loading