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