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 57 StatusListener::StatusListener() 58 { 59 } 60 61 StatusListener::~StatusListener() 62 { 63 } 64 65 Status 66 StatusListener::onReportStarted() 67 { 68 return Status::ok(); 69 } 70 71 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 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 88 StatusListener::onReportFinished() 89 { 90 fprintf(stderr, "done\n"); 91 ALOGD("done\n"); 92 exit(0); 93 return Status::ok(); 94 } 95 96 Status 97 StatusListener::onReportFailed() 98 { 99 fprintf(stderr, "failed\n"); 100 ALOGD("failed\n"); 101 exit(1); 102 return Status::ok(); 103 } 104 105 // ================================================================================ 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* 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 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 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 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 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