1 /*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #define DEBUG false
17 #include "Log.h"
18
19 #include "IncidentService.h"
20
21 #include "FdBuffer.h"
22 #include "PrivacyFilter.h"
23 #include "Reporter.h"
24 #include "incidentd_util.h"
25 #include "section_list.h"
26
27 #include <android/os/IncidentReportArgs.h>
28 #include <binder/IPCThreadState.h>
29 #include <binder/IResultReceiver.h>
30 #include <binder/IServiceManager.h>
31 #include <binder/IShellCallback.h>
32 #include <log/log.h>
33 #include <private/android_filesystem_config.h>
34 #include <utils/Looper.h>
35 #include <thread>
36
37 #include <unistd.h>
38
39 enum {
40 WHAT_TAKE_REPORT = 1,
41 WHAT_SEND_BROADCASTS = 2
42 };
43
44 #define DEFAULT_DELAY_NS (1000000000LL)
45
46 #define DEFAULT_BYTES_SIZE_LIMIT (96 * 1024 * 1024) // 96MB
47 #define DEFAULT_REFACTORY_PERIOD_MS (24 * 60 * 60 * 1000) // 1 Day
48
49 // Skip these sections (for dumpstate only)
50 // Skip logs (1100 - 1108) and traces (1200 - 1202) because they are already in the bug report.
51 #define SKIPPED_DUMPSTATE_SECTIONS { \
52 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, /* Logs */ \
53 1200, 1201, 1202, /* Native, hal, java traces */ }
54
55 namespace android {
56 namespace os {
57 namespace incidentd {
58
59 String16 const APPROVE_INCIDENT_REPORTS("android.permission.APPROVE_INCIDENT_REPORTS");
60 String16 const DUMP_PERMISSION("android.permission.DUMP");
61 String16 const USAGE_STATS_PERMISSION("android.permission.PACKAGE_USAGE_STATS");
62
checkIncidentPermissions(const IncidentReportArgs & args)63 static Status checkIncidentPermissions(const IncidentReportArgs& args) {
64 uid_t callingUid = IPCThreadState::self()->getCallingUid();
65 pid_t callingPid = IPCThreadState::self()->getCallingPid();
66 if (callingUid == AID_ROOT || callingUid == AID_SHELL) {
67 // Root and shell are ok.
68 return Status::ok();
69 }
70
71 if (checkCallingPermission(APPROVE_INCIDENT_REPORTS)) {
72 // Permission controller (this is a singleton permission that is always granted
73 // exactly for PermissionController) is allowed to access incident reports
74 // so it can show the user info about what they are approving.
75 return Status::ok();
76 }
77
78 // checking calling permission.
79 if (!checkCallingPermission(DUMP_PERMISSION)) {
80 ALOGW("Calling pid %d and uid %d does not have permission: android.permission.DUMP",
81 callingPid, callingUid);
82 return Status::fromExceptionCode(
83 Status::EX_SECURITY,
84 "Calling process does not have permission: android.permission.DUMP");
85 }
86 if (!checkCallingPermission(USAGE_STATS_PERMISSION)) {
87 ALOGW("Calling pid %d and uid %d does not have permission: android.permission.USAGE_STATS",
88 callingPid, callingUid);
89 return Status::fromExceptionCode(
90 Status::EX_SECURITY,
91 "Calling process does not have permission: android.permission.USAGE_STATS");
92 }
93
94 // checking calling request uid permission.
95 switch (args.getPrivacyPolicy()) {
96 case PRIVACY_POLICY_LOCAL:
97 if (callingUid != AID_SHELL && callingUid != AID_ROOT) {
98 ALOGW("Calling pid %d and uid %d does not have permission to get local data.",
99 callingPid, callingUid);
100 return Status::fromExceptionCode(
101 Status::EX_SECURITY,
102 "Calling process does not have permission to get local data.");
103 }
104 break;
105 case PRIVACY_POLICY_EXPLICIT:
106 if (callingUid != AID_SHELL && callingUid != AID_ROOT && callingUid != AID_STATSD &&
107 callingUid != AID_SYSTEM) {
108 ALOGW("Calling pid %d and uid %d does not have permission to get explicit data.",
109 callingPid, callingUid);
110 return Status::fromExceptionCode(
111 Status::EX_SECURITY,
112 "Calling process does not have permission to get explicit data.");
113 }
114 break;
115 }
116 return Status::ok();
117 }
118
build_uri(const string & pkg,const string & cls,const string & id)119 static string build_uri(const string& pkg, const string& cls, const string& id) {
120 return "content://android.os.IncidentManager/pending?pkg="
121 + pkg + "&receiver=" + cls + "&r=" + id;
122 }
123
124 // ================================================================================
ReportHandler(const sp<WorkDirectory> & workDirectory,const sp<Broadcaster> & broadcaster,const sp<Looper> & handlerLooper,const sp<Throttler> & throttler,const vector<BringYourOwnSection * > & registeredSections)125 ReportHandler::ReportHandler(const sp<WorkDirectory>& workDirectory,
126 const sp<Broadcaster>& broadcaster,
127 const sp<Looper>& handlerLooper,
128 const sp<Throttler>& throttler,
129 const vector<BringYourOwnSection*>& registeredSections)
130 :mLock(),
131 mWorkDirectory(workDirectory),
132 mBroadcaster(broadcaster),
133 mHandlerLooper(handlerLooper),
134 mBacklogDelay(DEFAULT_DELAY_NS),
135 mThrottler(throttler),
136 mRegisteredSections(registeredSections),
137 mBatch(new ReportBatch()) {
138 }
139
~ReportHandler()140 ReportHandler::~ReportHandler() {
141 }
142
handleMessage(const Message & message)143 void ReportHandler::handleMessage(const Message& message) {
144 switch (message.what) {
145 case WHAT_TAKE_REPORT:
146 take_report();
147 break;
148 case WHAT_SEND_BROADCASTS:
149 send_broadcasts();
150 break;
151 }
152 }
153
schedulePersistedReport(const IncidentReportArgs & args)154 void ReportHandler::schedulePersistedReport(const IncidentReportArgs& args) {
155 unique_lock<mutex> lock(mLock);
156 mBatch->addPersistedReport(args);
157 mHandlerLooper->removeMessages(this, WHAT_TAKE_REPORT);
158 mHandlerLooper->sendMessage(this, Message(WHAT_TAKE_REPORT));
159 }
160
scheduleStreamingReport(const IncidentReportArgs & args,const sp<IIncidentReportStatusListener> & listener,int streamFd)161 void ReportHandler::scheduleStreamingReport(const IncidentReportArgs& args,
162 const sp<IIncidentReportStatusListener>& listener, int streamFd) {
163 unique_lock<mutex> lock(mLock);
164 mBatch->addStreamingReport(args, listener, streamFd);
165 mHandlerLooper->removeMessages(this, WHAT_TAKE_REPORT);
166 mHandlerLooper->sendMessage(this, Message(WHAT_TAKE_REPORT));
167 }
168
scheduleSendBacklog()169 void ReportHandler::scheduleSendBacklog() {
170 unique_lock<mutex> lock(mLock);
171 mBacklogDelay = DEFAULT_DELAY_NS;
172 schedule_send_broadcasts_locked();
173 }
174
schedule_send_broadcasts_locked()175 void ReportHandler::schedule_send_broadcasts_locked() {
176 mHandlerLooper->removeMessages(this, WHAT_SEND_BROADCASTS);
177 mHandlerLooper->sendMessageDelayed(mBacklogDelay, this, Message(WHAT_SEND_BROADCASTS));
178 }
179
take_report()180 void ReportHandler::take_report() {
181 // Cycle the batch and throttle.
182 sp<ReportBatch> batch;
183 {
184 unique_lock<mutex> lock(mLock);
185 batch = mThrottler->filterBatch(mBatch);
186 }
187
188 if (batch->empty()) {
189 // Nothing to do.
190 return;
191 }
192
193 sp<Reporter> reporter = new Reporter(mWorkDirectory, batch, mRegisteredSections);
194
195 // Take the report, which might take a while. More requests might queue
196 // up while we're doing this, and we'll handle them in their next batch.
197 // TODO: We should further rate-limit the reports to no more than N per time-period.
198 // TODO: Move this inside reporter.
199 size_t reportByteSize = 0;
200 reporter->runReport(&reportByteSize);
201
202 // Tell the throttler how big it was, for the next throttling.
203 // TODO: This still isn't ideal. The throttler really should just track the
204 // persisted reqeusts, but changing Reporter::runReport() to track that individually
205 // will be a big change.
206 if (batch->hasPersistedReports()) {
207 mThrottler->addReportSize(reportByteSize);
208 }
209
210 // Kick off the next steps, one of which is to send any new or otherwise remaining
211 // approvals, and one of which is to send any new or remaining broadcasts.
212 {
213 unique_lock<mutex> lock(mLock);
214 schedule_send_broadcasts_locked();
215 }
216 }
217
send_broadcasts()218 void ReportHandler::send_broadcasts() {
219 Broadcaster::broadcast_status_t result = mBroadcaster->sendBroadcasts();
220 if (result == Broadcaster::BROADCASTS_FINISHED) {
221 // We're done.
222 unique_lock<mutex> lock(mLock);
223 mBacklogDelay = DEFAULT_DELAY_NS;
224 } else if (result == Broadcaster::BROADCASTS_REPEAT) {
225 // It worked, but there are more.
226 unique_lock<mutex> lock(mLock);
227 mBacklogDelay = DEFAULT_DELAY_NS;
228 schedule_send_broadcasts_locked();
229 } else if (result == Broadcaster::BROADCASTS_BACKOFF) {
230 // There was a failure. Exponential backoff.
231 unique_lock<mutex> lock(mLock);
232 mBacklogDelay *= 2;
233 ALOGI("Error sending to dropbox. Trying again in %lld minutes",
234 (mBacklogDelay / (1000000000LL * 60)));
235 schedule_send_broadcasts_locked();
236 }
237 }
238
239 // ================================================================================
IncidentService(const sp<Looper> & handlerLooper)240 IncidentService::IncidentService(const sp<Looper>& handlerLooper) {
241 mThrottler = new Throttler(DEFAULT_BYTES_SIZE_LIMIT, DEFAULT_REFACTORY_PERIOD_MS);
242 mWorkDirectory = new WorkDirectory();
243 mBroadcaster = new Broadcaster(mWorkDirectory);
244 mHandler = new ReportHandler(mWorkDirectory, mBroadcaster, handlerLooper,
245 mThrottler, mRegisteredSections);
246 mBroadcaster->setHandler(mHandler);
247 }
248
~IncidentService()249 IncidentService::~IncidentService() {}
250
reportIncident(const IncidentReportArgs & args)251 Status IncidentService::reportIncident(const IncidentReportArgs& args) {
252 IncidentReportArgs argsCopy(args);
253
254 // Validate that the privacy policy is one of the real ones.
255 // If it isn't, clamp it to the next more restrictive real one.
256 argsCopy.setPrivacyPolicy(cleanup_privacy_policy(args.getPrivacyPolicy()));
257
258 // TODO: Check that the broadcast recevier has the proper permissions
259 // TODO: Maybe we should consider relaxing the permissions if it's going to
260 // dropbox, but definitely not if it's going to the broadcaster.
261 Status status = checkIncidentPermissions(args);
262 if (!status.isOk()) {
263 return status;
264 }
265
266 // If they asked for the LOCAL privacy policy, give them EXPLICT. LOCAL has to
267 // be streamed. (This only applies to shell/root, because everyone else would have
268 // been rejected by checkIncidentPermissions()).
269 if (argsCopy.getPrivacyPolicy() < PRIVACY_POLICY_EXPLICIT) {
270 ALOGI("Demoting privacy policy to EXPLICT for persisted report.");
271 argsCopy.setPrivacyPolicy(PRIVACY_POLICY_EXPLICIT);
272 }
273
274 // If they didn't specify a component, use dropbox.
275 if (argsCopy.receiverPkg().length() == 0 && argsCopy.receiverCls().length() == 0) {
276 argsCopy.setReceiverPkg(DROPBOX_SENTINEL.getPackageName());
277 argsCopy.setReceiverCls(DROPBOX_SENTINEL.getClassName());
278 }
279
280 mHandler->schedulePersistedReport(argsCopy);
281
282 return Status::ok();
283 }
284
reportIncidentToStream(const IncidentReportArgs & args,const sp<IIncidentReportStatusListener> & listener,unique_fd stream)285 Status IncidentService::reportIncidentToStream(const IncidentReportArgs& args,
286 const sp<IIncidentReportStatusListener>& listener,
287 unique_fd stream) {
288 IncidentReportArgs argsCopy(args);
289
290 // Streaming reports can not also be broadcast.
291 argsCopy.setReceiverPkg("");
292 argsCopy.setReceiverCls("");
293
294 // Validate that the privacy policy is one of the real ones.
295 // If it isn't, clamp it to the next more restrictive real one.
296 argsCopy.setPrivacyPolicy(cleanup_privacy_policy(args.getPrivacyPolicy()));
297
298 Status status = checkIncidentPermissions(argsCopy);
299 if (!status.isOk()) {
300 return status;
301 }
302
303 // The ReportRequest takes ownership of the fd, so we need to dup it.
304 int fd = dup(stream.get());
305 if (fd < 0) {
306 return Status::fromStatusT(-errno);
307 }
308
309 mHandler->scheduleStreamingReport(argsCopy, listener, fd);
310
311 return Status::ok();
312 }
313
reportIncidentToDumpstate(unique_fd stream,const sp<IIncidentReportStatusListener> & listener)314 Status IncidentService::reportIncidentToDumpstate(unique_fd stream,
315 const sp<IIncidentReportStatusListener>& listener) {
316 uid_t caller = IPCThreadState::self()->getCallingUid();
317 if (caller != AID_ROOT && caller != AID_SHELL) {
318 ALOGW("Calling uid %d does not have permission: only ROOT or SHELL allowed", caller);
319 return Status::fromExceptionCode(Status::EX_SECURITY, "Only ROOT or SHELL allowed");
320 }
321
322 ALOGD("Stream incident report to dumpstate");
323 IncidentReportArgs incidentArgs;
324 // Privacy policy for dumpstate incident reports is always EXPLICIT.
325 incidentArgs.setPrivacyPolicy(PRIVACY_POLICY_EXPLICIT);
326
327 int skipped[] = SKIPPED_DUMPSTATE_SECTIONS;
328 for (const Section** section = SECTION_LIST; *section; section++) {
329 const int id = (*section)->id;
330 if (std::find(std::begin(skipped), std::end(skipped), id) == std::end(skipped)
331 && !section_requires_specific_mention(id)) {
332 incidentArgs.addSection(id);
333 }
334 }
335 for (const Section* section : mRegisteredSections) {
336 if (!section_requires_specific_mention(section->id)) {
337 incidentArgs.addSection(section->id);
338 }
339 }
340
341 // The ReportRequest takes ownership of the fd, so we need to dup it.
342 int fd = dup(stream.get());
343 if (fd < 0) {
344 return Status::fromStatusT(-errno);
345 }
346
347 mHandler->scheduleStreamingReport(incidentArgs, listener, fd);
348
349 return Status::ok();
350 }
351
registerSection(const int id,const String16 & name16,const sp<IIncidentDumpCallback> & callback)352 Status IncidentService::registerSection(const int id, const String16& name16,
353 const sp<IIncidentDumpCallback>& callback) {
354 const String8 name = String8(name16);
355 const uid_t callingUid = IPCThreadState::self()->getCallingUid();
356 ALOGI("Uid %d registers section %d '%s'", callingUid, id, name.c_str());
357 if (callback == nullptr) {
358 return Status::fromExceptionCode(Status::EX_NULL_POINTER);
359 }
360 for (int i = 0; i < mRegisteredSections.size(); i++) {
361 if (mRegisteredSections.at(i)->id == id) {
362 if (mRegisteredSections.at(i)->uid != callingUid) {
363 ALOGW("Error registering section %d: calling uid does not match", id);
364 return Status::fromExceptionCode(Status::EX_SECURITY);
365 }
366 mRegisteredSections.at(i) = new BringYourOwnSection(id, name.c_str(), callingUid, callback);
367 return Status::ok();
368 }
369 }
370 mRegisteredSections.push_back(new BringYourOwnSection(id, name.c_str(), callingUid, callback));
371 return Status::ok();
372 }
373
unregisterSection(const int id)374 Status IncidentService::unregisterSection(const int id) {
375 uid_t callingUid = IPCThreadState::self()->getCallingUid();
376 ALOGI("Uid %d unregisters section %d", callingUid, id);
377
378 for (auto it = mRegisteredSections.begin(); it != mRegisteredSections.end(); it++) {
379 if ((*it)->id == id) {
380 if ((*it)->uid != callingUid) {
381 ALOGW("Error unregistering section %d: calling uid does not match", id);
382 return Status::fromExceptionCode(Status::EX_SECURITY);
383 }
384 mRegisteredSections.erase(it);
385 return Status::ok();
386 }
387 }
388 ALOGW("Section %d not found", id);
389 return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
390 }
391
systemRunning()392 Status IncidentService::systemRunning() {
393 if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
394 return Status::fromExceptionCode(Status::EX_SECURITY,
395 "Only system uid can call systemRunning");
396 }
397
398 // When system_server is up and running, schedule the dropbox task to run.
399 mBroadcaster->reset();
400 mHandler->scheduleSendBacklog();
401
402 return Status::ok();
403 }
404
getIncidentReportList(const String16 & pkg16,const String16 & cls16,vector<String16> * result)405 Status IncidentService::getIncidentReportList(const String16& pkg16, const String16& cls16,
406 vector<String16>* result) {
407 status_t err;
408 const string pkg(String8(pkg16).string());
409 const string cls(String8(cls16).string());
410
411 // List the reports
412 vector<sp<ReportFile>> all;
413 err = mWorkDirectory->getReports(&all, 0);
414 if (err != NO_ERROR) {
415 return Status::fromStatusT(err);
416 }
417
418 // Find the ones that match pkg and cls.
419 for (sp<ReportFile>& file: all) {
420 err = file->loadEnvelope();
421 if (err != NO_ERROR) {
422 continue;
423 }
424 const ReportFileProto& envelope = file->getEnvelope();
425 size_t reportCount = envelope.report_size();
426 for (int reportIndex = 0; reportIndex < reportCount; reportIndex++) {
427 const ReportFileProto_Report& report = envelope.report(reportIndex);
428 if (pkg == report.pkg() && cls == report.cls()) {
429 result->push_back(String16(build_uri(pkg, cls, file->getId()).c_str()));
430 break;
431 }
432 }
433 }
434
435 return Status::ok();
436 }
437
getIncidentReport(const String16 & pkg16,const String16 & cls16,const String16 & id16,IncidentManager::IncidentReport * result)438 Status IncidentService::getIncidentReport(const String16& pkg16, const String16& cls16,
439 const String16& id16, IncidentManager::IncidentReport* result) {
440 status_t err;
441
442 const string pkg(String8(pkg16).string());
443 const string cls(String8(cls16).string());
444 const string id(String8(id16).string());
445
446 IncidentReportArgs args;
447 sp<ReportFile> file = mWorkDirectory->getReport(pkg, cls, id, &args);
448 if (file != nullptr) {
449 // Create pipe
450 int fds[2];
451 if (pipe(fds) != 0) {
452 ALOGW("Error opening pipe to filter incident report: %s",
453 file->getDataFileName().c_str());
454 return Status::ok();
455 }
456 result->setTimestampNs(file->getTimestampNs());
457 result->setPrivacyPolicy(file->getEnvelope().privacy_policy());
458 result->takeFileDescriptor(fds[0]);
459 int writeFd = fds[1];
460 // spawn a thread to write the data. Release the writeFd ownership to the thread.
461 thread th([file, writeFd, args]() { file->startFilteringData(writeFd, args); });
462
463 th.detach();
464 }
465
466 return Status::ok();
467 }
468
deleteIncidentReports(const String16 & pkg16,const String16 & cls16,const String16 & id16)469 Status IncidentService::deleteIncidentReports(const String16& pkg16, const String16& cls16,
470 const String16& id16) {
471 const string pkg(String8(pkg16).string());
472 const string cls(String8(cls16).string());
473 const string id(String8(id16).string());
474
475 sp<ReportFile> file = mWorkDirectory->getReport(pkg, cls, id, nullptr);
476 if (file != nullptr) {
477 mWorkDirectory->commit(file, pkg, cls);
478 }
479 mBroadcaster->clearBroadcasts(pkg, cls, id);
480
481 return Status::ok();
482 }
483
deleteAllIncidentReports(const String16 & pkg16)484 Status IncidentService::deleteAllIncidentReports(const String16& pkg16) {
485 const string pkg(String8(pkg16).string());
486
487 mWorkDirectory->commitAll(pkg);
488 mBroadcaster->clearPackageBroadcasts(pkg);
489
490 return Status::ok();
491 }
492
493 /**
494 * Implement our own because the default binder implementation isn't
495 * properly handling SHELL_COMMAND_TRANSACTION.
496 */
onTransact(uint32_t code,const Parcel & data,Parcel * reply,uint32_t flags)497 status_t IncidentService::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
498 uint32_t flags) {
499 status_t err;
500
501 switch (code) {
502 case SHELL_COMMAND_TRANSACTION: {
503 int in = data.readFileDescriptor();
504 int out = data.readFileDescriptor();
505 int err = data.readFileDescriptor();
506 int argc = data.readInt32();
507 Vector<String8> args;
508 for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
509 args.add(String8(data.readString16()));
510 }
511 sp<IShellCallback> shellCallback = IShellCallback::asInterface(data.readStrongBinder());
512 sp<IResultReceiver> resultReceiver =
513 IResultReceiver::asInterface(data.readStrongBinder());
514
515 FILE* fin = fdopen(in, "r");
516 FILE* fout = fdopen(out, "w");
517 FILE* ferr = fdopen(err, "w");
518
519 if (fin == NULL || fout == NULL || ferr == NULL) {
520 resultReceiver->send(NO_MEMORY);
521 } else {
522 err = command(fin, fout, ferr, args);
523 resultReceiver->send(err);
524 }
525
526 if (fin != NULL) {
527 fflush(fin);
528 fclose(fin);
529 }
530 if (fout != NULL) {
531 fflush(fout);
532 fclose(fout);
533 }
534 if (fout != NULL) {
535 fflush(ferr);
536 fclose(ferr);
537 }
538
539 return NO_ERROR;
540 } break;
541 default: { return BnIncidentManager::onTransact(code, data, reply, flags); }
542 }
543 }
544
command(FILE * in,FILE * out,FILE * err,Vector<String8> & args)545 status_t IncidentService::command(FILE* in, FILE* out, FILE* err, Vector<String8>& args) {
546 const int argCount = args.size();
547
548 if (argCount >= 1) {
549 if (!args[0].compare(String8("privacy"))) {
550 return cmd_privacy(in, out, err, args);
551 }
552 if (!args[0].compare(String8("throttler"))) {
553 mThrottler->dump(out);
554 return NO_ERROR;
555 }
556 if (!args[0].compare(String8("section"))) {
557 int id = atoi(args[1]);
558 int idx = 0;
559 while (SECTION_LIST[idx] != NULL) {
560 const Section* section = SECTION_LIST[idx];
561 if (section->id == id) {
562 fprintf(out, "Section[%d] %s\n", id, section->name.string());
563 break;
564 }
565 idx++;
566 }
567 return NO_ERROR;
568 }
569 }
570 return cmd_help(out);
571 }
572
cmd_help(FILE * out)573 status_t IncidentService::cmd_help(FILE* out) {
574 fprintf(out, "usage: adb shell cmd incident privacy print <section_id>\n");
575 fprintf(out, "usage: adb shell cmd incident privacy parse <section_id> < proto.txt\n");
576 fprintf(out, " Prints/parses for the section id.\n\n");
577 fprintf(out, "usage: adb shell cmd incident section <section_id>\n");
578 fprintf(out, " Prints section id and its name.\n\n");
579 fprintf(out, "usage: adb shell cmd incident throttler\n");
580 fprintf(out, " Prints the current throttler state\n");
581 return NO_ERROR;
582 }
583
printPrivacy(const Privacy * p,FILE * out,String8 indent)584 static void printPrivacy(const Privacy* p, FILE* out, String8 indent) {
585 if (p == NULL) return;
586 fprintf(out, "%sid:%d, type:%d, dest:%d\n", indent.string(), p->field_id, p->type, p->policy);
587 if (p->children == NULL) return;
588 for (int i = 0; p->children[i] != NULL; i++) { // NULL-terminated.
589 printPrivacy(p->children[i], out, indent + " ");
590 }
591 }
592
cmd_privacy(FILE * in,FILE * out,FILE * err,Vector<String8> & args)593 status_t IncidentService::cmd_privacy(FILE* in, FILE* out, FILE* err, Vector<String8>& args) {
594 (void)in;
595
596 const int argCount = args.size();
597 if (argCount >= 3) {
598 String8 opt = args[1];
599 int sectionId = atoi(args[2].string());
600
601 const Privacy* p = get_privacy_of_section(sectionId);
602 if (p == NULL) {
603 fprintf(err, "Can't find section id %d\n", sectionId);
604 return NO_ERROR;
605 }
606 fprintf(err, "Get privacy for %d\n", sectionId);
607 if (opt == "print") {
608 printPrivacy(p, out, String8(""));
609 } else if (opt == "parse") {
610 /*
611 FdBuffer buf;
612 status_t error = buf.read(fileno(in), 60000);
613 if (error != NO_ERROR) {
614 fprintf(err, "Error reading from stdin\n");
615 return error;
616 }
617 fprintf(err, "Read %zu bytes\n", buf.size());
618 PrivacyFilter pBuf(p, buf.data());
619
620 PrivacySpec spec = PrivacySpec::new_spec(argCount > 3 ? atoi(args[3]) : -1);
621 error = pBuf.strip(spec);
622 if (error != NO_ERROR) {
623 fprintf(err, "Error strip pii fields with spec %d\n", spec.policy);
624 return error;
625 }
626 return pBuf.flush(fileno(out));
627 */
628 return -1;
629 }
630 } else {
631 return cmd_help(out);
632 }
633 return NO_ERROR;
634 }
635
636 } // namespace incidentd
637 } // namespace os
638 } // namespace android
639