/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.app;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Binder;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemProperties;
import android.provider.Settings;
import android.util.Printer;
import android.util.Slog;
import com.android.internal.util.FastPrintWriter;

import java.io.PrintWriter;
import java.io.StringWriter;

Describes an application error. A report has a type, which is one of
/** * Describes an application error. * * A report has a type, which is one of * <ul> * <li> {@link #TYPE_NONE} uninitialized instance of {@link ApplicationErrorReport}. * <li> {@link #TYPE_CRASH} application crash. Information about the crash * is stored in {@link #crashInfo}. * <li> {@link #TYPE_ANR} application not responding. Information about the * ANR is stored in {@link #anrInfo}. * <li> {@link #TYPE_BATTERY} user reported application is using too much * battery. Information about the battery use is stored in {@link #batteryInfo}. * <li> {@link #TYPE_RUNNING_SERVICE} user reported application is leaving an * unneeded serive running. Information about the battery use is stored in * {@link #runningServiceInfo}. * </ul> */
public class ApplicationErrorReport implements Parcelable { // System property defining error report receiver for system apps static final String SYSTEM_APPS_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.system.apps"; // System property defining default error report receiver static final String DEFAULT_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.default";
Uninitialized error report.
/** * Uninitialized error report. */
public static final int TYPE_NONE = 0;
An error report about an application crash.
/** * An error report about an application crash. */
public static final int TYPE_CRASH = 1;
An error report about an application that's not responding.
/** * An error report about an application that's not responding. */
public static final int TYPE_ANR = 2;
An error report about an application that's consuming too much battery.
/** * An error report about an application that's consuming too much battery. */
public static final int TYPE_BATTERY = 3;
A report from a user to a developer about a running service that the user doesn't think should be running.
/** * A report from a user to a developer about a running service that the * user doesn't think should be running. */
public static final int TYPE_RUNNING_SERVICE = 5;
Type of this report. Can be one of TYPE_NONE, TYPE_CRASH, TYPE_ANR, TYPE_BATTERY, or TYPE_RUNNING_SERVICE.
/** * Type of this report. Can be one of {@link #TYPE_NONE}, * {@link #TYPE_CRASH}, {@link #TYPE_ANR}, {@link #TYPE_BATTERY}, * or {@link #TYPE_RUNNING_SERVICE}. */
public int type;
Package name of the application.
/** * Package name of the application. */
public String packageName;
Package name of the application which installed the application this report pertains to. This identifies which market the application came from.
/** * Package name of the application which installed the application this * report pertains to. * This identifies which market the application came from. */
public String installerPackageName;
Process name of the application.
/** * Process name of the application. */
public String processName;
Time at which the error occurred.
/** * Time at which the error occurred. */
public long time;
Set if the app is on the system image.
/** * Set if the app is on the system image. */
public boolean systemApp;
If this report is of type TYPE_CRASH, contains an instance of CrashInfo describing the crash; otherwise null.
/** * If this report is of type {@link #TYPE_CRASH}, contains an instance * of CrashInfo describing the crash; otherwise null. */
public CrashInfo crashInfo;
If this report is of type TYPE_ANR, contains an instance of AnrInfo describing the ANR; otherwise null.
/** * If this report is of type {@link #TYPE_ANR}, contains an instance * of AnrInfo describing the ANR; otherwise null. */
public AnrInfo anrInfo;
If this report is of type TYPE_BATTERY, contains an instance of BatteryInfo; otherwise null.
/** * If this report is of type {@link #TYPE_BATTERY}, contains an instance * of BatteryInfo; otherwise null. */
public BatteryInfo batteryInfo;
If this report is of type TYPE_RUNNING_SERVICE, contains an instance of RunningServiceInfo; otherwise null.
/** * If this report is of type {@link #TYPE_RUNNING_SERVICE}, contains an instance * of RunningServiceInfo; otherwise null. */
public RunningServiceInfo runningServiceInfo;
Create an uninitialized instance of ApplicationErrorReport.
/** * Create an uninitialized instance of {@link ApplicationErrorReport}. */
public ApplicationErrorReport() { }
Create an instance of ApplicationErrorReport initialized from a parcel.
/** * Create an instance of {@link ApplicationErrorReport} initialized from * a parcel. */
ApplicationErrorReport(Parcel in) { readFromParcel(in); } public static ComponentName getErrorReportReceiver(Context context, String packageName, int appFlags) { // check if error reporting is enabled in secure settings int enabled = Settings.Global.getInt(context.getContentResolver(), Settings.Global.SEND_ACTION_APP_ERROR, 0); if (enabled == 0) { return null; } PackageManager pm = context.getPackageManager(); // look for receiver in the installer package String candidate = null; ComponentName result = null; try { candidate = pm.getInstallerPackageName(packageName); } catch (IllegalArgumentException e) { // the package could already removed } if (candidate != null) { result = getErrorReportReceiver(pm, packageName, candidate); if (result != null) { return result; } } // if the error app is on the system image, look for system apps // error receiver if ((appFlags&ApplicationInfo.FLAG_SYSTEM) != 0) { candidate = SystemProperties.get(SYSTEM_APPS_ERROR_RECEIVER_PROPERTY); result = getErrorReportReceiver(pm, packageName, candidate); if (result != null) { return result; } } // if there is a default receiver, try that candidate = SystemProperties.get(DEFAULT_ERROR_RECEIVER_PROPERTY); return getErrorReportReceiver(pm, packageName, candidate); }
Return activity in receiverPackage that handles ACTION_APP_ERROR.
Params:
  • pm – PackageManager instance
  • errorPackage – package which caused the error
  • receiverPackage – candidate package to receive the error
Returns:activity component within receiverPackage which handles ACTION_APP_ERROR, or null if not found
/** * Return activity in receiverPackage that handles ACTION_APP_ERROR. * * @param pm PackageManager instance * @param errorPackage package which caused the error * @param receiverPackage candidate package to receive the error * @return activity component within receiverPackage which handles * ACTION_APP_ERROR, or null if not found */
static ComponentName getErrorReportReceiver(PackageManager pm, String errorPackage, String receiverPackage) { if (receiverPackage == null || receiverPackage.length() == 0) { return null; } // break the loop if it's the error report receiver package that crashed if (receiverPackage.equals(errorPackage)) { return null; } Intent intent = new Intent(Intent.ACTION_APP_ERROR); intent.setPackage(receiverPackage); ResolveInfo info = pm.resolveActivity(intent, 0); if (info == null || info.activityInfo == null) { return null; } return new ComponentName(receiverPackage, info.activityInfo.name); } public void writeToParcel(Parcel dest, int flags) { dest.writeInt(type); dest.writeString(packageName); dest.writeString(installerPackageName); dest.writeString(processName); dest.writeLong(time); dest.writeInt(systemApp ? 1 : 0); dest.writeInt(crashInfo != null ? 1 : 0); switch (type) { case TYPE_CRASH: if (crashInfo != null) { crashInfo.writeToParcel(dest, flags); } break; case TYPE_ANR: anrInfo.writeToParcel(dest, flags); break; case TYPE_BATTERY: batteryInfo.writeToParcel(dest, flags); break; case TYPE_RUNNING_SERVICE: runningServiceInfo.writeToParcel(dest, flags); break; } } public void readFromParcel(Parcel in) { type = in.readInt(); packageName = in.readString(); installerPackageName = in.readString(); processName = in.readString(); time = in.readLong(); systemApp = in.readInt() == 1; boolean hasCrashInfo = in.readInt() == 1; switch (type) { case TYPE_CRASH: crashInfo = hasCrashInfo ? new CrashInfo(in) : null; anrInfo = null; batteryInfo = null; runningServiceInfo = null; break; case TYPE_ANR: anrInfo = new AnrInfo(in); crashInfo = null; batteryInfo = null; runningServiceInfo = null; break; case TYPE_BATTERY: batteryInfo = new BatteryInfo(in); anrInfo = null; crashInfo = null; runningServiceInfo = null; break; case TYPE_RUNNING_SERVICE: batteryInfo = null; anrInfo = null; crashInfo = null; runningServiceInfo = new RunningServiceInfo(in); break; } }
Describes an application crash.
/** * Describes an application crash. */
public static class CrashInfo {
Class name of the exception that caused the crash.
/** * Class name of the exception that caused the crash. */
public String exceptionClassName;
Message stored in the exception.
/** * Message stored in the exception. */
public String exceptionMessage;
File which the exception was thrown from.
/** * File which the exception was thrown from. */
public String throwFileName;
Class which the exception was thrown from.
/** * Class which the exception was thrown from. */
public String throwClassName;
Method which the exception was thrown from.
/** * Method which the exception was thrown from. */
public String throwMethodName;
Line number the exception was thrown from.
/** * Line number the exception was thrown from. */
public int throwLineNumber;
Stack trace.
/** * Stack trace. */
public String stackTrace;
Create an uninitialized instance of CrashInfo.
/** * Create an uninitialized instance of CrashInfo. */
public CrashInfo() { }
Create an instance of CrashInfo initialized from an exception.
/** * Create an instance of CrashInfo initialized from an exception. */
public CrashInfo(Throwable tr) { StringWriter sw = new StringWriter(); PrintWriter pw = new FastPrintWriter(sw, false, 256); tr.printStackTrace(pw); pw.flush(); stackTrace = sanitizeString(sw.toString()); exceptionMessage = tr.getMessage(); // Populate fields with the "root cause" exception Throwable rootTr = tr; while (tr.getCause() != null) { tr = tr.getCause(); if (tr.getStackTrace() != null && tr.getStackTrace().length > 0) { rootTr = tr; } String msg = tr.getMessage(); if (msg != null && msg.length() > 0) { exceptionMessage = msg; } } exceptionClassName = rootTr.getClass().getName(); if (rootTr.getStackTrace().length > 0) { StackTraceElement trace = rootTr.getStackTrace()[0]; throwFileName = trace.getFileName(); throwClassName = trace.getClassName(); throwMethodName = trace.getMethodName(); throwLineNumber = trace.getLineNumber(); } else { throwFileName = "unknown"; throwClassName = "unknown"; throwMethodName = "unknown"; throwLineNumber = 0; } exceptionMessage = sanitizeString(exceptionMessage); }
{@hide}
/** {@hide} */
public void appendStackTrace(String tr) { stackTrace = sanitizeString(stackTrace + tr); }
Ensure that the string is of reasonable size, truncating from the middle if needed.
/** * Ensure that the string is of reasonable size, truncating from the middle if needed. */
private String sanitizeString(String s) { int prefixLength = 10 * 1024; int suffixLength = 10 * 1024; int acceptableLength = prefixLength + suffixLength; if (s != null && s.length() > acceptableLength) { String replacement = "\n[TRUNCATED " + (s.length() - acceptableLength) + " CHARS]\n"; StringBuilder sb = new StringBuilder(acceptableLength + replacement.length()); sb.append(s.substring(0, prefixLength)); sb.append(replacement); sb.append(s.substring(s.length() - suffixLength)); return sb.toString(); } return s; }
Create an instance of CrashInfo initialized from a Parcel.
/** * Create an instance of CrashInfo initialized from a Parcel. */
public CrashInfo(Parcel in) { exceptionClassName = in.readString(); exceptionMessage = in.readString(); throwFileName = in.readString(); throwClassName = in.readString(); throwMethodName = in.readString(); throwLineNumber = in.readInt(); stackTrace = in.readString(); }
Save a CrashInfo instance to a parcel.
/** * Save a CrashInfo instance to a parcel. */
public void writeToParcel(Parcel dest, int flags) { int start = dest.dataPosition(); dest.writeString(exceptionClassName); dest.writeString(exceptionMessage); dest.writeString(throwFileName); dest.writeString(throwClassName); dest.writeString(throwMethodName); dest.writeInt(throwLineNumber); dest.writeString(stackTrace); int total = dest.dataPosition()-start; if (Binder.CHECK_PARCEL_SIZE && total > 20*1024) { Slog.d("Error", "ERR: exClass=" + exceptionClassName); Slog.d("Error", "ERR: exMsg=" + exceptionMessage); Slog.d("Error", "ERR: file=" + throwFileName); Slog.d("Error", "ERR: class=" + throwClassName); Slog.d("Error", "ERR: method=" + throwMethodName + " line=" + throwLineNumber); Slog.d("Error", "ERR: stack=" + stackTrace); Slog.d("Error", "ERR: TOTAL BYTES WRITTEN: " + (dest.dataPosition()-start)); } }
Dump a CrashInfo instance to a Printer.
/** * Dump a CrashInfo instance to a Printer. */
public void dump(Printer pw, String prefix) { pw.println(prefix + "exceptionClassName: " + exceptionClassName); pw.println(prefix + "exceptionMessage: " + exceptionMessage); pw.println(prefix + "throwFileName: " + throwFileName); pw.println(prefix + "throwClassName: " + throwClassName); pw.println(prefix + "throwMethodName: " + throwMethodName); pw.println(prefix + "throwLineNumber: " + throwLineNumber); pw.println(prefix + "stackTrace: " + stackTrace); } }
Parcelable version of CrashInfo
@hide
/** * Parcelable version of {@link CrashInfo} * * @hide */
public static class ParcelableCrashInfo extends CrashInfo implements Parcelable {
Create an uninitialized instance of CrashInfo.
/** * Create an uninitialized instance of CrashInfo. */
public ParcelableCrashInfo() { }
Create an instance of CrashInfo initialized from an exception.
/** * Create an instance of CrashInfo initialized from an exception. */
public ParcelableCrashInfo(Throwable tr) { super(tr); } public ParcelableCrashInfo(Parcel in) { super(in); } public int describeContents() { return 0; } public static final Parcelable.Creator<ParcelableCrashInfo> CREATOR = new Parcelable.Creator<ParcelableCrashInfo>() { @Override public ParcelableCrashInfo createFromParcel(Parcel in) { return new ParcelableCrashInfo(in); } @Override public ParcelableCrashInfo[] newArray(int size) { return new ParcelableCrashInfo[size]; } }; }
Describes an application not responding error.
/** * Describes an application not responding error. */
public static class AnrInfo {
Activity name.
/** * Activity name. */
public String activity;
Description of the operation that timed out.
/** * Description of the operation that timed out. */
public String cause;
Additional info, including CPU stats.
/** * Additional info, including CPU stats. */
public String info;
Create an uninitialized instance of AnrInfo.
/** * Create an uninitialized instance of AnrInfo. */
public AnrInfo() { }
Create an instance of AnrInfo initialized from a Parcel.
/** * Create an instance of AnrInfo initialized from a Parcel. */
public AnrInfo(Parcel in) { activity = in.readString(); cause = in.readString(); info = in.readString(); }
Save an AnrInfo instance to a parcel.
/** * Save an AnrInfo instance to a parcel. */
public void writeToParcel(Parcel dest, int flags) { dest.writeString(activity); dest.writeString(cause); dest.writeString(info); }
Dump an AnrInfo instance to a Printer.
/** * Dump an AnrInfo instance to a Printer. */
public void dump(Printer pw, String prefix) { pw.println(prefix + "activity: " + activity); pw.println(prefix + "cause: " + cause); pw.println(prefix + "info: " + info); } }
Describes a battery usage report.
/** * Describes a battery usage report. */
public static class BatteryInfo {
Percentage of the battery that was used up by the process.
/** * Percentage of the battery that was used up by the process. */
public int usagePercent;
Duration in microseconds over which the process used the above percentage of battery.
/** * Duration in microseconds over which the process used the above * percentage of battery. */
public long durationMicros;
Dump of various info impacting battery use.
/** * Dump of various info impacting battery use. */
public String usageDetails;
Checkin details.
/** * Checkin details. */
public String checkinDetails;
Create an uninitialized instance of BatteryInfo.
/** * Create an uninitialized instance of BatteryInfo. */
public BatteryInfo() { }
Create an instance of BatteryInfo initialized from a Parcel.
/** * Create an instance of BatteryInfo initialized from a Parcel. */
public BatteryInfo(Parcel in) { usagePercent = in.readInt(); durationMicros = in.readLong(); usageDetails = in.readString(); checkinDetails = in.readString(); }
Save a BatteryInfo instance to a parcel.
/** * Save a BatteryInfo instance to a parcel. */
public void writeToParcel(Parcel dest, int flags) { dest.writeInt(usagePercent); dest.writeLong(durationMicros); dest.writeString(usageDetails); dest.writeString(checkinDetails); }
Dump a BatteryInfo instance to a Printer.
/** * Dump a BatteryInfo instance to a Printer. */
public void dump(Printer pw, String prefix) { pw.println(prefix + "usagePercent: " + usagePercent); pw.println(prefix + "durationMicros: " + durationMicros); pw.println(prefix + "usageDetails: " + usageDetails); pw.println(prefix + "checkinDetails: " + checkinDetails); } }
Describes a running service report.
/** * Describes a running service report. */
public static class RunningServiceInfo {
Duration in milliseconds that the service has been running.
/** * Duration in milliseconds that the service has been running. */
public long durationMillis;
Dump of debug information about the service.
/** * Dump of debug information about the service. */
public String serviceDetails;
Create an uninitialized instance of RunningServiceInfo.
/** * Create an uninitialized instance of RunningServiceInfo. */
public RunningServiceInfo() { }
Create an instance of RunningServiceInfo initialized from a Parcel.
/** * Create an instance of RunningServiceInfo initialized from a Parcel. */
public RunningServiceInfo(Parcel in) { durationMillis = in.readLong(); serviceDetails = in.readString(); }
Save a RunningServiceInfo instance to a parcel.
/** * Save a RunningServiceInfo instance to a parcel. */
public void writeToParcel(Parcel dest, int flags) { dest.writeLong(durationMillis); dest.writeString(serviceDetails); }
Dump a BatteryInfo instance to a Printer.
/** * Dump a BatteryInfo instance to a Printer. */
public void dump(Printer pw, String prefix) { pw.println(prefix + "durationMillis: " + durationMillis); pw.println(prefix + "serviceDetails: " + serviceDetails); } } public static final Parcelable.Creator<ApplicationErrorReport> CREATOR = new Parcelable.Creator<ApplicationErrorReport>() { public ApplicationErrorReport createFromParcel(Parcel source) { return new ApplicationErrorReport(source); } public ApplicationErrorReport[] newArray(int size) { return new ApplicationErrorReport[size]; } }; public int describeContents() { return 0; }
Dump the report to a Printer.
/** * Dump the report to a Printer. */
public void dump(Printer pw, String prefix) { pw.println(prefix + "type: " + type); pw.println(prefix + "packageName: " + packageName); pw.println(prefix + "installerPackageName: " + installerPackageName); pw.println(prefix + "processName: " + processName); pw.println(prefix + "time: " + time); pw.println(prefix + "systemApp: " + systemApp); switch (type) { case TYPE_CRASH: crashInfo.dump(pw, prefix); break; case TYPE_ANR: anrInfo.dump(pw, prefix); break; case TYPE_BATTERY: batteryInfo.dump(pw, prefix); break; case TYPE_RUNNING_SERVICE: runningServiceInfo.dump(pw, prefix); break; } } }