blob: b19b1df37f61b73ecca19944dc5b937621a4cfa2 [file] [log] [blame]
/*
* Copyright (C) 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 "GarageModeServerSideHandler.h"
#include <chrono>
#include <condition_variable>
#include <fstream>
#include <thread>
#include <errno.h>
#include <sys/inotify.h>
#include <android-base/logging.h>
#include <utils/SystemClock.h>
#include "Utils.h"
#include "vhal_v2_0/VehicleUtils.h"
namespace android::hardware::automotive::vehicle::V2_0::impl {
using std::chrono::duration_cast;
using std::chrono::steady_clock;
using std::literals::chrono_literals::operator""s;
class GarageModeServerSideHandlerImpl : public GarageModeServerSideHandler {
public:
GarageModeServerSideHandlerImpl(IVehicleServer* vehicleServer,
VehiclePropValuePool* vehicleObjectPool,
const std::string& powerStateMarkerFilePath)
: mVehicleServer(vehicleServer),
mValueObjectPool(vehicleObjectPool),
mPowerStateMarkerPath(powerStateMarkerFilePath) {
mThreads.emplace_back(std::bind(&GarageModeServerSideHandlerImpl::PowerStateWatcher, this));
mThreads.emplace_back(
std::bind(&GarageModeServerSideHandlerImpl::HeartbeatTimeoutWatcher, this));
}
~GarageModeServerSideHandlerImpl() {
mShuttingDownFlag.store(true);
mHeartbeatCV.notify_all();
for (auto& thread : mThreads) {
if (thread.joinable()) {
thread.join();
}
}
}
void HandleHeartbeat() override;
private:
void HeartbeatTimeoutWatcher();
void PowerStateWatcher();
void HandleNewPowerState();
recyclable_ptr<VehiclePropValue> CreateApPowerStateReq(VehicleApPowerStateReq state,
int32_t param);
IVehicleServer* const mVehicleServer;
VehiclePropValuePool* const mValueObjectPool;
// TODO(chenhaosjtuacm): use std::filesystem when toolchain >= gcc8 is available
const std::string mPowerStateMarkerPath;
std::atomic<bool> mSystemShuttingDownPrepareFlag{false};
std::atomic<bool> mShuttingDownFlag{false};
std::atomic<steady_clock::time_point> mLastHeartbeatTime{};
std::vector<std::thread> mThreads;
std::condition_variable mHeartbeatCV;
std::mutex mHeartbeatMutex;
};
void GarageModeServerSideHandlerImpl::HandleHeartbeat() {
LOG(DEBUG) << __func__ << ": received heartbeat from the client";
mLastHeartbeatTime.store(steady_clock::now());
}
void GarageModeServerSideHandlerImpl::HeartbeatTimeoutWatcher() {
constexpr auto kHeartbeatTimeout = duration_cast<steady_clock::duration>(5s);
constexpr auto kHeartbeatCheckPeriod = 1s;
while (!mShuttingDownFlag.load()) {
if (!mSystemShuttingDownPrepareFlag.load()) {
std::unique_lock<std::mutex> heartbeatLock(mHeartbeatMutex);
mHeartbeatCV.wait(heartbeatLock, [this]() {
return mSystemShuttingDownPrepareFlag.load() || mShuttingDownFlag.load();
});
// Reset mLastHeartbeatTime everytime after entering shutdown state
HandleHeartbeat();
}
auto timeSinceLastHeartbeat = steady_clock::now() - mLastHeartbeatTime.load();
if (timeSinceLastHeartbeat > kHeartbeatTimeout) {
LOG(ERROR) << __func__ << ": heartbeat timeout!";
// TODO(chenhaosjtuacm): Shutdown AGL
break;
}
std::this_thread::sleep_for(kHeartbeatCheckPeriod);
}
}
void GarageModeServerSideHandlerImpl::PowerStateWatcher() {
constexpr auto kFileStatusCheckPeriod = 1s;
bool log_marker_file_not_exists_message_once = false;
bool log_marker_file_no_access_message_once = false;
auto call_once = [](bool* once_flag, auto&& func) {
if (!*once_flag) {
*once_flag = true;
func();
}
};
while (access(mPowerStateMarkerPath.c_str(), F_OK | R_OK) < 0) {
if (errno == ENOENT) {
call_once(&log_marker_file_not_exists_message_once, [this]() {
LOG(ERROR) << __func__ << ": marker file " << mPowerStateMarkerPath
<< " has not been created yet.";
});
} else {
call_once(&log_marker_file_no_access_message_once, [this]() {
LOG(ERROR) << __func__ << ": no read access to marker file "
<< mPowerStateMarkerPath;
});
}
std::this_thread::sleep_for(kFileStatusCheckPeriod);
}
int inotifyFd = inotify_init();
if (inotifyFd < 0) {
LOG(ERROR) << __func__ << ": failed to open inotify instance: " << strerror(errno);
return;
}
int watchDescriptor = inotify_add_watch(inotifyFd, mPowerStateMarkerPath.c_str(), IN_MODIFY);
if (watchDescriptor < 0) {
LOG(ERROR) << __func__ << ": failed to watch file " << mPowerStateMarkerPath << " : "
<< strerror(errno);
return;
}
alignas(alignof(struct inotify_event)) char inotifyEventBuffer[4096] = {0};
[[maybe_unused]] struct inotify_event& inotifyEvent =
*reinterpret_cast<struct inotify_event*>(inotifyEventBuffer);
HandleNewPowerState();
while (!mShuttingDownFlag.load()) {
if (!WaitForReadWithTimeout(inotifyFd, kFileStatusCheckPeriod)) {
continue;
}
auto eventReadLen = read(inotifyFd, inotifyEventBuffer, sizeof(inotifyEventBuffer));
if (eventReadLen < 0) {
LOG(ERROR) << __func__ << "failed to read the inotify event: " << strerror(errno);
return;
}
if (eventReadLen < static_cast<ssize_t>(sizeof(struct inotify_event))) {
LOG(ERROR) << __func__ << ": failed to read the full event, min event size: "
<< sizeof(struct inotify_event) << ", read size: " << eventReadLen;
return;
}
HandleNewPowerState();
}
}
void GarageModeServerSideHandlerImpl::HandleNewPowerState() {
std::ifstream markerFileStream(mPowerStateMarkerPath);
std::string powerStateString;
markerFileStream >> powerStateString;
LOG(INFO) << __func__ << ": set power state to " << powerStateString;
if (powerStateString == "shutdown") {
mVehicleServer->onPropertyValueFromCar(
*CreateApPowerStateReq(VehicleApPowerStateReq::SHUTDOWN_PREPARE,
toInt(VehicleApPowerStateShutdownParam::CAN_SLEEP)),
true);
mSystemShuttingDownPrepareFlag.store(true);
mHeartbeatCV.notify_all();
} else if (powerStateString == "on") {
if (mSystemShuttingDownPrepareFlag.load()) {
mVehicleServer->onPropertyValueFromCar(
*CreateApPowerStateReq(VehicleApPowerStateReq::CANCEL_SHUTDOWN, 0), true);
mSystemShuttingDownPrepareFlag.store(false);
} else {
LOG(INFO) << __func__ << ": not in the shutdown state, nothing changed";
}
} else {
LOG(ERROR) << __func__ << ": unknown power state: " << powerStateString;
}
}
recyclable_ptr<VehiclePropValue> GarageModeServerSideHandlerImpl::CreateApPowerStateReq(
VehicleApPowerStateReq state, int32_t param) {
auto req = mValueObjectPool->obtain(VehiclePropertyType::INT32_VEC, 2);
req->prop = toInt(VehicleProperty::AP_POWER_STATE_REQ);
req->areaId = 0;
req->timestamp = elapsedRealtimeNano();
req->status = VehiclePropertyStatus::AVAILABLE;
req->value.int32Values[0] = toInt(state);
req->value.int32Values[1] = param;
return req;
}
std::unique_ptr<GarageModeServerSideHandler> makeGarageModeServerSideHandler(
IVehicleServer* vehicleServer, VehiclePropValuePool* valueObjectPool,
const std::string& powerStateMarkerFilePath) {
return std::make_unique<GarageModeServerSideHandlerImpl>(vehicleServer, valueObjectPool,
powerStateMarkerFilePath);
}
} // namespace android::hardware::automotive::vehicle::V2_0::impl