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