blob: a28f0850cceae426dddf5b301820314db3179afe [file] [log] [blame]
/*
* Copyright (C) 2016 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 "statsd"
#define DEBUG true
#include "StatsService.h"
#include "DropboxReader.h"
#include <android-base/file.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <cutils/log.h>
#include <frameworks/base/cmds/statsd/src/statsd_config.pb.h>
#include <private/android_filesystem_config.h>
#include <utils/Looper.h>
#include <utils/String16.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
using namespace android;
namespace android {
namespace os {
namespace statsd {
StatsService::StatsService(const sp<Looper>& handlerLooper)
: mAnomalyMonitor(new AnomalyMonitor(2)) // TODO: Change this based on the config
{
ALOGD("stats service constructed");
}
StatsService::~StatsService() {
}
status_t StatsService::setProcessor(const sp<StatsLogProcessor>& main_processor) {
m_processor = main_processor;
ALOGD("stats service set to processor %p", m_processor.get());
return NO_ERROR;
}
// Implement our own because the default binder implementation isn't
// properly handling SHELL_COMMAND_TRANSACTION
status_t StatsService::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
uint32_t flags) {
status_t err;
switch (code) {
case SHELL_COMMAND_TRANSACTION: {
int in = data.readFileDescriptor();
int out = data.readFileDescriptor();
int err = data.readFileDescriptor();
int argc = data.readInt32();
Vector<String8> args;
for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
args.add(String8(data.readString16()));
}
sp<IShellCallback> shellCallback = IShellCallback::asInterface(data.readStrongBinder());
sp<IResultReceiver> resultReceiver =
IResultReceiver::asInterface(data.readStrongBinder());
FILE* fin = fdopen(in, "r");
FILE* fout = fdopen(out, "w");
FILE* ferr = fdopen(err, "w");
if (fin == NULL || fout == NULL || ferr == NULL) {
resultReceiver->send(NO_MEMORY);
} else {
err = command(fin, fout, ferr, args);
resultReceiver->send(err);
}
if (fin != NULL) {
fflush(fin);
fclose(fin);
}
if (fout != NULL) {
fflush(fout);
fclose(fout);
}
if (fout != NULL) {
fflush(ferr);
fclose(ferr);
}
return NO_ERROR;
}
default: { return BnStatsManager::onTransact(code, data, reply, flags); }
}
}
status_t StatsService::dump(int fd, const Vector<String16>& args) {
FILE* out = fdopen(fd, "w");
if (out == NULL) {
return NO_MEMORY; // the fd is already open
}
fprintf(out, "StatsService::dump:");
ALOGD("StatsService::dump:");
const int N = args.size();
for (int i = 0; i < N; i++) {
fprintf(out, " %s", String8(args[i]).string());
ALOGD(" %s", String8(args[i]).string());
}
fprintf(out, "\n");
fclose(out);
return NO_ERROR;
}
status_t StatsService::command(FILE* in, FILE* out, FILE* err, Vector<String8>& args) {
if (args.size() > 0) {
if (!args[0].compare(String8("print-stats-log")) && args.size() > 1) {
return doPrintStatsLog(out, args);
}
if (!args[0].compare(String8("config"))) {
return doLoadConfig(in);
}
}
printCmdHelp(out);
return NO_ERROR;
}
status_t StatsService::doLoadConfig(FILE* in) {
string content;
if (!android::base::ReadFdToString(fileno(in), &content)) {
return UNKNOWN_ERROR;
}
StatsdConfig config;
if (config.ParseFromString(content)) {
ALOGD("Config parsed from command line: %s", config.SerializeAsString().c_str());
m_processor->UpdateConfig(0, config);
return NO_ERROR;
} else {
ALOGD("Config failed to be parsed");
return UNKNOWN_ERROR;
}
}
Status StatsService::informAnomalyAlarmFired() {
if (DEBUG) ALOGD("StatsService::informAnomalyAlarmFired was called");
if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
return Status::fromExceptionCode(Status::EX_SECURITY,
"Only system uid can call informAnomalyAlarmFired");
}
if (DEBUG) ALOGD("StatsService::informAnomalyAlarmFired succeeded");
// TODO: check through all counters/timers and see if an anomaly has indeed occurred.
return Status::ok();
}
Status StatsService::informPollAlarmFired() {
if (DEBUG) ALOGD("StatsService::informPollAlarmFired was called");
if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
return Status::fromExceptionCode(Status::EX_SECURITY,
"Only system uid can call informPollAlarmFired");
}
if (DEBUG) ALOGD("StatsService::informPollAlarmFired succeeded");
// TODO: determine what services to poll and poll (or ask StatsCompanionService to poll) them.
return Status::ok();
}
Status StatsService::systemRunning() {
if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
return Status::fromExceptionCode(Status::EX_SECURITY,
"Only system uid can call systemRunning");
}
// When system_server is up and running, schedule the dropbox task to run.
ALOGD("StatsService::systemRunning");
sayHiToStatsCompanion();
return Status::ok();
}
void StatsService::sayHiToStatsCompanion() {
// TODO: This method needs to be private. It is temporarily public and unsecured for testing
// purposes.
sp<IStatsCompanionService> statsCompanion = getStatsCompanionService();
if (statsCompanion != nullptr) {
if (DEBUG) ALOGD("Telling statsCompanion that statsd is ready");
statsCompanion->statsdReady();
} else {
if (DEBUG) ALOGD("Could not access statsCompanion");
}
}
sp<IStatsCompanionService> StatsService::getStatsCompanionService() {
sp<IStatsCompanionService> statsCompanion = nullptr;
// Get statscompanion service from service manager
const sp<IServiceManager> sm(defaultServiceManager());
if (sm != nullptr) {
const String16 name("statscompanion");
statsCompanion = interface_cast<IStatsCompanionService>(sm->checkService(name));
if (statsCompanion == nullptr) {
ALOGW("statscompanion service unavailable!");
return nullptr;
}
}
return statsCompanion;
}
Status StatsService::statsCompanionReady() {
if (DEBUG) ALOGD("StatsService::statsCompanionReady was called");
if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
return Status::fromExceptionCode(Status::EX_SECURITY,
"Only system uid can call statsCompanionReady");
}
sp<IStatsCompanionService> statsCompanion = getStatsCompanionService();
if (statsCompanion == nullptr) {
return Status::fromExceptionCode(
Status::EX_NULL_POINTER,
"statscompanion unavailable despite it contacting statsd!");
}
if (DEBUG) ALOGD("StatsService::statsCompanionReady linking to statsCompanion.");
IInterface::asBinder(statsCompanion)->linkToDeath(new StatsdDeathRecipient(mAnomalyMonitor));
mAnomalyMonitor->setStatsCompanionService(statsCompanion);
return Status::ok();
}
void StatsdDeathRecipient::binderDied(const wp<IBinder>& who) {
ALOGW("statscompanion service died");
mAnmlyMntr->setStatsCompanionService(nullptr);
}
status_t StatsService::doPrintStatsLog(FILE* out, const Vector<String8>& args) {
long msec = 0;
if (args.size() > 2) {
msec = strtol(args[2].string(), NULL, 10);
}
return DropboxReader::readStatsLogs(out, args[1].string(), msec);
}
void StatsService::printCmdHelp(FILE* out) {
fprintf(out, "Usage:\n");
fprintf(out, "\t print-stats-log [tag_required] [timestamp_nsec_optional]\n");
fprintf(out,
"\t config\t Loads a new config from command-line (must be proto in wire-encoded "
"format).\n");
}
} // namespace statsd
} // namespace os
} // namespace android