diff options
Diffstat (limited to 'cmds/incidentd/src/IncidentService.cpp')
-rw-r--r-- | cmds/incidentd/src/IncidentService.cpp | 294 |
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; +} |