ApplicationErrorReport.java revision fc46e9b643881b7b2ab76854f3a0ac077e9def8d
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.SystemProperties;
28import android.provider.Settings;
29import android.util.Printer;
30import android.util.Slog;
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 = null;
172        ComponentName result = null;
173
174        try {
175            candidate = pm.getInstallerPackageName(packageName);
176        } catch (IllegalArgumentException e) {
177            // the package could already removed
178        }
179
180        if (candidate != null) {
181            result = getErrorReportReceiver(pm, packageName, candidate);
182            if (result != null) {
183                return result;
184            }
185        }
186
187        // if the error app is on the system image, look for system apps
188        // error receiver
189        if ((appFlags&ApplicationInfo.FLAG_SYSTEM) != 0) {
190            candidate = SystemProperties.get(SYSTEM_APPS_ERROR_RECEIVER_PROPERTY);
191            result = getErrorReportReceiver(pm, packageName, candidate);
192            if (result != null) {
193                return result;
194            }
195        }
196
197        // if there is a default receiver, try that
198        candidate = SystemProperties.get(DEFAULT_ERROR_RECEIVER_PROPERTY);
199        return getErrorReportReceiver(pm, packageName, candidate);
200    }
201
202    /**
203     * Return activity in receiverPackage that handles ACTION_APP_ERROR.
204     *
205     * @param pm PackageManager instance
206     * @param errorPackage package which caused the error
207     * @param receiverPackage candidate package to receive the error
208     * @return activity component within receiverPackage which handles
209     * ACTION_APP_ERROR, or null if not found
210     */
211    static ComponentName getErrorReportReceiver(PackageManager pm, String errorPackage,
212            String receiverPackage) {
213        if (receiverPackage == null || receiverPackage.length() == 0) {
214            return null;
215        }
216
217        // break the loop if it's the error report receiver package that crashed
218        if (receiverPackage.equals(errorPackage)) {
219            return null;
220        }
221
222        Intent intent = new Intent(Intent.ACTION_APP_ERROR);
223        intent.setPackage(receiverPackage);
224        ResolveInfo info = pm.resolveActivity(intent, 0);
225        if (info == null || info.activityInfo == null) {
226            return null;
227        }
228        return new ComponentName(receiverPackage, info.activityInfo.name);
229    }
230
231    public void writeToParcel(Parcel dest, int flags) {
232        dest.writeInt(type);
233        dest.writeString(packageName);
234        dest.writeString(installerPackageName);
235        dest.writeString(processName);
236        dest.writeLong(time);
237        dest.writeInt(systemApp ? 1 : 0);
238        dest.writeInt(crashInfo != null ? 1 : 0);
239
240        switch (type) {
241            case TYPE_CRASH:
242                if (crashInfo != null) {
243                    crashInfo.writeToParcel(dest, flags);
244                }
245                break;
246            case TYPE_ANR:
247                anrInfo.writeToParcel(dest, flags);
248                break;
249            case TYPE_BATTERY:
250                batteryInfo.writeToParcel(dest, flags);
251                break;
252            case TYPE_RUNNING_SERVICE:
253                runningServiceInfo.writeToParcel(dest, flags);
254                break;
255        }
256    }
257
258    public void readFromParcel(Parcel in) {
259        type = in.readInt();
260        packageName = in.readString();
261        installerPackageName = in.readString();
262        processName = in.readString();
263        time = in.readLong();
264        systemApp = in.readInt() == 1;
265        boolean hasCrashInfo = in.readInt() == 1;
266
267        switch (type) {
268            case TYPE_CRASH:
269                crashInfo = hasCrashInfo ? new CrashInfo(in) : null;
270                anrInfo = null;
271                batteryInfo = null;
272                runningServiceInfo = null;
273                break;
274            case TYPE_ANR:
275                anrInfo = new AnrInfo(in);
276                crashInfo = null;
277                batteryInfo = null;
278                runningServiceInfo = null;
279                break;
280            case TYPE_BATTERY:
281                batteryInfo = new BatteryInfo(in);
282                anrInfo = null;
283                crashInfo = null;
284                runningServiceInfo = null;
285                break;
286            case TYPE_RUNNING_SERVICE:
287                batteryInfo = null;
288                anrInfo = null;
289                crashInfo = null;
290                runningServiceInfo = new RunningServiceInfo(in);
291                break;
292        }
293    }
294
295    /**
296     * Describes an application crash.
297     */
298    public static class CrashInfo {
299        /**
300         * Class name of the exception that caused the crash.
301         */
302        public String exceptionClassName;
303
304        /**
305         * Message stored in the exception.
306         */
307        public String exceptionMessage;
308
309        /**
310         * File which the exception was thrown from.
311         */
312        public String throwFileName;
313
314        /**
315         * Class which the exception was thrown from.
316         */
317        public String throwClassName;
318
319        /**
320         * Method which the exception was thrown from.
321         */
322        public String throwMethodName;
323
324        /**
325         * Line number the exception was thrown from.
326         */
327        public int throwLineNumber;
328
329        /**
330         * Stack trace.
331         */
332        public String stackTrace;
333
334        /**
335         * Create an uninitialized instance of CrashInfo.
336         */
337        public CrashInfo() {
338        }
339
340        /**
341         * Create an instance of CrashInfo initialized from an exception.
342         */
343        public CrashInfo(Throwable tr) {
344            StringWriter sw = new StringWriter();
345            PrintWriter pw = new FastPrintWriter(sw, false, 256);
346            tr.printStackTrace(pw);
347            pw.flush();
348            stackTrace = sanitizeString(sw.toString());
349            exceptionMessage = tr.getMessage();
350
351            // Populate fields with the "root cause" exception
352            Throwable rootTr = tr;
353            while (tr.getCause() != null) {
354                tr = tr.getCause();
355                if (tr.getStackTrace() != null && tr.getStackTrace().length > 0) {
356                    rootTr = tr;
357                }
358                String msg = tr.getMessage();
359                if (msg != null && msg.length() > 0) {
360                    exceptionMessage = msg;
361                }
362            }
363
364            exceptionClassName = rootTr.getClass().getName();
365            if (rootTr.getStackTrace().length > 0) {
366                StackTraceElement trace = rootTr.getStackTrace()[0];
367                throwFileName = trace.getFileName();
368                throwClassName = trace.getClassName();
369                throwMethodName = trace.getMethodName();
370                throwLineNumber = trace.getLineNumber();
371            } else {
372                throwFileName = "unknown";
373                throwClassName = "unknown";
374                throwMethodName = "unknown";
375                throwLineNumber = 0;
376            }
377
378            exceptionMessage = sanitizeString(exceptionMessage);
379        }
380
381        /**
382         * Ensure that the string is of reasonable size, truncating from the middle if needed.
383         */
384        private String sanitizeString(String s) {
385            int prefixLength = 10 * 1024;
386            int suffixLength = 10 * 1024;
387            int acceptableLength = prefixLength + suffixLength;
388
389            if (s != null && s.length() > acceptableLength) {
390                String replacement =
391                        "\n[TRUNCATED " + (s.length() - acceptableLength) + " CHARS]\n";
392
393                StringBuilder sb = new StringBuilder(acceptableLength + replacement.length());
394                sb.append(s.substring(0, prefixLength));
395                sb.append(replacement);
396                sb.append(s.substring(s.length() - suffixLength));
397                return sb.toString();
398            }
399            return s;
400        }
401
402        /**
403         * Create an instance of CrashInfo initialized from a Parcel.
404         */
405        public CrashInfo(Parcel in) {
406            exceptionClassName = in.readString();
407            exceptionMessage = in.readString();
408            throwFileName = in.readString();
409            throwClassName = in.readString();
410            throwMethodName = in.readString();
411            throwLineNumber = in.readInt();
412            stackTrace = in.readString();
413        }
414
415        /**
416         * Save a CrashInfo instance to a parcel.
417         */
418        public void writeToParcel(Parcel dest, int flags) {
419            int start = dest.dataPosition();
420            dest.writeString(exceptionClassName);
421            dest.writeString(exceptionMessage);
422            dest.writeString(throwFileName);
423            dest.writeString(throwClassName);
424            dest.writeString(throwMethodName);
425            dest.writeInt(throwLineNumber);
426            dest.writeString(stackTrace);
427            int total = dest.dataPosition()-start;
428            if (total > 20*1024) {
429                Slog.d("Error", "ERR: exClass=" + exceptionClassName);
430                Slog.d("Error", "ERR: exMsg=" + exceptionMessage);
431                Slog.d("Error", "ERR: file=" + throwFileName);
432                Slog.d("Error", "ERR: class=" + throwClassName);
433                Slog.d("Error", "ERR: method=" + throwMethodName + " line=" + throwLineNumber);
434                Slog.d("Error", "ERR: stack=" + stackTrace);
435                Slog.d("Error", "ERR: TOTAL BYTES WRITTEN: " + (dest.dataPosition()-start));
436            }
437        }
438
439        /**
440         * Dump a CrashInfo instance to a Printer.
441         */
442        public void dump(Printer pw, String prefix) {
443            pw.println(prefix + "exceptionClassName: " + exceptionClassName);
444            pw.println(prefix + "exceptionMessage: " + exceptionMessage);
445            pw.println(prefix + "throwFileName: " + throwFileName);
446            pw.println(prefix + "throwClassName: " + throwClassName);
447            pw.println(prefix + "throwMethodName: " + throwMethodName);
448            pw.println(prefix + "throwLineNumber: " + throwLineNumber);
449            pw.println(prefix + "stackTrace: " + stackTrace);
450        }
451    }
452
453    /**
454     * Parcelable version of {@link CrashInfo}
455     *
456     * @hide
457     */
458    public static class ParcelableCrashInfo extends CrashInfo implements Parcelable {
459        /**
460         * Create an uninitialized instance of CrashInfo.
461         */
462        public ParcelableCrashInfo() {
463        }
464
465        /**
466         * Create an instance of CrashInfo initialized from an exception.
467         */
468        public ParcelableCrashInfo(Throwable tr) {
469            super(tr);
470        }
471
472        public ParcelableCrashInfo(Parcel in) {
473            super(in);
474        }
475
476        public int describeContents() {
477            return 0;
478        }
479
480        public static final Parcelable.Creator<ParcelableCrashInfo> CREATOR =
481                new Parcelable.Creator<ParcelableCrashInfo>() {
482                    @Override
483                    public ParcelableCrashInfo createFromParcel(Parcel in) {
484                        return new ParcelableCrashInfo(in);
485                    }
486
487                    @Override
488                    public ParcelableCrashInfo[] newArray(int size) {
489                        return new ParcelableCrashInfo[size];
490                    }
491                };
492    }
493
494    /**
495     * Describes an application not responding error.
496     */
497    public static class AnrInfo {
498        /**
499         * Activity name.
500         */
501        public String activity;
502
503        /**
504         * Description of the operation that timed out.
505         */
506        public String cause;
507
508        /**
509         * Additional info, including CPU stats.
510         */
511        public String info;
512
513        /**
514         * Create an uninitialized instance of AnrInfo.
515         */
516        public AnrInfo() {
517        }
518
519        /**
520         * Create an instance of AnrInfo initialized from a Parcel.
521         */
522        public AnrInfo(Parcel in) {
523            activity = in.readString();
524            cause = in.readString();
525            info = in.readString();
526        }
527
528        /**
529         * Save an AnrInfo instance to a parcel.
530         */
531        public void writeToParcel(Parcel dest, int flags) {
532            dest.writeString(activity);
533            dest.writeString(cause);
534            dest.writeString(info);
535        }
536
537        /**
538         * Dump an AnrInfo instance to a Printer.
539         */
540        public void dump(Printer pw, String prefix) {
541            pw.println(prefix + "activity: " + activity);
542            pw.println(prefix + "cause: " + cause);
543            pw.println(prefix + "info: " + info);
544        }
545    }
546
547    /**
548     * Describes a battery usage report.
549     */
550    public static class BatteryInfo {
551        /**
552         * Percentage of the battery that was used up by the process.
553         */
554        public int usagePercent;
555
556        /**
557         * Duration in microseconds over which the process used the above
558         * percentage of battery.
559         */
560        public long durationMicros;
561
562        /**
563         * Dump of various info impacting battery use.
564         */
565        public String usageDetails;
566
567        /**
568         * Checkin details.
569         */
570        public String checkinDetails;
571
572        /**
573         * Create an uninitialized instance of BatteryInfo.
574         */
575        public BatteryInfo() {
576        }
577
578        /**
579         * Create an instance of BatteryInfo initialized from a Parcel.
580         */
581        public BatteryInfo(Parcel in) {
582            usagePercent = in.readInt();
583            durationMicros = in.readLong();
584            usageDetails = in.readString();
585            checkinDetails = in.readString();
586        }
587
588        /**
589         * Save a BatteryInfo instance to a parcel.
590         */
591        public void writeToParcel(Parcel dest, int flags) {
592            dest.writeInt(usagePercent);
593            dest.writeLong(durationMicros);
594            dest.writeString(usageDetails);
595            dest.writeString(checkinDetails);
596        }
597
598        /**
599         * Dump a BatteryInfo instance to a Printer.
600         */
601        public void dump(Printer pw, String prefix) {
602            pw.println(prefix + "usagePercent: " + usagePercent);
603            pw.println(prefix + "durationMicros: " + durationMicros);
604            pw.println(prefix + "usageDetails: " + usageDetails);
605            pw.println(prefix + "checkinDetails: " + checkinDetails);
606        }
607    }
608
609    /**
610     * Describes a running service report.
611     */
612    public static class RunningServiceInfo {
613        /**
614         * Duration in milliseconds that the service has been running.
615         */
616        public long durationMillis;
617
618        /**
619         * Dump of debug information about the service.
620         */
621        public String serviceDetails;
622
623        /**
624         * Create an uninitialized instance of RunningServiceInfo.
625         */
626        public RunningServiceInfo() {
627        }
628
629        /**
630         * Create an instance of RunningServiceInfo initialized from a Parcel.
631         */
632        public RunningServiceInfo(Parcel in) {
633            durationMillis = in.readLong();
634            serviceDetails = in.readString();
635        }
636
637        /**
638         * Save a RunningServiceInfo instance to a parcel.
639         */
640        public void writeToParcel(Parcel dest, int flags) {
641            dest.writeLong(durationMillis);
642            dest.writeString(serviceDetails);
643        }
644
645        /**
646         * Dump a BatteryInfo instance to a Printer.
647         */
648        public void dump(Printer pw, String prefix) {
649            pw.println(prefix + "durationMillis: " + durationMillis);
650            pw.println(prefix + "serviceDetails: " + serviceDetails);
651        }
652    }
653
654    public static final Parcelable.Creator<ApplicationErrorReport> CREATOR
655            = new Parcelable.Creator<ApplicationErrorReport>() {
656        public ApplicationErrorReport createFromParcel(Parcel source) {
657            return new ApplicationErrorReport(source);
658        }
659
660        public ApplicationErrorReport[] newArray(int size) {
661            return new ApplicationErrorReport[size];
662        }
663    };
664
665    public int describeContents() {
666        return 0;
667    }
668
669    /**
670     * Dump the report to a Printer.
671     */
672    public void dump(Printer pw, String prefix) {
673        pw.println(prefix + "type: " + type);
674        pw.println(prefix + "packageName: " + packageName);
675        pw.println(prefix + "installerPackageName: " + installerPackageName);
676        pw.println(prefix + "processName: " + processName);
677        pw.println(prefix + "time: " + time);
678        pw.println(prefix + "systemApp: " + systemApp);
679
680        switch (type) {
681            case TYPE_CRASH:
682                crashInfo.dump(pw, prefix);
683                break;
684            case TYPE_ANR:
685                anrInfo.dump(pw, prefix);
686                break;
687            case TYPE_BATTERY:
688                batteryInfo.dump(pw, prefix);
689                break;
690            case TYPE_RUNNING_SERVICE:
691                runningServiceInfo.dump(pw, prefix);
692                break;
693        }
694    }
695}
696