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 
17 #define LOG_TAG "incident"
18 
19 #include "incident_sections.h"
20 
21 #include <android/os/BnIncidentReportStatusListener.h>
22 #include <android/os/IIncidentManager.h>
23 #include <android/os/IncidentReportArgs.h>
24 #include <android/util/ProtoOutputStream.h>
25 #include <binder/IPCThreadState.h>
26 #include <binder/IServiceManager.h>
27 #include <utils/Looper.h>
28 
29 #include <cstring>
30 #include <fcntl.h>
31 #include <getopt.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 
36 using namespace android;
37 using namespace android::base;
38 using namespace android::binder;
39 using namespace android::os;
40 using android::util::FIELD_COUNT_SINGLE;
41 using android::util::FIELD_TYPE_STRING;
42 using android::util::ProtoOutputStream;
43 
44 // ================================================================================
45 class StatusListener : public BnIncidentReportStatusListener {
46 public:
47     StatusListener();
48     virtual ~StatusListener();
49 
50     virtual Status onReportStarted();
51     virtual Status onReportSectionStatus(int32_t section, int32_t status);
52     virtual Status onReportServiceStatus(const String16& service, int32_t status);
53     virtual Status onReportFinished();
54     virtual Status onReportFailed();
55 
56     int getExitCodeOrElse(int defaultCode);
57  private:
58     int mExitCode;
59 };
60 
StatusListener()61 StatusListener::StatusListener(): mExitCode(-1)
62 {
63 }
64 
~StatusListener()65 StatusListener::~StatusListener()
66 {
67 }
68 
69 Status
onReportStarted()70 StatusListener::onReportStarted()
71 {
72     return Status::ok();
73 }
74 
75 Status
onReportSectionStatus(int32_t section,int32_t status)76 StatusListener::onReportSectionStatus(int32_t section, int32_t status)
77 {
78     fprintf(stderr, "section %d status %d\n", section, status);
79     ALOGD("section %d status %d\n", section, status);
80     return Status::ok();
81 }
82 
83 Status
onReportServiceStatus(const String16 & service,int32_t status)84 StatusListener::onReportServiceStatus(const String16& service, int32_t status)
85 {
86     fprintf(stderr, "service '%s' status %d\n", String8(service).string(), status);
87     ALOGD("service '%s' status %d\n", String8(service).string(), status);
88     return Status::ok();
89 }
90 
91 Status
onReportFinished()92 StatusListener::onReportFinished()
93 {
94     fprintf(stderr, "done\n");
95     ALOGD("done\n");
96     mExitCode = 0;
97     return Status::ok();
98 }
99 
100 Status
onReportFailed()101 StatusListener::onReportFailed()
102 {
103     fprintf(stderr, "failed\n");
104     ALOGD("failed\n");
105     mExitCode = 1;
106     return Status::ok();
107 }
108 
109 int
getExitCodeOrElse(int defaultCode)110 StatusListener::getExitCodeOrElse(int defaultCode) {
111     return mExitCode == -1 ? defaultCode : mExitCode;
112 }
113 
114 // ================================================================================
section_list(FILE * out)115 static void section_list(FILE* out) {
116     IncidentSection sections[INCIDENT_SECTION_COUNT];
117     int i = 0;
118     int j = 0;
119     // sort the sections based on id
120     while (i < INCIDENT_SECTION_COUNT) {
121         IncidentSection curr = INCIDENT_SECTIONS[i];
122         for (int k = 0; k < j; k++) {
123             if (curr.id > sections[k].id) {
124                 continue;
125             }
126             IncidentSection tmp = curr;
127             curr = sections[k];
128             sections[k] = tmp;
129         }
130         sections[j] = curr;
131         i++;
132         j++;
133     }
134 
135     fprintf(out, "available sections:\n");
136     for (int i = 0; i < INCIDENT_SECTION_COUNT; ++i) {
137         fprintf(out, "id: %4d, name: %s\n", sections[i].id, sections[i].name);
138     }
139 }
140 
141 // ================================================================================
142 static IncidentSection const*
find_section(const char * name)143 find_section(const char* name)
144 {
145     ssize_t low = 0;
146     ssize_t high = INCIDENT_SECTION_COUNT - 1;
147 
148     while (low <= high) {
149         ssize_t mid = (low + high) / 2;
150         IncidentSection const* section = INCIDENT_SECTIONS + mid;
151 
152         int cmp = strcmp(section->name, name);
153         if (cmp < 0) {
154             low = mid + 1;
155         } else if (cmp > 0) {
156             high = mid - 1;
157         } else {
158             return section;
159         }
160     }
161     return NULL;
162 }
163 
164 // ================================================================================
165 static int
get_privacy_policy(const char * arg)166 get_privacy_policy(const char* arg)
167 {
168     if (strcmp(arg, "L") == 0
169         || strcmp(arg, "LOCAL") == 0) {
170       return PRIVACY_POLICY_LOCAL;
171     }
172     if (strcmp(arg, "E") == 0
173         || strcmp(arg, "EXPLICIT") == 0) {
174       return PRIVACY_POLICY_EXPLICIT;
175     }
176     if (strcmp(arg, "A") == 0
177         || strcmp(arg, "AUTO") == 0
178         || strcmp(arg, "AUTOMATIC") == 0) {
179       return PRIVACY_POLICY_AUTOMATIC;
180     }
181     return -1; // return the default value
182 }
183 
184 // ================================================================================
185 static bool
parse_receiver_arg(const string & arg,string * pkg,string * cls)186 parse_receiver_arg(const string& arg, string* pkg, string* cls)
187 {
188     if (arg.length() == 0) {
189         return true;
190     }
191     size_t slash = arg.find('/');
192     if (slash == string::npos) {
193         return false;
194     }
195     if (slash == 0 || slash == arg.length() - 1) {
196         return false;
197     }
198     if (arg.find('/', slash+1) != string::npos) {
199         return false;
200     }
201     pkg->assign(arg, 0, slash);
202     cls->assign(arg, slash+1);
203     if ((*cls)[0] == '.') {
204         *cls = (*pkg) + (*cls);
205     }
206     return true;
207 }
208 
209 // ================================================================================
210 static int
stream_output(const int read_fd,const int write_fd)211 stream_output(const int read_fd, const int write_fd) {
212     while (true) {
213         int amt = splice(read_fd, NULL, write_fd, NULL, 4096, 0);
214         if (amt < 0) {
215             return errno;
216         } else if (amt == 0) {
217             return 0;
218         }
219     }
220 }
221 
222 // ================================================================================
223 static void
usage(FILE * out)224 usage(FILE* out)
225 {
226     fprintf(out, "usage: incident OPTIONS [SECTION...]\n");
227     fprintf(out, "\n");
228     fprintf(out, "Takes an incident report.\n");
229     fprintf(out, "\n");
230     fprintf(out, "OPTIONS\n");
231     fprintf(out, "  -l           list available sections\n");
232     fprintf(out, "  -p           privacy spec, LOCAL, EXPLICIT or AUTOMATIC. Default AUTOMATIC.\n");
233     fprintf(out, "  -r REASON    human readable description of why the report is taken.\n");
234     fprintf(out, "  -z           gzip the incident report, i.e. pipe the output through gzip.\n");
235     fprintf(out, "\n");
236     fprintf(out, "and one of these destinations:\n");
237     fprintf(out, "  -b           (default) print the report to stdout (in proto format)\n");
238     fprintf(out, "  -d           send the report into dropbox\n");
239     fprintf(out, "  -u           print a full report to stdout for dumpstate to zip as a bug\n");
240     fprintf(out, "               report. SECTION is ignored. Should only be called by dumpstate.\n");
241     fprintf(out, "  -s PKG/CLS   send broadcast to the broadcast receiver.\n");
242     fprintf(out, "\n");
243     fprintf(out, "  SECTION     the field numbers of the incident report fields to include\n");
244     fprintf(out, "\n");
245 }
246 
247 int
main(int argc,char ** argv)248 main(int argc, char** argv)
249 {
250     Status status;
251     IncidentReportArgs args;
252     enum { DEST_UNSET, DEST_DROPBOX, DEST_STDOUT, DEST_BROADCAST, DEST_DUMPSTATE } destination = DEST_UNSET;
253     int privacyPolicy = PRIVACY_POLICY_AUTOMATIC;
254     string reason;
255     string receiverArg;
256 
257     // Parse the args
258     int opt;
259     while ((opt = getopt(argc, argv, "bhdlp:r:s:uz")) != -1) {
260         switch (opt) {
261             case 'h':
262                 usage(stdout);
263                 return 0;
264             case 'l':
265                 section_list(stdout);
266                 return 0;
267             case 'b':
268                 if (!(destination == DEST_UNSET || destination == DEST_STDOUT)) {
269                     usage(stderr);
270                     return 1;
271                 }
272                 destination = DEST_STDOUT;
273                 break;
274             case 'd':
275                 if (!(destination == DEST_UNSET || destination == DEST_DROPBOX)) {
276                     usage(stderr);
277                     return 1;
278                 }
279                 destination = DEST_DROPBOX;
280                 break;
281             case 'u':
282                 if (!(destination == DEST_UNSET || destination == DEST_DUMPSTATE)) {
283                     usage(stderr);
284                     return 1;
285                 }
286                 destination = DEST_DUMPSTATE;
287                 break;
288             case 'p':
289                 privacyPolicy = get_privacy_policy(optarg);
290                 break;
291             case 'r':
292                 if (reason.size() > 0) {
293                     usage(stderr);
294                     return 1;
295                 }
296                 reason = optarg;
297                 break;
298             case 's':
299                 if (destination != DEST_UNSET) {
300                     usage(stderr);
301                     return 1;
302                 }
303                 destination = DEST_BROADCAST;
304                 receiverArg = optarg;
305                 break;
306             case 'z':
307                 args.setGzip(true);
308                 break;
309             default:
310                 usage(stderr);
311                 return 1;
312         }
313     }
314     if (destination == DEST_UNSET) {
315         destination = DEST_STDOUT;
316     }
317 
318     string pkg;
319     string cls;
320     if (parse_receiver_arg(receiverArg, &pkg, &cls)) {
321         args.setReceiverPkg(pkg);
322         args.setReceiverCls(cls);
323     } else {
324         fprintf(stderr, "badly formatted -s package/class option: %s\n\n", receiverArg.c_str());
325         usage(stderr);
326         return 1;
327     }
328 
329     if (optind == argc) {
330         args.setAll(true);
331     } else {
332         for (int i=optind; i<argc; i++) {
333             const char* arg = argv[i];
334             char* end;
335             if (arg[0] != '\0') {
336                 int section = strtol(arg, &end, 0);
337                 if (*end == '\0') {
338                     args.addSection(section);
339                 } else {
340                     IncidentSection const* ic = find_section(arg);
341                     if (ic == NULL) {
342                         ALOGD("Invalid section: %s\n", arg);
343                         fprintf(stderr, "Invalid section: %s\n", arg);
344                         return 1;
345                     }
346                     args.addSection(ic->id);
347                 }
348             }
349         }
350     }
351     args.setPrivacyPolicy(privacyPolicy);
352 
353     if (reason.size() > 0) {
354         ProtoOutputStream proto;
355         proto.write(/* reason field id */ 2 | FIELD_TYPE_STRING | FIELD_COUNT_SINGLE, reason);
356         vector<uint8_t> header;
357         proto.serializeToVector(&header);
358         args.addHeader(header);
359     }
360 
361     // Start the thread pool.
362     sp<ProcessState> ps(ProcessState::self());
363     ps->startThreadPool();
364     ps->giveThreadPoolName();
365 
366     // Look up the service
367     sp<IIncidentManager> service = interface_cast<IIncidentManager>(
368             defaultServiceManager()->getService(android::String16("incident")));
369     if (service == NULL) {
370         fprintf(stderr, "Couldn't look up the incident service\n");
371         return 1;
372     }
373 
374     // Construct the stream
375     int fds[2];
376     pipe(fds);
377 
378     unique_fd readEnd(fds[0]);
379     unique_fd writeEnd(fds[1]);
380 
381     if (destination == DEST_STDOUT) {
382         // Call into the service
383         sp<StatusListener> listener(new StatusListener());
384         status = service->reportIncidentToStream(args, listener, std::move(writeEnd));
385 
386         if (!status.isOk()) {
387             fprintf(stderr, "reportIncident returned \"%s\"\n", status.toString8().string());
388             return 1;
389         }
390 
391         // Wait for the result and print out the data they send.
392         //IPCThreadState::self()->joinThreadPool();
393         return listener->getExitCodeOrElse(stream_output(fds[0], STDOUT_FILENO));
394     } else if (destination == DEST_DUMPSTATE) {
395         // Call into the service
396         sp<StatusListener> listener(new StatusListener());
397         status = service->reportIncidentToDumpstate(std::move(writeEnd), listener);
398         if (!status.isOk()) {
399             fprintf(stderr, "reportIncident returned \"%s\"\n", status.toString8().string());
400             return 1;
401         }
402         return listener->getExitCodeOrElse(stream_output(fds[0], STDOUT_FILENO));
403     } else {
404         status = service->reportIncident(args);
405         if (!status.isOk()) {
406             fprintf(stderr, "reportIncident returned \"%s\"\n", status.toString8().string());
407             return 1;
408         } else {
409             return 0;
410         }
411     }
412 
413 }
414