You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
228 lines
8.5 KiB
228 lines
8.5 KiB
4 months ago
|
/*
|
||
|
* 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;
|
||
|
}
|
||
|
|
||
|
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()) {
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
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
|