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