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