blob: 4c381c6417e242311544eb63f11ac07eef18ef62 [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.
*/
#define LOG_TAG "carwatchdog_testclient"
#include "WatchdogClient.h"
#include <android-base/file.h>
#include <android/binder_manager.h>
#include <unordered_map>
namespace aidl {
namespace android {
namespace automotive {
namespace watchdog {
using ::android::Looper;
using ::android::Message;
using ::android::Mutex;
using ::android::sp;
namespace {
enum { WHAT_CHECK_ALIVE = 1, WHAT_BECOME_INACTIVE = 2, WHAT_TERMINATE = 3 };
const std::unordered_map<std::string, TimeoutLength> kTimeoutMap =
{{"critical", TimeoutLength::TIMEOUT_CRITICAL},
{"moderate", TimeoutLength::TIMEOUT_MODERATE},
{"normal", TimeoutLength::TIMEOUT_NORMAL}};
} // namespace
WatchdogClient::WatchdogClient(const sp<Looper>& handlerLooper) : mHandlerLooper(handlerLooper) {
mMessageHandler = new MessageHandlerImpl(this);
}
ndk::ScopedAStatus WatchdogClient::checkIfAlive(int32_t sessionId, TimeoutLength timeout) {
if (mVerbose) {
ALOGI("Pinged by car watchdog daemon: session id = %d", sessionId);
}
Mutex::Autolock lock(mMutex);
mHandlerLooper->removeMessages(mMessageHandler, WHAT_CHECK_ALIVE);
mSession = HealthCheckSession(sessionId, timeout);
mHandlerLooper->sendMessage(mMessageHandler, Message(WHAT_CHECK_ALIVE));
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus WatchdogClient::prepareProcessTermination() {
ALOGI("This process is being terminated by car watchdog");
return ndk::ScopedAStatus::ok();
}
bool WatchdogClient::initialize(const CommandParam& param) {
ndk::SpAIBinder binder(
AServiceManager_getService("android.automotive.watchdog.ICarWatchdog/default"));
if (binder.get() == nullptr) {
ALOGE("Getting carwatchdog daemon failed");
return false;
}
std::shared_ptr<ICarWatchdog> server = ICarWatchdog::fromBinder(binder);
if (server == nullptr) {
ALOGE("Failed to connect to carwatchdog daemon");
return false;
}
{
Mutex::Autolock lock(mMutex);
mWatchdogServer = server;
mIsClientActive = true;
}
mForcedKill = param.forcedKill;
mVerbose = param.verbose;
registerClient(param.timeout);
if (param.inactiveAfterInSec >= 0) {
mHandlerLooper->sendMessageDelayed(seconds_to_nanoseconds(param.inactiveAfterInSec),
mMessageHandler, Message(WHAT_BECOME_INACTIVE));
}
if (param.terminateAfterInSec >= 0) {
mHandlerLooper->sendMessageDelayed(seconds_to_nanoseconds(param.terminateAfterInSec),
mMessageHandler, Message(WHAT_TERMINATE));
}
return true;
}
void WatchdogClient::finalize() {
{
Mutex::Autolock lock(mMutex);
if (!mWatchdogServer) {
return;
}
}
unregisterClient();
}
void WatchdogClient::respondToWatchdog() {
int sessionId;
std::shared_ptr<ICarWatchdog> watchdogServer;
std::shared_ptr<ICarWatchdogClient> testClient;
{
Mutex::Autolock lock(mMutex);
if (!mIsClientActive || mTestClient == nullptr || mWatchdogServer == nullptr) {
return;
}
watchdogServer = mWatchdogServer;
testClient = mTestClient;
sessionId = mSession.id;
}
ndk::ScopedAStatus status = watchdogServer->tellClientAlive(testClient, sessionId);
if (!status.isOk()) {
ALOGE("Failed to call binder interface: %d", status.getStatus());
return;
}
if (mVerbose) {
ALOGI("Sent response to car watchdog daemon: session id = %d", sessionId);
}
}
void WatchdogClient::becomeInactive() {
Mutex::Autolock lock(mMutex);
mIsClientActive = false;
if (mVerbose) {
ALOGI("Became inactive");
}
}
void WatchdogClient::terminateProcess() {
if (!mForcedKill) {
unregisterClient();
}
raise(SIGKILL);
}
void WatchdogClient::registerClient(const std::string& timeout) {
ndk::SpAIBinder binder = this->asBinder();
if (binder.get() == nullptr) {
ALOGW("Failed to get car watchdog client binder object");
return;
}
std::shared_ptr<ICarWatchdogClient> client = ICarWatchdogClient::fromBinder(binder);
if (client == nullptr) {
ALOGW("Failed to get ICarWatchdogClient from binder");
return;
}
std::shared_ptr<ICarWatchdog> watchdogServer;
{
Mutex::Autolock lock(mMutex);
if (mWatchdogServer == nullptr) {
return;
}
watchdogServer = mWatchdogServer;
mTestClient = client;
}
watchdogServer->registerClient(client, kTimeoutMap.at(timeout));
ALOGI("Successfully registered the client to car watchdog server");
}
void WatchdogClient::unregisterClient() {
std::shared_ptr<ICarWatchdog> watchdogServer;
std::shared_ptr<ICarWatchdogClient> testClient;
{
Mutex::Autolock lock(mMutex);
if (mWatchdogServer == nullptr || mTestClient == nullptr) {
return;
}
watchdogServer = mWatchdogServer;
testClient = mTestClient;
mTestClient = nullptr;
}
watchdogServer->unregisterClient(testClient);
ALOGI("Successfully unregistered the client from car watchdog server");
}
WatchdogClient::MessageHandlerImpl::MessageHandlerImpl(WatchdogClient* client) : mClient(client) {}
void WatchdogClient::MessageHandlerImpl::handleMessage(const Message& message) {
switch (message.what) {
case WHAT_CHECK_ALIVE:
mClient->respondToWatchdog();
break;
case WHAT_BECOME_INACTIVE:
mClient->becomeInactive();
break;
case WHAT_TERMINATE:
mClient->terminateProcess();
break;
default:
ALOGW("Unknown message: %d", message.what);
}
}
} // namespace watchdog
} // namespace automotive
} // namespace android
} // namespace aidl