blob: fd0dd8e206d92a8aa2085adb1cd12d95160fb4ae [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 "carwatchdogd"
#include "WatchdogBinderMediator.h"
#include <android-base/file.h>
#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android/automotive/watchdog/BootPhase.h>
#include <android/automotive/watchdog/PowerCycle.h>
#include <android/automotive/watchdog/UserState.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <cutils/multiuser.h>
#include <log/log.h>
#include <private/android_filesystem_config.h>
namespace android {
namespace automotive {
namespace watchdog {
using android::defaultServiceManager;
using android::base::Error;
using android::base::Join;
using android::base::ParseUint;
using android::base::Result;
using android::base::StringPrintf;
using android::base::WriteStringToFd;
using android::binder::Status;
namespace {
constexpr const char* kHelpFlag = "--help";
constexpr const char* kHelpShortFlag = "-h";
constexpr const char* kHelpText =
"CarWatchdog daemon dumpsys help page:\n"
"Format: dumpsys android.automotive.watchdog.ICarWatchdog/default [options]\n\n"
"%s or %s: Displays this help text.\n"
"When no options are specified, carwatchdog report is generated.\n";
Status checkSystemUser() {
if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
return Status::fromExceptionCode(Status::EX_SECURITY,
"Calling process does not have proper privilege");
}
return Status::ok();
}
Status fromExceptionCode(int32_t exceptionCode, std::string message) {
ALOGW("%s", message.c_str());
return Status::fromExceptionCode(exceptionCode, message.c_str());
}
} // namespace
Result<void> WatchdogBinderMediator::init(sp<WatchdogProcessService> watchdogProcessService,
sp<IoPerfCollection> ioPerfCollection) {
if (watchdogProcessService == nullptr || ioPerfCollection == nullptr) {
return Error(INVALID_OPERATION)
<< "Must initialize both process and I/O perf collection service before starting "
<< "carwatchdog binder mediator";
}
if (mWatchdogProcessService != nullptr || mIoPerfCollection != nullptr) {
return Error(INVALID_OPERATION)
<< "Cannot initialize carwatchdog binder mediator more than once";
}
mWatchdogProcessService = watchdogProcessService;
mIoPerfCollection = ioPerfCollection;
status_t status =
defaultServiceManager()
->addService(String16("android.automotive.watchdog.ICarWatchdog/default"),
this);
if (status != OK) {
return Error(status) << "Failed to start carwatchdog binder mediator";
}
return {};
}
status_t WatchdogBinderMediator::dump(int fd, const Vector<String16>& args) {
int numArgs = args.size();
if (numArgs == 1 && (args[0] == String16(kHelpFlag) || args[0] == String16(kHelpShortFlag))) {
if (!dumpHelpText(fd, "")) {
ALOGW("Failed to write help text to fd");
return FAILED_TRANSACTION;
}
return OK;
}
if (numArgs >= 1 &&
(args[0] == String16(kStartCustomCollectionFlag) ||
args[0] == String16(kEndCustomCollectionFlag))) {
auto ret = mIoPerfCollection->onCustomCollection(fd, args);
if (!ret.ok()) {
std::string mode = args[0] == String16(kStartCustomCollectionFlag) ? "start" : "end";
std::string errorMsg = StringPrintf("Failed to %s custom I/O perf collection: %s",
mode.c_str(), ret.error().message().c_str());
if (ret.error().code() == BAD_VALUE) {
dumpHelpText(fd, errorMsg);
} else {
ALOGW("%s", errorMsg.c_str());
}
return ret.error().code();
}
return OK;
}
if (numArgs > 0) {
ALOGW("Car watchdog cannot recognize the given option(%s). Dumping the current state...",
Join(args, " ").c_str());
}
auto ret = mWatchdogProcessService->dump(fd, args);
if (!ret.ok()) {
ALOGW("Failed to dump carwatchdog process service: %s", ret.error().message().c_str());
return ret.error().code();
}
ret = mIoPerfCollection->onDump(fd);
if (!ret.ok()) {
ALOGW("Failed to dump I/O perf collection: %s", ret.error().message().c_str());
return ret.error().code();
}
return OK;
}
bool WatchdogBinderMediator::dumpHelpText(int fd, std::string errorMsg) {
if (!errorMsg.empty()) {
ALOGW("Error: %s", errorMsg.c_str());
if (!WriteStringToFd(StringPrintf("Error: %s\n\n", errorMsg.c_str()), fd)) {
ALOGW("Failed to write error message to fd");
return false;
}
}
return WriteStringToFd(StringPrintf(kHelpText, kHelpFlag, kHelpShortFlag), fd) &&
mIoPerfCollection->dumpHelpText(fd);
}
Status WatchdogBinderMediator::registerMediator(const sp<ICarWatchdogClient>& mediator) {
Status status = checkSystemUser();
if (!status.isOk()) {
return status;
}
return mWatchdogProcessService->registerMediator(mediator);
}
Status WatchdogBinderMediator::unregisterMediator(const sp<ICarWatchdogClient>& mediator) {
Status status = checkSystemUser();
if (!status.isOk()) {
return status;
}
return mWatchdogProcessService->unregisterMediator(mediator);
}
Status WatchdogBinderMediator::registerMonitor(const sp<ICarWatchdogMonitor>& monitor) {
Status status = checkSystemUser();
if (!status.isOk()) {
return status;
}
return mWatchdogProcessService->registerMonitor(monitor);
}
Status WatchdogBinderMediator::unregisterMonitor(const sp<ICarWatchdogMonitor>& monitor) {
Status status = checkSystemUser();
if (!status.isOk()) {
return status;
}
return mWatchdogProcessService->unregisterMonitor(monitor);
}
Status WatchdogBinderMediator::tellMediatorAlive(const sp<ICarWatchdogClient>& mediator,
const std::vector<int32_t>& clientsNotResponding,
int32_t sessionId) {
Status status = checkSystemUser();
if (!status.isOk()) {
return status;
}
return mWatchdogProcessService->tellMediatorAlive(mediator, clientsNotResponding, sessionId);
}
Status WatchdogBinderMediator::tellDumpFinished(const android::sp<ICarWatchdogMonitor>& monitor,
int32_t pid) {
Status status = checkSystemUser();
if (!status.isOk()) {
return status;
}
return mWatchdogProcessService->tellDumpFinished(monitor, pid);
}
Status WatchdogBinderMediator::notifySystemStateChange(StateType type, int32_t arg1, int32_t arg2) {
Status status = checkSystemUser();
if (!status.isOk()) {
return status;
}
switch (type) {
case StateType::POWER_CYCLE: {
PowerCycle powerCycle = static_cast<PowerCycle>(static_cast<uint32_t>(arg1));
if (powerCycle >= PowerCycle::NUM_POWER_CYLES) {
return fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT,
StringPrintf("Invalid power cycle %d", powerCycle));
}
return mWatchdogProcessService->notifyPowerCycleChange(powerCycle);
}
case StateType::USER_STATE: {
userid_t userId = static_cast<userid_t>(arg1);
UserState userState = static_cast<UserState>(static_cast<uint32_t>(arg2));
if (userState >= UserState::NUM_USER_STATES) {
return fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT,
StringPrintf("Invalid user state %d", userState));
}
return mWatchdogProcessService->notifyUserStateChange(userId, userState);
}
case StateType::BOOT_PHASE: {
BootPhase phase = static_cast<BootPhase>(static_cast<uint32_t>(arg1));
if (phase >= BootPhase::BOOT_COMPLETED) {
auto ret = mIoPerfCollection->onBootFinished();
if (!ret.ok()) {
return fromExceptionCode(ret.error().code(), ret.error().message());
}
}
return Status::ok();
}
}
return fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT,
StringPrintf("Invalid state change type %d", type));
}
} // namespace watchdog
} // namespace automotive
} // namespace android