Home | History | Annotate | Download | only in app
      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