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