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