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