363 lines
18 KiB
C++
363 lines
18 KiB
C++
/*
|
|
* This file is part of openauto project.
|
|
* Copyright (C) 2018 f1x.studio (Michal Szwaj)
|
|
*
|
|
* openauto is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
|
|
* openauto is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with openauto. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <aasdk/Channel/Control/ControlServiceChannel.hpp>
|
|
#include <f1x/openauto/autoapp/Service/AndroidAutoEntity.hpp>
|
|
#include <f1x/openauto/Common/Log.hpp>
|
|
|
|
namespace f1x {
|
|
namespace openauto {
|
|
namespace autoapp {
|
|
namespace service {
|
|
|
|
AndroidAutoEntity::AndroidAutoEntity(boost::asio::io_service &ioService,
|
|
aasdk::messenger::ICryptor::Pointer cryptor,
|
|
aasdk::transport::ITransport::Pointer transport,
|
|
aasdk::messenger::IMessenger::Pointer messenger,
|
|
configuration::IConfiguration::Pointer configuration,
|
|
ServiceList serviceList,
|
|
IPinger::Pointer pinger)
|
|
: strand_(ioService), cryptor_(std::move(cryptor)), transport_(std::move(transport)),
|
|
messenger_(std::move(messenger)), controlServiceChannel_(
|
|
std::make_shared<aasdk::channel::control::ControlServiceChannel>(strand_, messenger_)),
|
|
configuration_(std::move(configuration)), serviceList_(std::move(serviceList)),
|
|
pinger_(std::move(pinger)), eventHandler_(nullptr) {
|
|
}
|
|
|
|
AndroidAutoEntity::~AndroidAutoEntity() {
|
|
OPENAUTO_LOG(debug) << "[AndroidAutoEntity] destroy.";
|
|
}
|
|
|
|
void AndroidAutoEntity::start(IAndroidAutoEntityEventHandler &eventHandler) {
|
|
strand_.dispatch([this, self = this->shared_from_this(), eventHandler = &eventHandler]() {
|
|
OPENAUTO_LOG(info) << "[AndroidAutoEntity] start()";
|
|
|
|
eventHandler_ = eventHandler;
|
|
std::for_each(serviceList_.begin(), serviceList_.end(), std::bind(&IService::start, std::placeholders::_1));
|
|
|
|
auto versionRequestPromise = aasdk::channel::SendPromise::defer(strand_);
|
|
versionRequestPromise->then([]() { }, std::bind(&AndroidAutoEntity::onChannelError, this->shared_from_this(),
|
|
std::placeholders::_1));
|
|
|
|
OPENAUTO_LOG(debug) << "[AndroidAutoEntity] Send Version Request.";
|
|
controlServiceChannel_->sendVersionRequest(std::move(versionRequestPromise));
|
|
controlServiceChannel_->receive(this->shared_from_this());
|
|
});
|
|
}
|
|
|
|
void AndroidAutoEntity::stop() {
|
|
strand_.dispatch([this, self = this->shared_from_this()]() {
|
|
OPENAUTO_LOG(info) << "[AndroidAutoEntity] stop()";
|
|
|
|
try {
|
|
eventHandler_ = nullptr;
|
|
std::for_each(serviceList_.begin(), serviceList_.end(),
|
|
std::bind(&IService::stop, std::placeholders::_1));
|
|
|
|
messenger_->stop();
|
|
transport_->stop();
|
|
cryptor_->deinit();
|
|
} catch (...) {
|
|
OPENAUTO_LOG(error) << "[AndroidAutoEntity] stop() - exception when stopping.";
|
|
}
|
|
});
|
|
}
|
|
|
|
void AndroidAutoEntity::pause() {
|
|
strand_.dispatch([this, self = this->shared_from_this()]() {
|
|
OPENAUTO_LOG(info) << "[AndroidAutoEntity] pause()";
|
|
|
|
try {
|
|
std::for_each(serviceList_.begin(), serviceList_.end(),
|
|
std::bind(&IService::pause, std::placeholders::_1));
|
|
} catch (...) {
|
|
OPENAUTO_LOG(error) << "[AndroidAutoEntity] pause() - exception when pausing.";
|
|
}
|
|
});
|
|
}
|
|
|
|
void AndroidAutoEntity::resume() {
|
|
strand_.dispatch([this, self = this->shared_from_this()]() {
|
|
OPENAUTO_LOG(info) << "[AndroidAutoEntity] resume()";
|
|
|
|
try {
|
|
std::for_each(serviceList_.begin(), serviceList_.end(),
|
|
std::bind(&IService::resume, std::placeholders::_1));
|
|
} catch (...) {
|
|
OPENAUTO_LOG(error) << "[AndroidAutoEntity] resume() exception when resuming.";
|
|
}
|
|
});
|
|
}
|
|
|
|
void AndroidAutoEntity::onVersionResponse(uint16_t majorCode, uint16_t minorCode,
|
|
aap_protobuf::shared::MessageStatus status) {
|
|
OPENAUTO_LOG(info) << "[AndroidAutoEntity] onVersionResponse()";
|
|
OPENAUTO_LOG(info) << "[AndroidAutoEntity] Version Received: " << majorCode << "." << minorCode
|
|
<< ", with status: " << status;
|
|
|
|
if (status == aap_protobuf::shared::MessageStatus::STATUS_NO_COMPATIBLE_VERSION) {
|
|
OPENAUTO_LOG(error) << "[AndroidAutoEntity] Version mismatch.";
|
|
this->triggerQuit();
|
|
} else {
|
|
OPENAUTO_LOG(debug) << "[AndroidAutoEntity] Version matches.";
|
|
|
|
try {
|
|
OPENAUTO_LOG(info) << "[AndroidAutoEntity] Beginning SSL handshake.";
|
|
cryptor_->doHandshake();
|
|
|
|
auto handshakePromise = aasdk::channel::SendPromise::defer(strand_);
|
|
handshakePromise->then([]() { }, std::bind(&AndroidAutoEntity::onChannelError, this->shared_from_this(),
|
|
std::placeholders::_1));
|
|
controlServiceChannel_->sendHandshake(cryptor_->readHandshakeBuffer(), std::move(handshakePromise));
|
|
controlServiceChannel_->receive(this->shared_from_this());
|
|
}
|
|
catch (const aasdk::error::Error &e) {
|
|
OPENAUTO_LOG(info) << "[AndroidAutoEntity] Handshake Error.";
|
|
this->onChannelError(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
void AndroidAutoEntity::onHandshake(const aasdk::common::DataConstBuffer &payload) {
|
|
OPENAUTO_LOG(info) << "[AndroidAutoEntity] onHandshake()";
|
|
OPENAUTO_LOG(debug) << "[AndroidAutoEntity] Payload size: " << payload.size;
|
|
|
|
try {
|
|
cryptor_->writeHandshakeBuffer(payload);
|
|
|
|
if (!cryptor_->doHandshake()) {
|
|
OPENAUTO_LOG(info) << "[AndroidAutoEntity] Re-attempting handshake.";
|
|
|
|
auto handshakePromise = aasdk::channel::SendPromise::defer(strand_);
|
|
handshakePromise->then([]() {}, std::bind(&AndroidAutoEntity::onChannelError, this->shared_from_this(),
|
|
std::placeholders::_1));
|
|
controlServiceChannel_->sendHandshake(cryptor_->readHandshakeBuffer(), std::move(handshakePromise));
|
|
} else {
|
|
OPENAUTO_LOG(info) << "[AndroidAutoEntity] Handshake completed.";
|
|
|
|
aap_protobuf::service::control::message::AuthResponse authCompleteIndication;
|
|
authCompleteIndication.set_status(aap_protobuf::shared::MessageStatus::STATUS_SUCCESS);
|
|
|
|
auto authCompletePromise = aasdk::channel::SendPromise::defer(strand_);
|
|
authCompletePromise->then([]() {}, std::bind(&AndroidAutoEntity::onChannelError, this->shared_from_this(),
|
|
std::placeholders::_1));
|
|
controlServiceChannel_->sendAuthComplete(authCompleteIndication, std::move(authCompletePromise));
|
|
}
|
|
|
|
controlServiceChannel_->receive(this->shared_from_this());
|
|
}
|
|
catch (const aasdk::error::Error &e) {
|
|
OPENAUTO_LOG(error) << "[AndroidAutoEntity] Error during handshake";
|
|
this->onChannelError(e);
|
|
}
|
|
}
|
|
|
|
void AndroidAutoEntity::onServiceDiscoveryRequest(
|
|
const aap_protobuf::service::control::message::ServiceDiscoveryRequest &request) {
|
|
OPENAUTO_LOG(info) << "[AndroidAutoEntity] onServiceDiscoveryRequest()";
|
|
OPENAUTO_LOG(debug) << "[AndroidAutoEntity] Type: " << request.label_text() << ", Model: "
|
|
<< request.device_name();
|
|
|
|
aap_protobuf::service::control::message::ServiceDiscoveryResponse serviceDiscoveryResponse;
|
|
serviceDiscoveryResponse.mutable_channels()->Reserve(256);
|
|
serviceDiscoveryResponse.set_driver_position(aap_protobuf::service::control::message::DriverPosition::DRIVER_POSITION_RIGHT);
|
|
serviceDiscoveryResponse.set_can_play_native_media_during_vr(false);
|
|
serviceDiscoveryResponse.set_display_name("Crankshaft-NG");
|
|
serviceDiscoveryResponse.set_probe_for_support(false);
|
|
|
|
auto *connectionConfiguration = serviceDiscoveryResponse.mutable_connection_configuration();
|
|
|
|
auto *pingConfiguration = connectionConfiguration->mutable_ping_configuration();
|
|
pingConfiguration->set_tracked_ping_count(5);
|
|
pingConfiguration->set_timeout_ms(3000);
|
|
pingConfiguration->set_interval_ms(1000);
|
|
pingConfiguration->set_high_latency_threshold_ms(200);
|
|
|
|
|
|
auto *headUnitInfo = serviceDiscoveryResponse.mutable_headunit_info();
|
|
|
|
serviceDiscoveryResponse.set_display_name("Crankshaft-NG");
|
|
headUnitInfo->set_make("Crankshaft");
|
|
headUnitInfo->set_model("Universal");
|
|
headUnitInfo->set_year("2018");
|
|
headUnitInfo->set_vehicle_id("2024110822150988");
|
|
headUnitInfo->set_head_unit_make("f1x");
|
|
headUnitInfo->set_head_unit_model("Crankshaft-NG Autoapp");
|
|
headUnitInfo->set_head_unit_software_build("1");
|
|
headUnitInfo->set_head_unit_software_version("1.0");
|
|
|
|
std::for_each(serviceList_.begin(), serviceList_.end(),
|
|
std::bind(&IService::fillFeatures, std::placeholders::_1, std::ref(serviceDiscoveryResponse)));
|
|
|
|
auto promise = aasdk::channel::SendPromise::defer(strand_);
|
|
promise->then([]() { },
|
|
std::bind(&AndroidAutoEntity::onChannelError, this->shared_from_this(), std::placeholders::_1));
|
|
controlServiceChannel_->sendServiceDiscoveryResponse(serviceDiscoveryResponse, std::move(promise));
|
|
controlServiceChannel_->receive(this->shared_from_this());
|
|
}
|
|
|
|
void AndroidAutoEntity::onAudioFocusRequest(
|
|
const aap_protobuf::service::control::message::AudioFocusRequest &request) {
|
|
OPENAUTO_LOG(info) << "[AndroidAutoEntity] onAudioFocusRequest()";
|
|
OPENAUTO_LOG(debug) << "[AndroidAutoEntity] AudioFocusRequestType received: "
|
|
<< AudioFocusRequestType_Name(request.audio_focus_type());
|
|
|
|
/*
|
|
* When the MD starts playing music for example, it sends a gain request. The HU replies:
|
|
* STATE_GAIN - no restrictions
|
|
* STATE_GAIN_MEDIA_ONLY when using a guidance channel
|
|
* STATE_LOSS when vehicle is playing high priority sound after stopping native media (ie USB, RADIO)
|
|
*
|
|
* When HU starts playing music, we should send a STATE LOSS to stop MD music and guidance.
|
|
*/
|
|
|
|
// If release, we should stop all playback
|
|
// MD wants to play a sound, get a notifiation regarding GAIN
|
|
// HU grants focus - to enable MD to send audio over both MEDIA and GUIDANCE channels.
|
|
// MD can then play guidance over the MEDIA or GUIDANCE streams
|
|
// HU should send STATE_LOSS to stop MD playing (ie if user starts radio player)
|
|
aap_protobuf::service::control::message::AudioFocusStateType audioFocusStateType =
|
|
request.audio_focus_type() ==
|
|
aap_protobuf::service::control::message::AudioFocusRequestType::AUDIO_FOCUS_RELEASE
|
|
? aap_protobuf::service::control::message::AudioFocusStateType::AUDIO_FOCUS_STATE_LOSS
|
|
: aap_protobuf::service::control::message::AudioFocusStateType::AUDIO_FOCUS_STATE_GAIN;
|
|
|
|
OPENAUTO_LOG(debug) << "[AndroidAutoEntity] AudioFocusStateType determined: "
|
|
<< AudioFocusStateType_Name(audioFocusStateType);
|
|
|
|
aap_protobuf::service::control::message::AudioFocusNotification response;
|
|
response.set_focus_state(audioFocusStateType);
|
|
|
|
auto promise = aasdk::channel::SendPromise::defer(strand_);
|
|
promise->then([]() { },
|
|
[capture0 = this->shared_from_this()](auto && PH1) { capture0->onChannelError(std::forward<decltype(PH1)>(PH1)); });
|
|
controlServiceChannel_->sendAudioFocusResponse(response, std::move(promise));
|
|
controlServiceChannel_->receive(this->shared_from_this());
|
|
}
|
|
|
|
void AndroidAutoEntity::onByeByeRequest(
|
|
const aap_protobuf::service::control::message::ByeByeRequest &request) {
|
|
OPENAUTO_LOG(info) << "[AndroidAutoEntity] onByeByeRequest()";
|
|
OPENAUTO_LOG(debug) << "[AndroidAutoEntity] Reason received: " << request.reason();
|
|
|
|
aap_protobuf::service::control::message::ByeByeResponse response;
|
|
auto promise = aasdk::channel::SendPromise::defer(strand_);
|
|
promise->then(std::bind(&AndroidAutoEntity::triggerQuit, this->shared_from_this()),
|
|
std::bind(&AndroidAutoEntity::onChannelError, this->shared_from_this(), std::placeholders::_1));
|
|
|
|
controlServiceChannel_->sendShutdownResponse(response, std::move(promise));
|
|
}
|
|
|
|
void AndroidAutoEntity::onByeByeResponse(
|
|
const aap_protobuf::service::control::message::ByeByeResponse &response) {
|
|
OPENAUTO_LOG(info) << "[AndroidAutoEntity] onByeByeResponse()";
|
|
this->triggerQuit();
|
|
}
|
|
|
|
void AndroidAutoEntity::onNavigationFocusRequest(
|
|
const aap_protobuf::service::control::message::NavFocusRequestNotification &request) {
|
|
OPENAUTO_LOG(info) << "[AndroidAutoEntity] onNavigationFocusRequest()";
|
|
OPENAUTO_LOG(debug) << "[AndroidAutoEntity] NavFocusRequestNotification type received: " << NavFocusType_Name(request.focus_type());
|
|
|
|
/*
|
|
* If the MD sends NAV_FOCUS_PROJECTED in the request, we should stop any local navigation on the HU and grant NAV_FOCUS_NATIVE in the response.
|
|
* If the HU starts its own Nav, we should send NAV_FOCUS_NATIVE.
|
|
*
|
|
* For now, this is fine to be hardcoded as OpenAuto does not provide any local navigation, only that provided through Android Auto.
|
|
*/
|
|
aap_protobuf::service::control::message::NavFocusNotification response;
|
|
response.set_focus_type(
|
|
aap_protobuf::service::control::message::NavFocusType::NAV_FOCUS_PROJECTED);
|
|
|
|
auto promise = aasdk::channel::SendPromise::defer(strand_);
|
|
promise->then([]() {},
|
|
std::bind(&AndroidAutoEntity::onChannelError, this->shared_from_this(), std::placeholders::_1));
|
|
controlServiceChannel_->sendNavigationFocusResponse(response, std::move(promise));
|
|
controlServiceChannel_->receive(this->shared_from_this());
|
|
}
|
|
|
|
void AndroidAutoEntity::onBatteryStatusNotification(const aap_protobuf::service::control::message::BatteryStatusNotification ¬ification) {
|
|
OPENAUTO_LOG(info) << "[AndroidAutoEntity] onBatteryStatusNotification()";
|
|
controlServiceChannel_->receive(this->shared_from_this());
|
|
}
|
|
|
|
void AndroidAutoEntity::onPingRequest(const aap_protobuf::service::control::message::PingRequest& request) {
|
|
OPENAUTO_LOG(info) << "[AndroidAutoEntity] onPingRequest()";
|
|
controlServiceChannel_->receive(this->shared_from_this());
|
|
}
|
|
|
|
void AndroidAutoEntity::onVoiceSessionRequest(
|
|
const aap_protobuf::service::control::message::VoiceSessionNotification &request) {
|
|
OPENAUTO_LOG(info) << "[AndroidAutoEntity] onVoiceSessionRequest()";
|
|
controlServiceChannel_->receive(this->shared_from_this());
|
|
}
|
|
|
|
void AndroidAutoEntity::onPingResponse(const aap_protobuf::service::control::message::PingResponse &response) {
|
|
OPENAUTO_LOG(info) << "[AndroidAutoEntity] onPingResponse()";
|
|
OPENAUTO_LOG(debug) << "[AndroidAutoEntity] Timestamp: " << response.timestamp();
|
|
pinger_->pong();
|
|
controlServiceChannel_->receive(this->shared_from_this());
|
|
}
|
|
|
|
void AndroidAutoEntity::onChannelError(const aasdk::error::Error &e) {
|
|
OPENAUTO_LOG(fatal) << "[AndroidAutoEntity] onChannelError(): " << e.what();
|
|
this->triggerQuit();
|
|
}
|
|
|
|
void AndroidAutoEntity::triggerQuit() {
|
|
OPENAUTO_LOG(info) << "[AndroidAutoEntity] triggerQuit()";
|
|
if (eventHandler_ != nullptr) {
|
|
eventHandler_->onAndroidAutoQuit();
|
|
}
|
|
}
|
|
|
|
void AndroidAutoEntity::schedulePing() {
|
|
OPENAUTO_LOG(info) << "[AndroidAutoEntity] schedulePing()";
|
|
auto promise = IPinger::Promise::defer(strand_);
|
|
promise->then([this, self = this->shared_from_this()]() {
|
|
this->sendPing();
|
|
this->schedulePing();
|
|
},
|
|
[this, self = this->shared_from_this()](auto error) {
|
|
if (error != aasdk::error::ErrorCode::OPERATION_ABORTED &&
|
|
error != aasdk::error::ErrorCode::OPERATION_IN_PROGRESS) {
|
|
OPENAUTO_LOG(error) << "[AndroidAutoEntity] Ping timer exceeded.";
|
|
this->triggerQuit();
|
|
}
|
|
});
|
|
|
|
pinger_->ping(std::move(promise));
|
|
}
|
|
|
|
void AndroidAutoEntity::sendPing() {
|
|
OPENAUTO_LOG(debug) << "[AndroidAutoEntity] sendPing()";
|
|
auto promise = aasdk::channel::SendPromise::defer(strand_);
|
|
promise->then([]() {},
|
|
std::bind(&AndroidAutoEntity::onChannelError, this->shared_from_this(), std::placeholders::_1));
|
|
|
|
aap_protobuf::service::control::message::PingRequest request;
|
|
auto timestamp = std::chrono::duration_cast<std::chrono::microseconds>(
|
|
std::chrono::high_resolution_clock::now().time_since_epoch());
|
|
request.set_timestamp(timestamp.count());
|
|
controlServiceChannel_->sendPingRequest(request, std::move(promise));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|