summaryrefslogtreecommitdiff
path: root/cmds/incidentd/src/IncidentService.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cmds/incidentd/src/IncidentService.cpp')
-rw-r--r--cmds/incidentd/src/IncidentService.cpp294
1 files changed, 213 insertions, 81 deletions
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index 7c6789e6e5ba..28fb38a79a35 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -13,15 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-#define LOG_TAG "incidentd"
+#define DEBUG false
+#include "Log.h"
#include "IncidentService.h"
+#include "FdBuffer.h"
+#include "PrivacyBuffer.h"
#include "Reporter.h"
+#include "incidentd_util.h"
+#include "section_list.h"
#include <binder/IPCThreadState.h>
+#include <binder/IResultReceiver.h>
#include <binder/IServiceManager.h>
+#include <binder/IShellCallback.h>
#include <cutils/log.h>
#include <private/android_filesystem_config.h>
#include <utils/Looper.h>
@@ -29,57 +35,76 @@
#include <unistd.h>
using namespace android;
+using namespace android::base;
-enum {
- WHAT_RUN_REPORT = 1,
- WHAT_SEND_BACKLOG_TO_DROPBOX = 2
-};
+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)
+#define DEFAULT_BYTES_SIZE_LIMIT (20 * 1024 * 1024) // 20MB
+#define DEFAULT_REFACTORY_PERIOD_MS (24 * 60 * 60 * 1000) // 1 Day
+
// ================================================================================
String16 const DUMP_PERMISSION("android.permission.DUMP");
String16 const USAGE_STATS_PERMISSION("android.permission.PACKAGE_USAGE_STATS");
-static Status
-checkIncidentPermissions()
-{
+static Status checkIncidentPermissions(const IncidentReportArgs& args) {
+ uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ pid_t callingPid = IPCThreadState::self()->getCallingPid();
+ if (callingUid == AID_ROOT || callingUid == AID_SHELL) {
+ // root doesn't have permission.DUMP if don't do this!
+ return Status::ok();
+ }
+
+ // checking calling permission.
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,
+ callingPid, callingUid);
+ 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,
+ callingPid, callingUid);
+ return Status::fromExceptionCode(
+ Status::EX_SECURITY,
"Calling process does not have permission: android.permission.USAGE_STATS");
}
+
+ // checking calling request uid permission.
+ switch (args.dest()) {
+ case DEST_LOCAL:
+ if (callingUid != AID_SHELL && callingUid != AID_ROOT) {
+ ALOGW("Calling pid %d and uid %d does not have permission to get local data.",
+ callingPid, callingUid);
+ return Status::fromExceptionCode(
+ Status::EX_SECURITY,
+ "Calling process does not have permission to get local data.");
+ }
+ case DEST_EXPLICIT:
+ if (callingUid != AID_SHELL && callingUid != AID_ROOT && callingUid != AID_STATSD &&
+ callingUid != AID_SYSTEM) {
+ ALOGW("Calling pid %d and uid %d does not have permission to get explicit data.",
+ callingPid, callingUid);
+ return Status::fromExceptionCode(
+ Status::EX_SECURITY,
+ "Calling process does not have permission to get explicit data.");
+ }
+ }
return Status::ok();
}
-
-
// ================================================================================
-ReportRequestQueue::ReportRequestQueue()
-{
-}
+ReportRequestQueue::ReportRequestQueue() {}
-ReportRequestQueue::~ReportRequestQueue()
-{
-}
+ReportRequestQueue::~ReportRequestQueue() {}
-void
-ReportRequestQueue::addRequest(const sp<ReportRequest>& request)
-{
+void ReportRequestQueue::addRequest(const sp<ReportRequest>& request) {
unique_lock<mutex> lock(mLock);
mQueue.push_back(request);
}
-sp<ReportRequest>
-ReportRequestQueue::getNextRequest()
-{
+sp<ReportRequest> ReportRequestQueue::getNextRequest() {
unique_lock<mutex> lock(mLock);
if (mQueue.empty()) {
return NULL;
@@ -90,22 +115,17 @@ ReportRequestQueue::getNextRequest()
}
}
-
// ================================================================================
-ReportHandler::ReportHandler(const sp<Looper>& handlerLooper, const sp<ReportRequestQueue>& queue)
- :mBacklogDelay(DEFAULT_BACKLOG_DELAY_NS),
- mHandlerLooper(handlerLooper),
- mQueue(queue)
-{
-}
+ReportHandler::ReportHandler(const sp<Looper>& handlerLooper, const sp<ReportRequestQueue>& queue,
+ const sp<Throttler>& throttler)
+ : mBacklogDelay(DEFAULT_BACKLOG_DELAY_NS),
+ mHandlerLooper(handlerLooper),
+ mQueue(queue),
+ mThrottler(throttler) {}
-ReportHandler::~ReportHandler()
-{
-}
+ReportHandler::~ReportHandler() {}
-void
-ReportHandler::handleMessage(const Message& message)
-{
+void ReportHandler::handleMessage(const Message& message) {
switch (message.what) {
case WHAT_RUN_REPORT:
run_report();
@@ -116,33 +136,24 @@ ReportHandler::handleMessage(const Message& message)
}
}
-void
-ReportHandler::scheduleRunReport(const sp<ReportRequest>& request)
-{
+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()
-{
+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()
-{
+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));
+ mHandlerLooper->sendMessageDelayed(mBacklogDelay, this, Message(WHAT_SEND_BACKLOG_TO_DROPBOX));
}
-void
-ReportHandler::run_report()
-{
+void ReportHandler::run_report() {
sp<Reporter> reporter = new Reporter();
// Merge all of the requests into one that has all of the
@@ -153,28 +164,32 @@ ReportHandler::run_report()
break;
}
reporter->batch.add(request);
- reporter->args.merge(request->args);
+ }
+
+ if (mThrottler->shouldThrottle()) {
+ ALOGW("RunReport got throttled.");
+ return;
}
// 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();
+ size_t reportByteSize = 0;
+ Reporter::run_report_status_t reportStatus = reporter->runReport(&reportByteSize);
+ mThrottler->addReportSize(reportByteSize);
if (reportStatus == Reporter::REPORT_NEEDS_DROPBOX) {
unique_lock<mutex> lock(mLock);
schedule_send_backlog_to_dropbox_locked();
}
}
-void
-ReportHandler::send_backlog_to_dropbox()
-{
+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)));
+ (mBacklogDelay / (1000000000LL * 60)));
schedule_send_backlog_to_dropbox_locked();
} else {
mBacklogDelay = DEFAULT_BACKLOG_DELAY_NS;
@@ -183,21 +198,17 @@ ReportHandler::send_backlog_to_dropbox()
// ================================================================================
IncidentService::IncidentService(const sp<Looper>& handlerLooper)
- :mQueue(new ReportRequestQueue())
-{
- mHandler = new ReportHandler(handlerLooper, mQueue);
+ : mQueue(new ReportRequestQueue()),
+ mThrottler(new Throttler(DEFAULT_BYTES_SIZE_LIMIT, DEFAULT_REFACTORY_PERIOD_MS)) {
+ mHandler = new ReportHandler(handlerLooper, mQueue, mThrottler);
}
-IncidentService::~IncidentService()
-{
-}
+IncidentService::~IncidentService() {}
-Status
-IncidentService::reportIncident(const IncidentReportArgs& args)
-{
+Status IncidentService::reportIncident(const IncidentReportArgs& args) {
ALOGI("reportIncident");
- Status status = checkIncidentPermissions();
+ Status status = checkIncidentPermissions(args);
if (!status.isOk()) {
return status;
}
@@ -207,13 +218,12 @@ IncidentService::reportIncident(const IncidentReportArgs& args)
return Status::ok();
}
-Status
-IncidentService::reportIncidentToStream(const IncidentReportArgs& args,
- const sp<IIncidentReportStatusListener>& listener, const unique_fd& stream)
-{
+Status IncidentService::reportIncidentToStream(const IncidentReportArgs& args,
+ const sp<IIncidentReportStatusListener>& listener,
+ const unique_fd& stream) {
ALOGI("reportIncidentToStream");
- Status status = checkIncidentPermissions();
+ Status status = checkIncidentPermissions(args);
if (!status.isOk()) {
return status;
}
@@ -228,17 +238,139 @@ IncidentService::reportIncidentToStream(const IncidentReportArgs& args,
return Status::ok();
}
-Status
-IncidentService::systemRunning()
-{
+Status IncidentService::systemRunning() {
if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
return Status::fromExceptionCode(Status::EX_SECURITY,
- "Only system uid can call systemRunning");
+ "Only system uid can call systemRunning");
}
-
+
// When system_server is up and running, schedule the dropbox task to run.
mHandler->scheduleSendBacklogToDropbox();
return Status::ok();
}
+/**
+ * Implement our own because the default binder implementation isn't
+ * properly handling SHELL_COMMAND_TRANSACTION.
+ */
+status_t IncidentService::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 BnIncidentManager::onTransact(code, data, reply, flags); }
+ }
+}
+
+status_t IncidentService::command(FILE* in, FILE* out, FILE* err, Vector<String8>& args) {
+ const int argCount = args.size();
+
+ if (argCount >= 1) {
+ if (!args[0].compare(String8("privacy"))) {
+ return cmd_privacy(in, out, err, args);
+ }
+ if (!args[0].compare(String8("throttler"))) {
+ mThrottler->dump(out);
+ return NO_ERROR;
+ }
+ }
+ return cmd_help(out);
+}
+
+status_t IncidentService::cmd_help(FILE* out) {
+ fprintf(out, "usage: adb shell cmd incident privacy print <section_id>\n");
+ fprintf(out, "usage: adb shell cmd incident privacy parse <section_id> < proto.txt\n");
+ fprintf(out, " Prints/parses for the section id.\n");
+ fprintf(out, "\n");
+ fprintf(out, "usage: adb shell cmd incident throttler\n");
+ fprintf(out, " Prints the current throttler state\n");
+ return NO_ERROR;
+}
+
+static void printPrivacy(const Privacy* p, FILE* out, String8 indent) {
+ if (p == NULL) return;
+ fprintf(out, "%sid:%d, type:%d, dest:%d\n", indent.string(), p->field_id, p->type, p->dest);
+ if (p->children == NULL) return;
+ for (int i = 0; p->children[i] != NULL; i++) { // NULL-terminated.
+ printPrivacy(p->children[i], out, indent + " ");
+ }
+}
+
+status_t IncidentService::cmd_privacy(FILE* in, FILE* out, FILE* err, Vector<String8>& args) {
+ const int argCount = args.size();
+ if (argCount >= 3) {
+ String8 opt = args[1];
+ int sectionId = atoi(args[2].string());
+
+ const Privacy* p = get_privacy_of_section(sectionId);
+ if (p == NULL) {
+ fprintf(err, "Can't find section id %d\n", sectionId);
+ return NO_ERROR;
+ }
+ fprintf(err, "Get privacy for %d\n", sectionId);
+ if (opt == "print") {
+ printPrivacy(p, out, String8(""));
+ } else if (opt == "parse") {
+ FdBuffer buf;
+ status_t error = buf.read(fileno(in), 60000);
+ if (error != NO_ERROR) {
+ fprintf(err, "Error reading from stdin\n");
+ return error;
+ }
+ fprintf(err, "Read %zu bytes\n", buf.size());
+ auto data = buf.data();
+ PrivacyBuffer pBuf(p, data);
+
+ PrivacySpec spec = PrivacySpec::new_spec(argCount > 3 ? atoi(args[3]) : -1);
+ error = pBuf.strip(spec);
+ if (error != NO_ERROR) {
+ fprintf(err, "Error strip pii fields with spec %d\n", spec.dest);
+ return error;
+ }
+ return pBuf.flush(fileno(out));
+ }
+ } else {
+ return cmd_help(out);
+ }
+ return NO_ERROR;
+}