ApplicationErrorReport.java revision 625239a05401bbf18b04d9874cea3f82da7c29a1
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.Global.getInt(context.getContentResolver(),
162                Settings.Global.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            Throwable rootTr = tr;
336            while (tr.getCause() != null) {
337                tr = tr.getCause();
338                if (tr.getStackTrace() != null && tr.getStackTrace().length > 0) {
339                    rootTr = tr;
340                }
341                String msg = tr.getMessage();
342                if (msg != null && msg.length() > 0) {
343                    exceptionMessage = msg;
344                }
345            }
346
347            exceptionClassName = rootTr.getClass().getName();
348            if (rootTr.getStackTrace().length > 0) {
349                StackTraceElement trace = rootTr.getStackTrace()[0];
350                throwFileName = trace.getFileName();
351                throwClassName = trace.getClassName();
352                throwMethodName = trace.getMethodName();
353                throwLineNumber = trace.getLineNumber();
354            } else {
355                throwFileName = "unknown";
356                throwClassName = "unknown";
357                throwMethodName = "unknown";
358                throwLineNumber = 0;
359            }
360        }
361
362        /**
363         * Create an instance of CrashInfo initialized from a Parcel.
364         */
365        public CrashInfo(Parcel in) {
366            exceptionClassName = in.readString();
367            exceptionMessage = in.readString();
368            throwFileName = in.readString();
369            throwClassName = in.readString();
370            throwMethodName = in.readString();
371            throwLineNumber = in.readInt();
372            stackTrace = in.readString();
373        }
374
375        /**
376         * Save a CrashInfo instance to a parcel.
377         */
378        public void writeToParcel(Parcel dest, int flags) {
379            dest.writeString(exceptionClassName);
380            dest.writeString(exceptionMessage);
381            dest.writeString(throwFileName);
382            dest.writeString(throwClassName);
383            dest.writeString(throwMethodName);
384            dest.writeInt(throwLineNumber);
385            dest.writeString(stackTrace);
386        }
387
388        /**
389         * Dump a CrashInfo instance to a Printer.
390         */
391        public void dump(Printer pw, String prefix) {
392            pw.println(prefix + "exceptionClassName: " + exceptionClassName);
393            pw.println(prefix + "exceptionMessage: " + exceptionMessage);
394            pw.println(prefix + "throwFileName: " + throwFileName);
395            pw.println(prefix + "throwClassName: " + throwClassName);
396            pw.println(prefix + "throwMethodName: " + throwMethodName);
397            pw.println(prefix + "throwLineNumber: " + throwLineNumber);
398            pw.println(prefix + "stackTrace: " + stackTrace);
399        }
400    }
401
402    /**
403     * Describes an application not responding error.
404     */
405    public static class AnrInfo {
406        /**
407         * Activity name.
408         */
409        public String activity;
410
411        /**
412         * Description of the operation that timed out.
413         */
414        public String cause;
415
416        /**
417         * Additional info, including CPU stats.
418         */
419        public String info;
420
421        /**
422         * Create an uninitialized instance of AnrInfo.
423         */
424        public AnrInfo() {
425        }
426
427        /**
428         * Create an instance of AnrInfo initialized from a Parcel.
429         */
430        public AnrInfo(Parcel in) {
431            activity = in.readString();
432            cause = in.readString();
433            info = in.readString();
434        }
435
436        /**
437         * Save an AnrInfo instance to a parcel.
438         */
439        public void writeToParcel(Parcel dest, int flags) {
440            dest.writeString(activity);
441            dest.writeString(cause);
442            dest.writeString(info);
443        }
444
445        /**
446         * Dump an AnrInfo instance to a Printer.
447         */
448        public void dump(Printer pw, String prefix) {
449            pw.println(prefix + "activity: " + activity);
450            pw.println(prefix + "cause: " + cause);
451            pw.println(prefix + "info: " + info);
452        }
453    }
454
455    /**
456     * Describes a battery usage report.
457     */
458    public static class BatteryInfo {
459        /**
460         * Percentage of the battery that was used up by the process.
461         */
462        public int usagePercent;
463
464        /**
465         * Duration in microseconds over which the process used the above
466         * percentage of battery.
467         */
468        public long durationMicros;
469
470        /**
471         * Dump of various info impacting battery use.
472         */
473        public String usageDetails;
474
475        /**
476         * Checkin details.
477         */
478        public String checkinDetails;
479
480        /**
481         * Create an uninitialized instance of BatteryInfo.
482         */
483        public BatteryInfo() {
484        }
485
486        /**
487         * Create an instance of BatteryInfo initialized from a Parcel.
488         */
489        public BatteryInfo(Parcel in) {
490            usagePercent = in.readInt();
491            durationMicros = in.readLong();
492            usageDetails = in.readString();
493            checkinDetails = in.readString();
494        }
495
496        /**
497         * Save a BatteryInfo instance to a parcel.
498         */
499        public void writeToParcel(Parcel dest, int flags) {
500            dest.writeInt(usagePercent);
501            dest.writeLong(durationMicros);
502            dest.writeString(usageDetails);
503            dest.writeString(checkinDetails);
504        }
505
506        /**
507         * Dump a BatteryInfo instance to a Printer.
508         */
509        public void dump(Printer pw, String prefix) {
510            pw.println(prefix + "usagePercent: " + usagePercent);
511            pw.println(prefix + "durationMicros: " + durationMicros);
512            pw.println(prefix + "usageDetails: " + usageDetails);
513            pw.println(prefix + "checkinDetails: " + checkinDetails);
514        }
515    }
516
517    /**
518     * Describes a running service report.
519     */
520    public static class RunningServiceInfo {
521        /**
522         * Duration in milliseconds that the service has been running.
523         */
524        public long durationMillis;
525
526        /**
527         * Dump of debug information about the service.
528         */
529        public String serviceDetails;
530
531        /**
532         * Create an uninitialized instance of RunningServiceInfo.
533         */
534        public RunningServiceInfo() {
535        }
536
537        /**
538         * Create an instance of RunningServiceInfo initialized from a Parcel.
539         */
540        public RunningServiceInfo(Parcel in) {
541            durationMillis = in.readLong();
542            serviceDetails = in.readString();
543        }
544
545        /**
546         * Save a RunningServiceInfo instance to a parcel.
547         */
548        public void writeToParcel(Parcel dest, int flags) {
549            dest.writeLong(durationMillis);
550            dest.writeString(serviceDetails);
551        }
552
553        /**
554         * Dump a BatteryInfo instance to a Printer.
555         */
556        public void dump(Printer pw, String prefix) {
557            pw.println(prefix + "durationMillis: " + durationMillis);
558            pw.println(prefix + "serviceDetails: " + serviceDetails);
559        }
560    }
561
562    public static final Parcelable.Creator<ApplicationErrorReport> CREATOR
563            = new Parcelable.Creator<ApplicationErrorReport>() {
564        public ApplicationErrorReport createFromParcel(Parcel source) {
565            return new ApplicationErrorReport(source);
566        }
567
568        public ApplicationErrorReport[] newArray(int size) {
569            return new ApplicationErrorReport[size];
570        }
571    };
572
573    public int describeContents() {
574        return 0;
575    }
576
577    /**
578     * Dump the report to a Printer.
579     */
580    public void dump(Printer pw, String prefix) {
581        pw.println(prefix + "type: " + type);
582        pw.println(prefix + "packageName: " + packageName);
583        pw.println(prefix + "installerPackageName: " + installerPackageName);
584        pw.println(prefix + "processName: " + processName);
585        pw.println(prefix + "time: " + time);
586        pw.println(prefix + "systemApp: " + systemApp);
587
588        switch (type) {
589            case TYPE_CRASH:
590                crashInfo.dump(pw, prefix);
591                break;
592            case TYPE_ANR:
593                anrInfo.dump(pw, prefix);
594                break;
595            case TYPE_BATTERY:
596                batteryInfo.dump(pw, prefix);
597                break;
598            case TYPE_RUNNING_SERVICE:
599                runningServiceInfo.dump(pw, prefix);
600                break;
601        }
602    }
603}
604