blob: 0a7ec9f63638bf0f16464b65cb95243e467f0958 [file] [log] [blame]
/*
* Copyright (C) 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 "GrpcVehicleServer.h"
#include <condition_variable>
#include <mutex>
#include <shared_mutex>
#include <android-base/logging.h>
#include <grpc++/grpc++.h>
#include "GarageModeServerSideHandler.h"
#include "PowerStateListener.h"
#include "VehicleServer.grpc.pb.h"
#include "VehicleServer.pb.h"
#include "vhal_v2_0/DefaultConfig.h"
#include "vhal_v2_0/ProtoMessageConverter.h"
namespace android {
namespace hardware {
namespace automotive {
namespace vehicle {
namespace V2_0 {
namespace impl {
class GrpcVehicleServerImpl : public GrpcVehicleServer, public vhal_proto::VehicleServer::Service {
public:
explicit GrpcVehicleServerImpl(const VirtualizedVhalServerInfo& serverInfo)
: mServiceAddr(serverInfo.getServerUri()),
mGarageModeHandler(makeGarageModeServerSideHandler(this, &mValueObjectPool,
serverInfo.powerStateMarkerFilePath)),
mPowerStateListener(serverInfo.powerStateSocket, serverInfo.powerStateMarkerFilePath) {
setValuePool(&mValueObjectPool);
}
// method from GrpcVehicleServer
GrpcVehicleServer& Start() override;
void Wait() override;
GrpcVehicleServer& Stop() override;
uint32_t NumOfActivePropertyValueStream() override;
// methods from IVehicleServer
void onPropertyValueFromCar(const VehiclePropValue& value, bool updateStatus) override;
StatusCode onSetProperty(const VehiclePropValue& value, bool updateStatus) override;
// methods from vhal_proto::VehicleServer::Service
::grpc::Status GetAllPropertyConfig(
::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
::grpc::ServerWriter<vhal_proto::VehiclePropConfig>* stream) override;
::grpc::Status SetProperty(::grpc::ServerContext* context,
const vhal_proto::WrappedVehiclePropValue* wrappedPropValue,
vhal_proto::VehicleHalCallStatus* status) override;
::grpc::Status StartPropertyValuesStream(
::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
::grpc::ServerWriter<vhal_proto::WrappedVehiclePropValue>* stream) override;
private:
// We keep long-lasting connection for streaming the prop values.
// For us, each connection can be represented as a function to send the new value, and
// an ID to identify this connection
struct ConnectionDescriptor {
using ValueWriterType = std::function<bool(const vhal_proto::WrappedVehiclePropValue&)>;
explicit ConnectionDescriptor(ValueWriterType&& value_writer)
: mValueWriter(std::move(value_writer)),
mConnectionID(CONNECTION_ID_COUNTER.fetch_add(1)) {}
ConnectionDescriptor(const ConnectionDescriptor&) = delete;
ConnectionDescriptor& operator=(const ConnectionDescriptor&) = delete;
// This move constructor is NOT THREAD-SAFE, which means it cannot be moved
// while using. Since the connection descriptors are pretected by mConnectionMutex
// then we are fine here
ConnectionDescriptor(ConnectionDescriptor&& cd)
: mValueWriter(std::move(cd.mValueWriter)),
mConnectionID(cd.mConnectionID),
mIsAlive(cd.mIsAlive.load()) {
cd.mIsAlive.store(false);
}
ValueWriterType mValueWriter;
uint64_t mConnectionID;
std::atomic<bool> mIsAlive{true};
static std::atomic<uint64_t> CONNECTION_ID_COUNTER;
};
std::string mServiceAddr;
std::unique_ptr<::grpc::Server> mServer{nullptr};
VehiclePropValuePool mValueObjectPool;
std::unique_ptr<GarageModeServerSideHandler> mGarageModeHandler;
PowerStateListener mPowerStateListener;
std::thread mPowerStateListenerThread{};
mutable std::shared_mutex mConnectionMutex;
mutable std::shared_mutex mWriterMutex;
std::list<ConnectionDescriptor> mValueStreamingConnections;
};
std::atomic<uint64_t> GrpcVehicleServerImpl::ConnectionDescriptor::CONNECTION_ID_COUNTER = 0;
static std::shared_ptr<::grpc::ServerCredentials> getServerCredentials() {
// TODO(chenhaosjtuacm): get secured credentials here
return ::grpc::InsecureServerCredentials();
}
GrpcVehicleServerPtr makeGrpcVehicleServer(const VirtualizedVhalServerInfo& serverInfo) {
return std::make_unique<GrpcVehicleServerImpl>(serverInfo);
}
GrpcVehicleServer& GrpcVehicleServerImpl::Start() {
if (mServer) {
LOG(WARNING) << __func__ << ": GrpcVehicleServer has already started.";
return *this;
}
::grpc::ServerBuilder builder;
builder.RegisterService(this);
builder.AddListeningPort(mServiceAddr, getServerCredentials());
mServer = builder.BuildAndStart();
CHECK(mServer) << __func__ << ": failed to create the GRPC server, "
<< "please make sure the configuration and permissions are correct";
mPowerStateListenerThread = std::thread([this]() { mPowerStateListener.Listen(); });
return *this;
}
void GrpcVehicleServerImpl::Wait() {
if (mServer) {
mServer->Wait();
}
if (mPowerStateListenerThread.joinable()) {
mPowerStateListenerThread.join();
}
mPowerStateListenerThread = {};
mServer.reset();
}
GrpcVehicleServer& GrpcVehicleServerImpl::Stop() {
if (!mServer) {
LOG(WARNING) << __func__ << ": GrpcVehicleServer has not started.";
return *this;
}
mServer->Shutdown();
mPowerStateListener.Stop();
return *this;
}
uint32_t GrpcVehicleServerImpl::NumOfActivePropertyValueStream() {
std::shared_lock read_lock(mConnectionMutex);
return mValueStreamingConnections.size();
}
void GrpcVehicleServerImpl::onPropertyValueFromCar(const VehiclePropValue& value,
bool updateStatus) {
vhal_proto::WrappedVehiclePropValue wrappedPropValue;
proto_msg_converter::toProto(wrappedPropValue.mutable_value(), value);
wrappedPropValue.set_update_status(updateStatus);
std::shared_lock read_lock(mConnectionMutex);
bool has_terminated_connections = 0;
for (auto& connection : mValueStreamingConnections) {
auto writeOK = connection.mValueWriter(wrappedPropValue);
if (!writeOK) {
LOG(ERROR) << __func__ << ": Server Write failed, connection lost. ID: "
<< connection.mConnectionID;
has_terminated_connections = true;
connection.mIsAlive.store(false);
}
}
if (!has_terminated_connections) {
return;
}
read_lock.unlock();
std::unique_lock write_lock(mConnectionMutex);
for (auto itr = mValueStreamingConnections.begin(); itr != mValueStreamingConnections.end();) {
if (!itr->mIsAlive.load()) {
itr = mValueStreamingConnections.erase(itr);
} else {
++itr;
}
}
}
StatusCode GrpcVehicleServerImpl::onSetProperty(const VehiclePropValue& value, bool updateStatus) {
if (value.prop == AP_POWER_STATE_REPORT &&
value.value.int32Values[0] == toInt(VehicleApPowerStateReport::SHUTDOWN_POSTPONE)) {
mGarageModeHandler->HandleHeartbeat();
}
return GrpcVehicleServer::onSetProperty(value, updateStatus);
}
::grpc::Status GrpcVehicleServerImpl::GetAllPropertyConfig(
::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
::grpc::ServerWriter<vhal_proto::VehiclePropConfig>* stream) {
auto configs = onGetAllPropertyConfig();
for (auto& config : configs) {
vhal_proto::VehiclePropConfig protoConfig;
proto_msg_converter::toProto(&protoConfig, config);
if (!stream->Write(protoConfig)) {
return ::grpc::Status(::grpc::StatusCode::ABORTED, "Connection lost.");
}
}
return ::grpc::Status::OK;
}
::grpc::Status GrpcVehicleServerImpl::SetProperty(
::grpc::ServerContext* context, const vhal_proto::WrappedVehiclePropValue* wrappedPropValue,
vhal_proto::VehicleHalCallStatus* status) {
VehiclePropValue value;
proto_msg_converter::fromProto(&value, wrappedPropValue->value());
auto set_status = static_cast<int32_t>(onSetProperty(value, wrappedPropValue->update_status()));
if (!vhal_proto::VehicleHalStatusCode_IsValid(set_status)) {
return ::grpc::Status(::grpc::StatusCode::INTERNAL, "Unknown status code");
}
status->set_status_code(static_cast<vhal_proto::VehicleHalStatusCode>(set_status));
return ::grpc::Status::OK;
}
::grpc::Status GrpcVehicleServerImpl::StartPropertyValuesStream(
::grpc::ServerContext* context, const ::google::protobuf::Empty* request,
::grpc::ServerWriter<vhal_proto::WrappedVehiclePropValue>* stream) {
std::mutex terminateMutex;
std::condition_variable terminateCV;
std::unique_lock<std::mutex> terminateLock(terminateMutex);
bool terminated{false};
auto callBack = [stream, &terminateMutex, &terminateCV, &terminated,
this](const vhal_proto::WrappedVehiclePropValue& value) {
std::unique_lock lock(mWriterMutex);
if (!stream->Write(value)) {
std::unique_lock<std::mutex> terminateLock(terminateMutex);
terminated = true;
terminateLock.unlock();
terminateCV.notify_all();
return false;
}
return true;
};
// Register connection
std::unique_lock lock(mConnectionMutex);
auto& conn = mValueStreamingConnections.emplace_back(std::move(callBack));
lock.unlock();
// Never stop until connection lost
terminateCV.wait(terminateLock, [&terminated]() { return terminated; });
LOG(ERROR) << __func__ << ": Stream lost, ID : " << conn.mConnectionID;
return ::grpc::Status(::grpc::StatusCode::ABORTED, "Connection lost.");
}
} // namespace impl
} // namespace V2_0
} // namespace vehicle
} // namespace automotive
} // namespace hardware
} // namespace android