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 = 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            int start = dest.dataPosition();
383            dest.writeString(exceptionClassName);
384            dest.writeString(exceptionMessage);
385            dest.writeString(throwFileName);
386            dest.writeString(throwClassName);
387            dest.writeString(throwMethodName);
388            dest.writeInt(throwLineNumber);
389            dest.writeString(stackTrace);
390            int total = dest.dataPosition()-start;
391            if (total > 20*1024) {
392                Slog.d("Error", "ERR: exClass=" + exceptionClassName);
393                Slog.d("Error", "ERR: exMsg=" + exceptionMessage);
394                Slog.d("Error", "ERR: file=" + throwFileName);
395                Slog.d("Error", "ERR: class=" + throwClassName);
396                Slog.d("Error", "ERR: method=" + throwMethodName + " line=" + throwLineNumber);
397                Slog.d("Error", "ERR: stack=" + stackTrace);
398                Slog.d("Error", "ERR: TOTAL BYTES WRITTEN: " + (dest.dataPosition()-start));
399            }
400        }
401
402        /**
403         * Dump a CrashInfo instance to a Printer.
404         */
405        public void dump(Printer pw, String prefix) {
406            pw.println(prefix + "exceptionClassName: " + exceptionClassName);
407            pw.println(prefix + "exceptionMessage: " + exceptionMessage);
408            pw.println(prefix + "throwFileName: " + throwFileName);
409            pw.println(prefix + "throwClassName: " + throwClassName);
410            pw.println(prefix + "throwMethodName: " + throwMethodName);
411            pw.println(prefix + "throwLineNumber: " + throwLineNumber);
412            pw.println(prefix + "stackTrace: " + stackTrace);
413        }
414    }
415
416    /**
417     * Describes an application not responding error.
418     */
419    public static class AnrInfo {
420        /**
421         * Activity name.
422         */
423        public String activity;
424
425        /**
426         * Description of the operation that timed out.
427         */
428        public String cause;
429
430        /**
431         * Additional info, including CPU stats.
432         */
433        public String info;
434
435        /**
436         * Create an uninitialized instance of AnrInfo.
437         */
438        public AnrInfo() {
439        }
440
441        /**
442         * Create an instance of AnrInfo initialized from a Parcel.
443         */
444        public AnrInfo(Parcel in) {
445            activity = in.readString();
446            cause = in.readString();
447            info = in.readString();
448        }
449
450        /**
451         * Save an AnrInfo instance to a parcel.
452         */
453        public void writeToParcel(Parcel dest, int flags) {
454            dest.writeString(activity);
455            dest.writeString(cause);
456            dest.writeString(info);
457        }
458
459        /**
460         * Dump an AnrInfo instance to a Printer.
461         */
462        public void dump(Printer pw, String prefix) {
463            pw.println(prefix + "activity: " + activity);
464            pw.println(prefix + "cause: " + cause);
465            pw.println(prefix + "info: " + info);
466        }
467    }
468
469    /**
470     * Describes a battery usage report.
471     */
472    public static class BatteryInfo {
473        /**
474         * Percentage of the battery that was used up by the process.
475         */
476        public int usagePercent;
477
478        /**
479         * Duration in microseconds over which the process used the above
480         * percentage of battery.
481         */
482        public long durationMicros;
483
484        /**
485         * Dump of various info impacting battery use.
486         */
487        public String usageDetails;
488
489        /**
490         * Checkin details.
491         */
492        public String checkinDetails;
493
494        /**
495         * Create an uninitialized instance of BatteryInfo.
496         */
497        public BatteryInfo() {
498        }
499
500        /**
501         * Create an instance of BatteryInfo initialized from a Parcel.
502         */
503        public BatteryInfo(Parcel in) {
504            usagePercent = in.readInt();
505            durationMicros = in.readLong();
506            usageDetails = in.readString();
507            checkinDetails = in.readString();
508        }
509
510        /**
511         * Save a BatteryInfo instance to a parcel.
512         */
513        public void writeToParcel(Parcel dest, int flags) {
514            dest.writeInt(usagePercent);
515            dest.writeLong(durationMicros);
516            dest.writeString(usageDetails);
517            dest.writeString(checkinDetails);
518        }
519
520        /**
521         * Dump a BatteryInfo instance to a Printer.
522         */
523        public void dump(Printer pw, String prefix) {
524            pw.println(prefix + "usagePercent: " + usagePercent);
525            pw.println(prefix + "durationMicros: " + durationMicros);
526            pw.println(prefix + "usageDetails: " + usageDetails);
527            pw.println(prefix + "checkinDetails: " + checkinDetails);
528        }
529    }
530
531    /**
532     * Describes a running service report.
533     */
534    public static class RunningServiceInfo {
535        /**
536         * Duration in milliseconds that the service has been running.
537         */
538        public long durationMillis;
539
540        /**
541         * Dump of debug information about the service.
542         */
543        public String serviceDetails;
544
545        /**
546         * Create an uninitialized instance of RunningServiceInfo.
547         */
548        public RunningServiceInfo() {
549        }
550
551        /**
552         * Create an instance of RunningServiceInfo initialized from a Parcel.
553         */
554        public RunningServiceInfo(Parcel in) {
555            durationMillis = in.readLong();
556            serviceDetails = in.readString();
557        }
558
559        /**
560         * Save a RunningServiceInfo instance to a parcel.
561         */
562        public void writeToParcel(Parcel dest, int flags) {
563            dest.writeLong(durationMillis);
564            dest.writeString(serviceDetails);
565        }
566
567        /**
568         * Dump a BatteryInfo instance to a Printer.
569         */
570        public void dump(Printer pw, String prefix) {
571            pw.println(prefix + "durationMillis: " + durationMillis);
572            pw.println(prefix + "serviceDetails: " + serviceDetails);
573        }
574    }
575
576    public static final Parcelable.Creator<ApplicationErrorReport> CREATOR
577            = new Parcelable.Creator<ApplicationErrorReport>() {
578        public ApplicationErrorReport createFromParcel(Parcel source) {
579            return new ApplicationErrorReport(source);
580        }
581
582        public ApplicationErrorReport[] newArray(int size) {
583            return new ApplicationErrorReport[size];
584        }
585    };
586
587    public int describeContents() {
588        return 0;
589    }
590
591    /**
592     * Dump the report to a Printer.
593     */
594    public void dump(Printer pw, String prefix) {
595        pw.println(prefix + "type: " + type);
596        pw.println(prefix + "packageName: " + packageName);
597        pw.println(prefix + "installerPackageName: " + installerPackageName);
598        pw.println(prefix + "processName: " + processName);
599        pw.println(prefix + "time: " + time);
600        pw.println(prefix + "systemApp: " + systemApp);
601
602        switch (type) {
603            case TYPE_CRASH:
604                crashInfo.dump(pw, prefix);
605                break;
606            case TYPE_ANR:
607                anrInfo.dump(pw, prefix);
608                break;
609            case TYPE_BATTERY:
610                batteryInfo.dump(pw, prefix);
611                break;
612            case TYPE_RUNNING_SERVICE:
613                runningServiceInfo.dump(pw, prefix);
614                break;
615        }
616    }
617}
618