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