1 /* 2 * Copyright (C) 2008 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 package android.app; 18 19 import android.content.ComponentName; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.pm.ApplicationInfo; 23 import android.content.pm.PackageManager; 24 import android.content.pm.ResolveInfo; 25 import android.os.Parcel; 26 import android.os.Parcelable; 27 import android.os.SystemProperties; 28 import android.provider.Settings; 29 import android.util.Printer; 30 import android.util.Slog; 31 import com.android.internal.util.FastPrintWriter; 32 33 import java.io.PrintWriter; 34 import java.io.StringWriter; 35 36 /** 37 * Describes an application error. 38 * 39 * A report has a type, which is one of 40 * <ul> 41 * <li> {@link #TYPE_NONE} uninitialized instance of {@link ApplicationErrorReport}. 42 * <li> {@link #TYPE_CRASH} application crash. Information about the crash 43 * is stored in {@link #crashInfo}. 44 * <li> {@link #TYPE_ANR} application not responding. Information about the 45 * ANR is stored in {@link #anrInfo}. 46 * <li> {@link #TYPE_BATTERY} user reported application is using too much 47 * battery. Information about the battery use is stored in {@link #batteryInfo}. 48 * <li> {@link #TYPE_RUNNING_SERVICE} user reported application is leaving an 49 * unneeded serive running. Information about the battery use is stored in 50 * {@link #runningServiceInfo}. 51 * </ul> 52 */ 53 54 public class ApplicationErrorReport implements Parcelable { 55 // System property defining error report receiver for system apps 56 static final String SYSTEM_APPS_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.system.apps"; 57 58 // System property defining default error report receiver 59 static final String DEFAULT_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.default"; 60 61 /** 62 * Uninitialized error report. 63 */ 64 public static final int TYPE_NONE = 0; 65 66 /** 67 * An error report about an application crash. 68 */ 69 public static final int TYPE_CRASH = 1; 70 71 /** 72 * An error report about an application that's not responding. 73 */ 74 public static final int TYPE_ANR = 2; 75 76 /** 77 * An error report about an application that's consuming too much battery. 78 */ 79 public static final int TYPE_BATTERY = 3; 80 81 /** 82 * A report from a user to a developer about a running service that the 83 * user doesn't think should be running. 84 */ 85 public static final int TYPE_RUNNING_SERVICE = 5; 86 87 /** 88 * Type of this report. Can be one of {@link #TYPE_NONE}, 89 * {@link #TYPE_CRASH}, {@link #TYPE_ANR}, {@link #TYPE_BATTERY}, 90 * or {@link #TYPE_RUNNING_SERVICE}. 91 */ 92 public int type; 93 94 /** 95 * Package name of the application. 96 */ 97 public String packageName; 98 99 /** 100 * Package name of the application which installed the application this 101 * report pertains to. 102 * This identifies which market the application came from. 103 */ 104 public String installerPackageName; 105 106 /** 107 * Process name of the application. 108 */ 109 public String processName; 110 111 /** 112 * Time at which the error occurred. 113 */ 114 public long time; 115 116 /** 117 * Set if the app is on the system image. 118 */ 119 public boolean systemApp; 120 121 /** 122 * If this report is of type {@link #TYPE_CRASH}, contains an instance 123 * of CrashInfo describing the crash; otherwise null. 124 */ 125 public CrashInfo crashInfo; 126 127 /** 128 * If this report is of type {@link #TYPE_ANR}, contains an instance 129 * of AnrInfo describing the ANR; otherwise null. 130 */ 131 public AnrInfo anrInfo; 132 133 /** 134 * If this report is of type {@link #TYPE_BATTERY}, contains an instance 135 * of BatteryInfo; otherwise null. 136 */ 137 public BatteryInfo batteryInfo; 138 139 /** 140 * If this report is of type {@link #TYPE_RUNNING_SERVICE}, contains an instance 141 * of RunningServiceInfo; otherwise null. 142 */ 143 public RunningServiceInfo runningServiceInfo; 144 145 /** 146 * Create an uninitialized instance of {@link ApplicationErrorReport}. 147 */ 148 public ApplicationErrorReport() { 149 } 150 151 /** 152 * Create an instance of {@link ApplicationErrorReport} initialized from 153 * a parcel. 154 */ 155 ApplicationErrorReport(Parcel in) { 156 readFromParcel(in); 157 } 158 159 public static ComponentName getErrorReportReceiver(Context context, 160 String packageName, int appFlags) { 161 // check if error reporting is enabled in secure settings 162 int enabled = Settings.Global.getInt(context.getContentResolver(), 163 Settings.Global.SEND_ACTION_APP_ERROR, 0); 164 if (enabled == 0) { 165 return null; 166 } 167 168 PackageManager pm = context.getPackageManager(); 169 170 // look for receiver in the installer package 171 String candidate = null; 172 ComponentName result = null; 173 174 try { 175 candidate = pm.getInstallerPackageName(packageName); 176 } catch (IllegalArgumentException e) { 177 // the package could already removed 178 } 179 180 if (candidate != null) { 181 result = getErrorReportReceiver(pm, packageName, candidate); 182 if (result != null) { 183 return result; 184 } 185 } 186 187 // if the error app is on the system image, look for system apps 188 // error receiver 189 if ((appFlags&ApplicationInfo.FLAG_SYSTEM) != 0) { 190 candidate = SystemProperties.get(SYSTEM_APPS_ERROR_RECEIVER_PROPERTY); 191 result = getErrorReportReceiver(pm, packageName, candidate); 192 if (result != null) { 193 return result; 194 } 195 } 196 197 // if there is a default receiver, try that 198 candidate = SystemProperties.get(DEFAULT_ERROR_RECEIVER_PROPERTY); 199 return getErrorReportReceiver(pm, packageName, candidate); 200 } 201 202 /** 203 * Return activity in receiverPackage that handles ACTION_APP_ERROR. 204 * 205 * @param pm PackageManager instance 206 * @param errorPackage package which caused the error 207 * @param receiverPackage candidate package to receive the error 208 * @return activity component within receiverPackage which handles 209 * ACTION_APP_ERROR, or null if not found 210 */ 211 static ComponentName getErrorReportReceiver(PackageManager pm, String errorPackage, 212 String receiverPackage) { 213 if (receiverPackage == null || receiverPackage.length() == 0) { 214 return null; 215 } 216 217 // break the loop if it's the error report receiver package that crashed 218 if (receiverPackage.equals(errorPackage)) { 219 return null; 220 } 221 222 Intent intent = new Intent(Intent.ACTION_APP_ERROR); 223 intent.setPackage(receiverPackage); 224 ResolveInfo info = pm.resolveActivity(intent, 0); 225 if (info == null || info.activityInfo == null) { 226 return null; 227 } 228 return new ComponentName(receiverPackage, info.activityInfo.name); 229 } 230 231 public void writeToParcel(Parcel dest, int flags) { 232 dest.writeInt(type); 233 dest.writeString(packageName); 234 dest.writeString(installerPackageName); 235 dest.writeString(processName); 236 dest.writeLong(time); 237 dest.writeInt(systemApp ? 1 : 0); 238 dest.writeInt(crashInfo != null ? 1 : 0); 239 240 switch (type) { 241 case TYPE_CRASH: 242 if (crashInfo != null) { 243 crashInfo.writeToParcel(dest, flags); 244 } 245 break; 246 case TYPE_ANR: 247 anrInfo.writeToParcel(dest, flags); 248 break; 249 case TYPE_BATTERY: 250 batteryInfo.writeToParcel(dest, flags); 251 break; 252 case TYPE_RUNNING_SERVICE: 253 runningServiceInfo.writeToParcel(dest, flags); 254 break; 255 } 256 } 257 258 public void readFromParcel(Parcel in) { 259 type = in.readInt(); 260 packageName = in.readString(); 261 installerPackageName = in.readString(); 262 processName = in.readString(); 263 time = in.readLong(); 264 systemApp = in.readInt() == 1; 265 boolean hasCrashInfo = in.readInt() == 1; 266 267 switch (type) { 268 case TYPE_CRASH: 269 crashInfo = hasCrashInfo ? new CrashInfo(in) : null; 270 anrInfo = null; 271 batteryInfo = null; 272 runningServiceInfo = null; 273 break; 274 case TYPE_ANR: 275 anrInfo = new AnrInfo(in); 276 crashInfo = null; 277 batteryInfo = null; 278 runningServiceInfo = null; 279 break; 280 case TYPE_BATTERY: 281 batteryInfo = new BatteryInfo(in); 282 anrInfo = null; 283 crashInfo = null; 284 runningServiceInfo = null; 285 break; 286 case TYPE_RUNNING_SERVICE: 287 batteryInfo = null; 288 anrInfo = null; 289 crashInfo = null; 290 runningServiceInfo = new RunningServiceInfo(in); 291 break; 292 } 293 } 294 295 /** 296 * Describes an application crash. 297 */ 298 public static class CrashInfo { 299 /** 300 * Class name of the exception that caused the crash. 301 */ 302 public String exceptionClassName; 303 304 /** 305 * Message stored in the exception. 306 */ 307 public String exceptionMessage; 308 309 /** 310 * File which the exception was thrown from. 311 */ 312 public String throwFileName; 313 314 /** 315 * Class which the exception was thrown from. 316 */ 317 public String throwClassName; 318 319 /** 320 * Method which the exception was thrown from. 321 */ 322 public String throwMethodName; 323 324 /** 325 * Line number the exception was thrown from. 326 */ 327 public int throwLineNumber; 328 329 /** 330 * Stack trace. 331 */ 332 public String stackTrace; 333 334 /** 335 * Create an uninitialized instance of CrashInfo. 336 */ 337 public CrashInfo() { 338 } 339 340 /** 341 * Create an instance of CrashInfo initialized from an exception. 342 */ 343 public CrashInfo(Throwable tr) { 344 StringWriter sw = new StringWriter(); 345 PrintWriter pw = new FastPrintWriter(sw, false, 256); 346 tr.printStackTrace(pw); 347 pw.flush(); 348 stackTrace = sanitizeString(sw.toString()); 349 exceptionMessage = tr.getMessage(); 350 351 // Populate fields with the "root cause" exception 352 Throwable rootTr = tr; 353 while (tr.getCause() != null) { 354 tr = tr.getCause(); 355 if (tr.getStackTrace() != null && tr.getStackTrace().length > 0) { 356 rootTr = tr; 357 } 358 String msg = tr.getMessage(); 359 if (msg != null && msg.length() > 0) { 360 exceptionMessage = msg; 361 } 362 } 363 364 exceptionClassName = rootTr.getClass().getName(); 365 if (rootTr.getStackTrace().length > 0) { 366 StackTraceElement trace = rootTr.getStackTrace()[0]; 367 throwFileName = trace.getFileName(); 368 throwClassName = trace.getClassName(); 369 throwMethodName = trace.getMethodName(); 370 throwLineNumber = trace.getLineNumber(); 371 } else { 372 throwFileName = "unknown"; 373 throwClassName = "unknown"; 374 throwMethodName = "unknown"; 375 throwLineNumber = 0; 376 } 377 378 exceptionMessage = sanitizeString(exceptionMessage); 379 } 380 381 /** 382 * Ensure that the string is of reasonable size, truncating from the middle if needed. 383 */ 384 private String sanitizeString(String s) { 385 int prefixLength = 10 * 1024; 386 int suffixLength = 10 * 1024; 387 int acceptableLength = prefixLength + suffixLength; 388 389 if (s != null && s.length() > acceptableLength) { 390 String replacement = 391 "\n[TRUNCATED " + (s.length() - acceptableLength) + " CHARS]\n"; 392 393 StringBuilder sb = new StringBuilder(acceptableLength + replacement.length()); 394 sb.append(s.substring(0, prefixLength)); 395 sb.append(replacement); 396 sb.append(s.substring(s.length() - suffixLength)); 397 return sb.toString(); 398 } 399 return s; 400 } 401 402 /** 403 * Create an instance of CrashInfo initialized from a Parcel. 404 */ 405 public CrashInfo(Parcel in) { 406 exceptionClassName = in.readString(); 407 exceptionMessage = in.readString(); 408 throwFileName = in.readString(); 409 throwClassName = in.readString(); 410 throwMethodName = in.readString(); 411 throwLineNumber = in.readInt(); 412 stackTrace = in.readString(); 413 } 414 415 /** 416 * Save a CrashInfo instance to a parcel. 417 */ 418 public void writeToParcel(Parcel dest, int flags) { 419 int start = dest.dataPosition(); 420 dest.writeString(exceptionClassName); 421 dest.writeString(exceptionMessage); 422 dest.writeString(throwFileName); 423 dest.writeString(throwClassName); 424 dest.writeString(throwMethodName); 425 dest.writeInt(throwLineNumber); 426 dest.writeString(stackTrace); 427 int total = dest.dataPosition()-start; 428 if (total > 20*1024) { 429 Slog.d("Error", "ERR: exClass=" + exceptionClassName); 430 Slog.d("Error", "ERR: exMsg=" + exceptionMessage); 431 Slog.d("Error", "ERR: file=" + throwFileName); 432 Slog.d("Error", "ERR: class=" + throwClassName); 433 Slog.d("Error", "ERR: method=" + throwMethodName + " line=" + throwLineNumber); 434 Slog.d("Error", "ERR: stack=" + stackTrace); 435 Slog.d("Error", "ERR: TOTAL BYTES WRITTEN: " + (dest.dataPosition()-start)); 436 } 437 } 438 439 /** 440 * Dump a CrashInfo instance to a Printer. 441 */ 442 public void dump(Printer pw, String prefix) { 443 pw.println(prefix + "exceptionClassName: " + exceptionClassName); 444 pw.println(prefix + "exceptionMessage: " + exceptionMessage); 445 pw.println(prefix + "throwFileName: " + throwFileName); 446 pw.println(prefix + "throwClassName: " + throwClassName); 447 pw.println(prefix + "throwMethodName: " + throwMethodName); 448 pw.println(prefix + "throwLineNumber: " + throwLineNumber); 449 pw.println(prefix + "stackTrace: " + stackTrace); 450 } 451 } 452 453 /** 454 * Describes an application not responding error. 455 */ 456 public static class AnrInfo { 457 /** 458 * Activity name. 459 */ 460 public String activity; 461 462 /** 463 * Description of the operation that timed out. 464 */ 465 public String cause; 466 467 /** 468 * Additional info, including CPU stats. 469 */ 470 public String info; 471 472 /** 473 * Create an uninitialized instance of AnrInfo. 474 */ 475 public AnrInfo() { 476 } 477 478 /** 479 * Create an instance of AnrInfo initialized from a Parcel. 480 */ 481 public AnrInfo(Parcel in) { 482 activity = in.readString(); 483 cause = in.readString(); 484 info = in.readString(); 485 } 486 487 /** 488 * Save an AnrInfo instance to a parcel. 489 */ 490 public void writeToParcel(Parcel dest, int flags) { 491 dest.writeString(activity); 492 dest.writeString(cause); 493 dest.writeString(info); 494 } 495 496 /** 497 * Dump an AnrInfo instance to a Printer. 498 */ 499 public void dump(Printer pw, String prefix) { 500 pw.println(prefix + "activity: " + activity); 501 pw.println(prefix + "cause: " + cause); 502 pw.println(prefix + "info: " + info); 503 } 504 } 505 506 /** 507 * Describes a battery usage report. 508 */ 509 public static class BatteryInfo { 510 /** 511 * Percentage of the battery that was used up by the process. 512 */ 513 public int usagePercent; 514 515 /** 516 * Duration in microseconds over which the process used the above 517 * percentage of battery. 518 */ 519 public long durationMicros; 520 521 /** 522 * Dump of various info impacting battery use. 523 */ 524 public String usageDetails; 525 526 /** 527 * Checkin details. 528 */ 529 public String checkinDetails; 530 531 /** 532 * Create an uninitialized instance of BatteryInfo. 533 */ 534 public BatteryInfo() { 535 } 536 537 /** 538 * Create an instance of BatteryInfo initialized from a Parcel. 539 */ 540 public BatteryInfo(Parcel in) { 541 usagePercent = in.readInt(); 542 durationMicros = in.readLong(); 543 usageDetails = in.readString(); 544 checkinDetails = in.readString(); 545 } 546 547 /** 548 * Save a BatteryInfo instance to a parcel. 549 */ 550 public void writeToParcel(Parcel dest, int flags) { 551 dest.writeInt(usagePercent); 552 dest.writeLong(durationMicros); 553 dest.writeString(usageDetails); 554 dest.writeString(checkinDetails); 555 } 556 557 /** 558 * Dump a BatteryInfo instance to a Printer. 559 */ 560 public void dump(Printer pw, String prefix) { 561 pw.println(prefix + "usagePercent: " + usagePercent); 562 pw.println(prefix + "durationMicros: " + durationMicros); 563 pw.println(prefix + "usageDetails: " + usageDetails); 564 pw.println(prefix + "checkinDetails: " + checkinDetails); 565 } 566 } 567 568 /** 569 * Describes a running service report. 570 */ 571 public static class RunningServiceInfo { 572 /** 573 * Duration in milliseconds that the service has been running. 574 */ 575 public long durationMillis; 576 577 /** 578 * Dump of debug information about the service. 579 */ 580 public String serviceDetails; 581 582 /** 583 * Create an uninitialized instance of RunningServiceInfo. 584 */ 585 public RunningServiceInfo() { 586 } 587 588 /** 589 * Create an instance of RunningServiceInfo initialized from a Parcel. 590 */ 591 public RunningServiceInfo(Parcel in) { 592 durationMillis = in.readLong(); 593 serviceDetails = in.readString(); 594 } 595 596 /** 597 * Save a RunningServiceInfo instance to a parcel. 598 */ 599 public void writeToParcel(Parcel dest, int flags) { 600 dest.writeLong(durationMillis); 601 dest.writeString(serviceDetails); 602 } 603 604 /** 605 * Dump a BatteryInfo instance to a Printer. 606 */ 607 public void dump(Printer pw, String prefix) { 608 pw.println(prefix + "durationMillis: " + durationMillis); 609 pw.println(prefix + "serviceDetails: " + serviceDetails); 610 } 611 } 612 613 public static final Parcelable.Creator<ApplicationErrorReport> CREATOR 614 = new Parcelable.Creator<ApplicationErrorReport>() { 615 public ApplicationErrorReport createFromParcel(Parcel source) { 616 return new ApplicationErrorReport(source); 617 } 618 619 public ApplicationErrorReport[] newArray(int size) { 620 return new ApplicationErrorReport[size]; 621 } 622 }; 623 624 public int describeContents() { 625 return 0; 626 } 627 628 /** 629 * Dump the report to a Printer. 630 */ 631 public void dump(Printer pw, String prefix) { 632 pw.println(prefix + "type: " + type); 633 pw.println(prefix + "packageName: " + packageName); 634 pw.println(prefix + "installerPackageName: " + installerPackageName); 635 pw.println(prefix + "processName: " + processName); 636 pw.println(prefix + "time: " + time); 637 pw.println(prefix + "systemApp: " + systemApp); 638 639 switch (type) { 640 case TYPE_CRASH: 641 crashInfo.dump(pw, prefix); 642 break; 643 case TYPE_ANR: 644 anrInfo.dump(pw, prefix); 645 break; 646 case TYPE_BATTERY: 647 batteryInfo.dump(pw, prefix); 648 break; 649 case TYPE_RUNNING_SERVICE: 650 runningServiceInfo.dump(pw, prefix); 651 break; 652 } 653 } 654 } 655