blob: c4b54bbbc022f7b1a1bf2ff419e8d81d8053b7ec [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 "incidentd"
#include "IncidentService.h"
#include "Reporter.h"
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <cutils/log.h>
#include <private/android_filesystem_config.h>
#include <utils/Looper.h>
#include <unistd.h>
using namespace android;
enum {
WHAT_RUN_REPORT = 1,
WHAT_SEND_BACKLOG_TO_DROPBOX = 2
};
//#define DEFAULT_BACKLOG_DELAY_NS (1000000000LL * 60 * 5)
#define DEFAULT_BACKLOG_DELAY_NS (1000000000LL)
// ================================================================================
String16 const DUMP_PERMISSION("android.permission.DUMP");
String16 const USAGE_STATS_PERMISSION("android.permission.PACKAGE_USAGE_STATS");
static Status
checkIncidentPermissions()
{
if (!checkCallingPermission(DUMP_PERMISSION)) {
ALOGW("Calling pid %d and uid %d does not have permission: android.permission.DUMP",
IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid());
return Status::fromExceptionCode(Status::EX_SECURITY,
"Calling process does not have permission: android.permission.DUMP");
}
if (!checkCallingPermission(USAGE_STATS_PERMISSION)) {
ALOGW("Calling pid %d and uid %d does not have permission: android.permission.USAGE_STATS",
IPCThreadState::self()->getCallingPid(), IPCThreadState::self()->getCallingUid());
return Status::fromExceptionCode(Status::EX_SECURITY,
"Calling process does not have permission: android.permission.USAGE_STATS");
}
return Status::ok();
}
// ================================================================================
ReportRequestQueue::ReportRequestQueue()
{
}
ReportRequestQueue::~ReportRequestQueue()
{
}
void
ReportRequestQueue::addRequest(const sp<ReportRequest>& request)
{
unique_lock<mutex> lock(mLock);
mQueue.push_back(request);
}
sp<ReportRequest>
ReportRequestQueue::getNextRequest()
{
unique_lock<mutex> lock(mLock);
if (mQueue.empty()) {
return NULL;
} else {
sp<ReportRequest> front(mQueue.front());
mQueue.pop_front();
return front;
}
}
// ================================================================================
ReportHandler::ReportHandler(const sp<Looper>& handlerLooper, const sp<ReportRequestQueue>& queue)
:mBacklogDelay(DEFAULT_BACKLOG_DELAY_NS),
mHandlerLooper(handlerLooper),
mQueue(queue)
{
}
ReportHandler::~ReportHandler()
{
}
void
ReportHandler::handleMessage(const Message& message)
{
switch (message.what) {
case WHAT_RUN_REPORT:
run_report();
break;
case WHAT_SEND_BACKLOG_TO_DROPBOX:
send_backlog_to_dropbox();
break;
}
}
void
ReportHandler::scheduleRunReport(const sp<ReportRequest>& request)
{
mQueue->addRequest(request);
mHandlerLooper->removeMessages(this, WHAT_RUN_REPORT);
mHandlerLooper->sendMessage(this, Message(WHAT_RUN_REPORT));
}
void
ReportHandler::scheduleSendBacklogToDropbox()
{
unique_lock<mutex> lock(mLock);
mBacklogDelay = DEFAULT_BACKLOG_DELAY_NS;
schedule_send_backlog_to_dropbox_locked();
}
void
ReportHandler::schedule_send_backlog_to_dropbox_locked()
{
mHandlerLooper->removeMessages(this, WHAT_SEND_BACKLOG_TO_DROPBOX);
mHandlerLooper->sendMessageDelayed(mBacklogDelay, this,
Message(WHAT_SEND_BACKLOG_TO_DROPBOX));
}
void
ReportHandler::run_report()
{
sp<Reporter> reporter = new Reporter();
// Merge all of the requests into one that has all of the
// requested fields.
while (true) {
sp<ReportRequest> request = mQueue->getNextRequest();
if (request == NULL) {
break;
}
reporter->batch.add(request);
}
// Take the report, which might take a while. More requests might queue
// up while we're doing this, and we'll handle them in their next batch.
// TODO: We should further rate-limit the reports to no more than N per time-period.
Reporter::run_report_status_t reportStatus = reporter->runReport();
if (reportStatus == Reporter::REPORT_NEEDS_DROPBOX) {
unique_lock<mutex> lock(mLock);
schedule_send_backlog_to_dropbox_locked();
}
}
void
ReportHandler::send_backlog_to_dropbox()
{
if (Reporter::upload_backlog() == Reporter::REPORT_NEEDS_DROPBOX) {
// There was a failure. Exponential backoff.
unique_lock<mutex> lock(mLock);
mBacklogDelay *= 2;
ALOGI("Error sending to dropbox. Trying again in %lld minutes",
(mBacklogDelay / (1000000000LL * 60)));
schedule_send_backlog_to_dropbox_locked();
} else {
mBacklogDelay = DEFAULT_BACKLOG_DELAY_NS;
}
}
// ================================================================================
IncidentService::IncidentService(const sp<Looper>& handlerLooper)
:mQueue(new ReportRequestQueue())
{
mHandler = new ReportHandler(handlerLooper, mQueue);
}
IncidentService::~IncidentService()
{
}
Status
IncidentService::reportIncident(const IncidentReportArgs& args)
{
ALOGI("reportIncident");
Status status = checkIncidentPermissions();
if (!status.isOk()) {
return status;
}
mHandler->scheduleRunReport(new ReportRequest(args, NULL, -1));
return Status::ok();
}
Status
IncidentService::reportIncidentToStream(const IncidentReportArgs& args,
const sp<IIncidentReportStatusListener>& listener, const unique_fd& stream)
{
ALOGI("reportIncidentToStream");
Status status = checkIncidentPermissions();
if (!status.isOk()) {
return status;
}
int fd = dup(stream.get());
if (fd < 0) {
return Status::fromStatusT(-errno);
}
mHandler->scheduleRunReport(new ReportRequest(args, listener, fd));
return Status::ok();
}
Status
IncidentService::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.
mHandler->scheduleSendBacklogToDropbox();
return Status::ok();
}