ApplicationErrorReport.java revision 21f1bd17b2dfe361acbb28453b3f3b1a110932fa
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 java.io.PrintWriter;
31import java.io.StringWriter;
32
33/**
34 * Describes an application error.
35 *
36 * A report has a type, which is one of
37 * <ul>
38 * <li> {@link #TYPE_CRASH} application crash. Information about the crash
39 * is stored in {@link #crashInfo}.
40 * <li> {@link #TYPE_ANR} application not responding. Information about the
41 * ANR is stored in {@link #anrInfo}.
42 * <li> {@link #TYPE_NONE} uninitialized instance of {@link ApplicationErrorReport}.
43 * </ul>
44 *
45 * @hide
46 */
47
48public class ApplicationErrorReport implements Parcelable {
49    // System property defining error report receiver for system apps
50    static final String SYSTEM_APPS_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.system.apps";
51
52    // System property defining default error report receiver
53    static final String DEFAULT_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.default";
54
55
56    /**
57     * Uninitialized error report.
58     */
59    public static final int TYPE_NONE = 0;
60
61    /**
62     * An error report about an application crash.
63     */
64    public static final int TYPE_CRASH = 1;
65
66    /**
67     * An error report about an application that's not responding.
68     */
69    public static final int TYPE_ANR = 2;
70
71    /**
72     * An error report about an application that's consuming too much battery.
73     */
74    public static final int TYPE_BATTERY = 3;
75
76    /**
77     * Type of this report. Can be one of {@link #TYPE_NONE},
78     * {@link #TYPE_CRASH}, {@link #TYPE_ANR}, or {@link #TYPE_BATTERY}.
79     */
80    public int type;
81
82    /**
83     * Package name of the application.
84     */
85    public String packageName;
86
87    /**
88     * Package name of the application which installed the application this
89     * report pertains to.
90     * This identifies which Market the application came from.
91     */
92    public String installerPackageName;
93
94    /**
95     * Process name of the application.
96     */
97    public String processName;
98
99    /**
100     * Time at which the error occurred.
101     */
102    public long time;
103
104    /**
105     * Set if the app is on the system image.
106     */
107    public boolean systemApp;
108
109    /**
110     * If this report is of type {@link #TYPE_CRASH}, contains an instance
111     * of CrashInfo describing the crash; otherwise null.
112     */
113    public CrashInfo crashInfo;
114
115    /**
116     * If this report is of type {@link #TYPE_ANR}, contains an instance
117     * of AnrInfo describing the ANR; otherwise null.
118     */
119    public AnrInfo anrInfo;
120
121    /**
122     * Text containing battery usage data.
123     */
124    public String batteryText;
125
126    /**
127     * Create an uninitialized instance of {@link ApplicationErrorReport}.
128     */
129    public ApplicationErrorReport() {
130    }
131
132    /**
133     * Create an instance of {@link ApplicationErrorReport} initialized from
134     * a parcel.
135     */
136    ApplicationErrorReport(Parcel in) {
137        readFromParcel(in);
138    }
139
140    public static ComponentName getErrorReportReceiver(Context context,
141            String packageName, int appFlags) {
142        // check if error reporting is enabled in secure settings
143        int enabled = Settings.Secure.getInt(context.getContentResolver(),
144                Settings.Secure.SEND_ACTION_APP_ERROR, 0);
145        if (enabled == 0) {
146            return null;
147        }
148
149        PackageManager pm = context.getPackageManager();
150
151        // look for receiver in the installer package
152        String candidate = pm.getInstallerPackageName(packageName);
153        ComponentName result = getErrorReportReceiver(pm, packageName, candidate);
154        if (result != null) {
155            return result;
156        }
157
158        // if the error app is on the system image, look for system apps
159        // error receiver
160        if ((appFlags&ApplicationInfo.FLAG_SYSTEM) != 0) {
161            candidate = SystemProperties.get(SYSTEM_APPS_ERROR_RECEIVER_PROPERTY);
162            result = getErrorReportReceiver(pm, packageName, candidate);
163            if (result != null) {
164                return result;
165            }
166        }
167
168        // if there is a default receiver, try that
169        candidate = SystemProperties.get(DEFAULT_ERROR_RECEIVER_PROPERTY);
170        return getErrorReportReceiver(pm, packageName, candidate);
171    }
172
173    /**
174     * Return activity in receiverPackage that handles ACTION_APP_ERROR.
175     *
176     * @param pm PackageManager isntance
177     * @param errorPackage package which caused the error
178     * @param receiverPackage candidate package to receive the error
179     * @return activity component within receiverPackage which handles
180     * ACTION_APP_ERROR, or null if not found
181     */
182    static ComponentName getErrorReportReceiver(PackageManager pm, String errorPackage,
183            String receiverPackage) {
184        if (receiverPackage == null || receiverPackage.length() == 0) {
185            return null;
186        }
187
188        // break the loop if it's the error report receiver package that crashed
189        if (receiverPackage.equals(errorPackage)) {
190            return null;
191        }
192
193        Intent intent = new Intent(Intent.ACTION_APP_ERROR);
194        intent.setPackage(receiverPackage);
195        ResolveInfo info = pm.resolveActivity(intent, 0);
196        if (info == null || info.activityInfo == null) {
197            return null;
198        }
199        return new ComponentName(receiverPackage, info.activityInfo.name);
200    }
201
202    public void writeToParcel(Parcel dest, int flags) {
203        dest.writeInt(type);
204        dest.writeString(packageName);
205        dest.writeString(installerPackageName);
206        dest.writeString(processName);
207        dest.writeLong(time);
208        dest.writeInt(systemApp ? 1 : 0);
209
210        switch (type) {
211            case TYPE_CRASH:
212                crashInfo.writeToParcel(dest, flags);
213                break;
214            case TYPE_ANR:
215                anrInfo.writeToParcel(dest, flags);
216                break;
217            case TYPE_BATTERY:
218                dest.writeString(batteryText);
219                break;
220        }
221    }
222
223    public void readFromParcel(Parcel in) {
224        type = in.readInt();
225        packageName = in.readString();
226        installerPackageName = in.readString();
227        processName = in.readString();
228        time = in.readLong();
229        systemApp = in.readInt() == 1;
230
231        switch (type) {
232            case TYPE_CRASH:
233                crashInfo = new CrashInfo(in);
234                anrInfo = null;
235                batteryText = null;
236                break;
237            case TYPE_ANR:
238                anrInfo = new AnrInfo(in);
239                crashInfo = null;
240                batteryText = null;
241                break;
242            case TYPE_BATTERY:
243                batteryText = in.readString();
244                anrInfo = null;
245                crashInfo = null;
246                break;
247        }
248    }
249
250    /**
251     * Describes an application crash.
252     */
253    public static class CrashInfo {
254        /**
255         * Class name of the exception that caused the crash.
256         */
257        public String exceptionClassName;
258
259        /**
260         * Message stored in the exception.
261         */
262        public String exceptionMessage;
263
264        /**
265         * File which the exception was thrown from.
266         */
267        public String throwFileName;
268
269        /**
270         * Class which the exception was thrown from.
271         */
272        public String throwClassName;
273
274        /**
275         * Method which the exception was thrown from.
276         */
277        public String throwMethodName;
278
279        /**
280         * Line number the exception was thrown from.
281         */
282        public int throwLineNumber;
283
284        /**
285         * Stack trace.
286         */
287        public String stackTrace;
288
289        /**
290         * Create an uninitialized instance of CrashInfo.
291         */
292        public CrashInfo() {
293        }
294
295        /**
296         * Create an instance of CrashInfo initialized from an exception.
297         */
298        public CrashInfo(Throwable tr) {
299            StringWriter sw = new StringWriter();
300            tr.printStackTrace(new PrintWriter(sw));
301            stackTrace = sw.toString();
302            exceptionMessage = tr.getMessage();
303
304            // Populate fields with the "root cause" exception
305            while (tr.getCause() != null) {
306                tr = tr.getCause();
307                String msg = tr.getMessage();
308                if (msg != null && msg.length() > 0) {
309                    exceptionMessage = msg;
310                }
311            }
312
313            exceptionClassName = tr.getClass().getName();
314            StackTraceElement trace = tr.getStackTrace()[0];
315            throwFileName = trace.getFileName();
316            throwClassName = trace.getClassName();
317            throwMethodName = trace.getMethodName();
318            throwLineNumber = trace.getLineNumber();
319        }
320
321        /**
322         * Create an instance of CrashInfo initialized from a Parcel.
323         */
324        public CrashInfo(Parcel in) {
325            exceptionClassName = in.readString();
326            exceptionMessage = in.readString();
327            throwFileName = in.readString();
328            throwClassName = in.readString();
329            throwMethodName = in.readString();
330            throwLineNumber = in.readInt();
331            stackTrace = in.readString();
332        }
333
334        /**
335         * Save a CrashInfo instance to a parcel.
336         */
337        public void writeToParcel(Parcel dest, int flags) {
338            dest.writeString(exceptionClassName);
339            dest.writeString(exceptionMessage);
340            dest.writeString(throwFileName);
341            dest.writeString(throwClassName);
342            dest.writeString(throwMethodName);
343            dest.writeInt(throwLineNumber);
344            dest.writeString(stackTrace);
345        }
346
347        /**
348         * Dump a CrashInfo instance to a Printer.
349         */
350        public void dump(Printer pw, String prefix) {
351            pw.println(prefix + "exceptionClassName: " + exceptionClassName);
352            pw.println(prefix + "exceptionMessage: " + exceptionMessage);
353            pw.println(prefix + "throwFileName: " + throwFileName);
354            pw.println(prefix + "throwClassName: " + throwClassName);
355            pw.println(prefix + "throwMethodName: " + throwMethodName);
356            pw.println(prefix + "throwLineNumber: " + throwLineNumber);
357            pw.println(prefix + "stackTrace: " + stackTrace);
358        }
359    }
360
361    /**
362     * Describes an application not responding error.
363     */
364    public static class AnrInfo {
365        /**
366         * Activity name.
367         */
368        public String activity;
369
370        /**
371         * Description of the operation that timed out.
372         */
373        public String cause;
374
375        /**
376         * Additional info, including CPU stats.
377         */
378        public String info;
379
380        /**
381         * Create an uninitialized instance of AnrInfo.
382         */
383        public AnrInfo() {
384        }
385
386        /**
387         * Create an instance of AnrInfo initialized from a Parcel.
388         */
389        public AnrInfo(Parcel in) {
390            activity = in.readString();
391            cause = in.readString();
392            info = in.readString();
393        }
394
395        /**
396         * Save an AnrInfo instance to a parcel.
397         */
398        public void writeToParcel(Parcel dest, int flags) {
399            dest.writeString(activity);
400            dest.writeString(cause);
401            dest.writeString(info);
402        }
403
404        /**
405         * Dump an AnrInfo instance to a Printer.
406         */
407        public void dump(Printer pw, String prefix) {
408            pw.println(prefix + "activity: " + activity);
409            pw.println(prefix + "cause: " + cause);
410            pw.println(prefix + "info: " + info);
411        }
412    }
413
414    public static final Parcelable.Creator<ApplicationErrorReport> CREATOR
415            = new Parcelable.Creator<ApplicationErrorReport>() {
416        public ApplicationErrorReport createFromParcel(Parcel source) {
417            return new ApplicationErrorReport(source);
418        }
419
420        public ApplicationErrorReport[] newArray(int size) {
421            return new ApplicationErrorReport[size];
422        }
423    };
424
425    public int describeContents() {
426        return 0;
427    }
428
429    /**
430     * Dump the report to a Printer.
431     */
432    public void dump(Printer pw, String prefix) {
433        pw.println(prefix + "type: " + type);
434        pw.println(prefix + "packageName: " + packageName);
435        pw.println(prefix + "installerPackageName: " + installerPackageName);
436        pw.println(prefix + "processName: " + processName);
437        pw.println(prefix + "time: " + time);
438        pw.println(prefix + "systemApp: " + systemApp);
439
440        switch (type) {
441            case TYPE_CRASH:
442                crashInfo.dump(pw, prefix);
443                break;
444            case TYPE_ANR:
445                anrInfo.dump(pw, prefix);
446                break;
447            case TYPE_BATTERY:
448                pw.println(batteryText);
449                break;
450        }
451    }
452}
453