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