/* * 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 #include #include 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 kTimeoutMap = {{"critical", TimeoutLength::TIMEOUT_CRITICAL}, {"moderate", TimeoutLength::TIMEOUT_MODERATE}, {"normal", TimeoutLength::TIMEOUT_NORMAL}}; } // namespace WatchdogClient::WatchdogClient(const sp& 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 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 watchdogServer; std::shared_ptr 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 client = ICarWatchdogClient::fromBinder(binder); if (client == nullptr) { ALOGW("Failed to get ICarWatchdogClient from binder"); return; } std::shared_ptr 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 watchdogServer; std::shared_ptr 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