ApplicationErrorReport.java revision dfa34cd517dfb794e31598211ed0fc8a419ddc28
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
239        switch (type) {
240            case TYPE_CRASH:
241                crashInfo.writeToParcel(dest, flags);
242                break;
243            case TYPE_ANR:
244                anrInfo.writeToParcel(dest, flags);
245                break;
246            case TYPE_BATTERY:
247                batteryInfo.writeToParcel(dest, flags);
248                break;
249            case TYPE_RUNNING_SERVICE:
250                runningServiceInfo.writeToParcel(dest, flags);
251                break;
252        }
253    }
254
255    public void readFromParcel(Parcel in) {
256        type = in.readInt();
257        packageName = in.readString();
258        installerPackageName = in.readString();
259        processName = in.readString();
260        time = in.readLong();
261        systemApp = in.readInt() == 1;
262
263        switch (type) {
264            case TYPE_CRASH:
265                crashInfo = new CrashInfo(in);
266                anrInfo = null;
267                batteryInfo = null;
268                runningServiceInfo = null;
269                break;
270            case TYPE_ANR:
271                anrInfo = new AnrInfo(in);
272                crashInfo = null;
273                batteryInfo = null;
274                runningServiceInfo = null;
275                break;
276            case TYPE_BATTERY:
277                batteryInfo = new BatteryInfo(in);
278                anrInfo = null;
279                crashInfo = null;
280                runningServiceInfo = null;
281                break;
282            case TYPE_RUNNING_SERVICE:
283                batteryInfo = null;
284                anrInfo = null;
285                crashInfo = null;
286                runningServiceInfo = new RunningServiceInfo(in);
287                break;
288        }
289    }
290
291    /**
292     * Describes an application crash.
293     */
294    public static class CrashInfo {
295        /**
296         * Class name of the exception that caused the crash.
297         */
298        public String exceptionClassName;
299
300        /**
301         * Message stored in the exception.
302         */
303        public String exceptionMessage;
304
305        /**
306         * File which the exception was thrown from.
307         */
308        public String throwFileName;
309
310        /**
311         * Class which the exception was thrown from.
312         */
313        public String throwClassName;
314
315        /**
316         * Method which the exception was thrown from.
317         */
318        public String throwMethodName;
319
320        /**
321         * Line number the exception was thrown from.
322         */
323        public int throwLineNumber;
324
325        /**
326         * Stack trace.
327         */
328        public String stackTrace;
329
330        /**
331         * Create an uninitialized instance of CrashInfo.
332         */
333        public CrashInfo() {
334        }
335
336        /**
337         * Create an instance of CrashInfo initialized from an exception.
338         */
339        public CrashInfo(Throwable tr) {
340            StringWriter sw = new StringWriter();
341            PrintWriter pw = new FastPrintWriter(sw, false, 256);
342            tr.printStackTrace(pw);
343            pw.flush();
344            stackTrace = sw.toString();
345            exceptionMessage = tr.getMessage();
346
347            // Populate fields with the "root cause" exception
348            Throwable rootTr = tr;
349            while (tr.getCause() != null) {
350                tr = tr.getCause();
351                if (tr.getStackTrace() != null && tr.getStackTrace().length > 0) {
352                    rootTr = tr;
353                }
354                String msg = tr.getMessage();
355                if (msg != null && msg.length() > 0) {
356                    exceptionMessage = msg;
357                }
358            }
359
360            exceptionClassName = rootTr.getClass().getName();
361            if (rootTr.getStackTrace().length > 0) {
362                StackTraceElement trace = rootTr.getStackTrace()[0];
363                throwFileName = trace.getFileName();
364                throwClassName = trace.getClassName();
365                throwMethodName = trace.getMethodName();
366                throwLineNumber = trace.getLineNumber();
367            } else {
368                throwFileName = "unknown";
369                throwClassName = "unknown";
370                throwMethodName = "unknown";
371                throwLineNumber = 0;
372            }
373        }
374
375        /**
376         * Create an instance of CrashInfo initialized from a Parcel.
377         */
378        public CrashInfo(Parcel in) {
379            exceptionClassName = in.readString();
380            exceptionMessage = in.readString();
381            throwFileName = in.readString();
382            throwClassName = in.readString();
383            throwMethodName = in.readString();
384            throwLineNumber = in.readInt();
385            stackTrace = in.readString();
386        }
387
388        /**
389         * Save a CrashInfo instance to a parcel.
390         */
391        public void writeToParcel(Parcel dest, int flags) {
392            int start = dest.dataPosition();
393            dest.writeString(exceptionClassName);
394            dest.writeString(exceptionMessage);
395            dest.writeString(throwFileName);
396            dest.writeString(throwClassName);
397            dest.writeString(throwMethodName);
398            dest.writeInt(throwLineNumber);
399            dest.writeString(stackTrace);
400            int total = dest.dataPosition()-start;
401            if (total > 20*1024) {
402                Slog.d("Error", "ERR: exClass=" + exceptionClassName);
403                Slog.d("Error", "ERR: exMsg=" + exceptionMessage);
404                Slog.d("Error", "ERR: file=" + throwFileName);
405                Slog.d("Error", "ERR: class=" + throwClassName);
406                Slog.d("Error", "ERR: method=" + throwMethodName + " line=" + throwLineNumber);
407                Slog.d("Error", "ERR: stack=" + stackTrace);
408                Slog.d("Error", "ERR: TOTAL BYTES WRITTEN: " + (dest.dataPosition()-start));
409            }
410        }
411
412        /**
413         * Dump a CrashInfo instance to a Printer.
414         */
415        public void dump(Printer pw, String prefix) {
416            pw.println(prefix + "exceptionClassName: " + exceptionClassName);
417            pw.println(prefix + "exceptionMessage: " + exceptionMessage);
418            pw.println(prefix + "throwFileName: " + throwFileName);
419            pw.println(prefix + "throwClassName: " + throwClassName);
420            pw.println(prefix + "throwMethodName: " + throwMethodName);
421            pw.println(prefix + "throwLineNumber: " + throwLineNumber);
422            pw.println(prefix + "stackTrace: " + stackTrace);
423        }
424    }
425
426    /**
427     * Describes an application not responding error.
428     */
429    public static class AnrInfo {
430        /**
431         * Activity name.
432         */
433        public String activity;
434
435        /**
436         * Description of the operation that timed out.
437         */
438        public String cause;
439
440        /**
441         * Additional info, including CPU stats.
442         */
443        public String info;
444
445        /**
446         * Create an uninitialized instance of AnrInfo.
447         */
448        public AnrInfo() {
449        }
450
451        /**
452         * Create an instance of AnrInfo initialized from a Parcel.
453         */
454        public AnrInfo(Parcel in) {
455            activity = in.readString();
456            cause = in.readString();
457            info = in.readString();
458        }
459
460        /**
461         * Save an AnrInfo instance to a parcel.
462         */
463        public void writeToParcel(Parcel dest, int flags) {
464            dest.writeString(activity);
465            dest.writeString(cause);
466            dest.writeString(info);
467        }
468
469        /**
470         * Dump an AnrInfo instance to a Printer.
471         */
472        public void dump(Printer pw, String prefix) {
473            pw.println(prefix + "activity: " + activity);
474            pw.println(prefix + "cause: " + cause);
475            pw.println(prefix + "info: " + info);
476        }
477    }
478
479    /**
480     * Describes a battery usage report.
481     */
482    public static class BatteryInfo {
483        /**
484         * Percentage of the battery that was used up by the process.
485         */
486        public int usagePercent;
487
488        /**
489         * Duration in microseconds over which the process used the above
490         * percentage of battery.
491         */
492        public long durationMicros;
493
494        /**
495         * Dump of various info impacting battery use.
496         */
497        public String usageDetails;
498
499        /**
500         * Checkin details.
501         */
502        public String checkinDetails;
503
504        /**
505         * Create an uninitialized instance of BatteryInfo.
506         */
507        public BatteryInfo() {
508        }
509
510        /**
511         * Create an instance of BatteryInfo initialized from a Parcel.
512         */
513        public BatteryInfo(Parcel in) {
514            usagePercent = in.readInt();
515            durationMicros = in.readLong();
516            usageDetails = in.readString();
517            checkinDetails = in.readString();
518        }
519
520        /**
521         * Save a BatteryInfo instance to a parcel.
522         */
523        public void writeToParcel(Parcel dest, int flags) {
524            dest.writeInt(usagePercent);
525            dest.writeLong(durationMicros);
526            dest.writeString(usageDetails);
527            dest.writeString(checkinDetails);
528        }
529
530        /**
531         * Dump a BatteryInfo instance to a Printer.
532         */
533        public void dump(Printer pw, String prefix) {
534            pw.println(prefix + "usagePercent: " + usagePercent);
535            pw.println(prefix + "durationMicros: " + durationMicros);
536            pw.println(prefix + "usageDetails: " + usageDetails);
537            pw.println(prefix + "checkinDetails: " + checkinDetails);
538        }
539    }
540
541    /**
542     * Describes a running service report.
543     */
544    public static class RunningServiceInfo {
545        /**
546         * Duration in milliseconds that the service has been running.
547         */
548        public long durationMillis;
549
550        /**
551         * Dump of debug information about the service.
552         */
553        public String serviceDetails;
554
555        /**
556         * Create an uninitialized instance of RunningServiceInfo.
557         */
558        public RunningServiceInfo() {
559        }
560
561        /**
562         * Create an instance of RunningServiceInfo initialized from a Parcel.
563         */
564        public RunningServiceInfo(Parcel in) {
565            durationMillis = in.readLong();
566            serviceDetails = in.readString();
567        }
568
569        /**
570         * Save a RunningServiceInfo instance to a parcel.
571         */
572        public void writeToParcel(Parcel dest, int flags) {
573            dest.writeLong(durationMillis);
574            dest.writeString(serviceDetails);
575        }
576
577        /**
578         * Dump a BatteryInfo instance to a Printer.
579         */
580        public void dump(Printer pw, String prefix) {
581            pw.println(prefix + "durationMillis: " + durationMillis);
582            pw.println(prefix + "serviceDetails: " + serviceDetails);
583        }
584    }
585
586    public static final Parcelable.Creator<ApplicationErrorReport> CREATOR
587            = new Parcelable.Creator<ApplicationErrorReport>() {
588        public ApplicationErrorReport createFromParcel(Parcel source) {
589            return new ApplicationErrorReport(source);
590        }
591
592        public ApplicationErrorReport[] newArray(int size) {
593            return new ApplicationErrorReport[size];
594        }
595    };
596
597    public int describeContents() {
598        return 0;
599    }
600
601    /**
602     * Dump the report to a Printer.
603     */
604    public void dump(Printer pw, String prefix) {
605        pw.println(prefix + "type: " + type);
606        pw.println(prefix + "packageName: " + packageName);
607        pw.println(prefix + "installerPackageName: " + installerPackageName);
608        pw.println(prefix + "processName: " + processName);
609        pw.println(prefix + "time: " + time);
610        pw.println(prefix + "systemApp: " + systemApp);
611
612        switch (type) {
613            case TYPE_CRASH:
614                crashInfo.dump(pw, prefix);
615                break;
616            case TYPE_ANR:
617                anrInfo.dump(pw, prefix);
618                break;
619            case TYPE_BATTERY:
620                batteryInfo.dump(pw, prefix);
621                break;
622            case TYPE_RUNNING_SERVICE:
623                runningServiceInfo.dump(pw, prefix);
624                break;
625        }
626    }
627}
628