Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cad616ff authored by Android (Google) Code Review's avatar Android (Google) Code Review Committed by The Android Open Source Project
Browse files

am 97dd7ac8: Merge change 1860 into donut

Merge commit '97dd7ac8'

* commit '97dd7ac8':
  ActivityManagerService sends bug reports on crashes and ANRs
parents ed73bad6 97dd7ac8
Loading
Loading
Loading
Loading
+295 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.app;

import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
import android.util.Printer;

/**
 * Describes an application error.
 *
 * A report has a type, which is one of
 * <ul>
 * <li> {@link #TYPE_CRASH} application crash. Information about the crash
 * is stored in {@link #crashInfo}.
 * <li> {@link #TYPE_ANR} application not responding. Information about the
 * ANR is stored in {@link #anrInfo}.
 * <li> {@link #TYPE_NONE} uninitialized instance of {@link ApplicationErrorReport}.
 * </ul>
 *
 * @hide
 */

public class ApplicationErrorReport implements Parcelable {
    /**
     * Uninitialized error report.
     */
    public static final int TYPE_NONE = 0;

    /**
     * An error report about an application crash.
     */
    public static final int TYPE_CRASH = 1;

    /**
     * An error report about an application that's not responding.
     */
    public static final int TYPE_ANR = 2;

    /**
     * Type of this report. Can be one of {@link #TYPE_NONE},
     * {@link #TYPE_CRASH} or {@link #TYPE_ANR}.
     */
    public int type;

    /**
     * Package name of the application.
     */
    public String packageName;

    /**
     * Package name of the application which installed the application this
     * report pertains to.
     * This identifies which Market the application came from.
     */
    public String installerPackageName;

    /**
     * Process name of the application.
     */
    public String processName;

    /**
     * Time at which the error occurred.
     */
    public long time;

    /**
     * If this report is of type {@link #TYPE_CRASH}, contains an instance
     * of CrashInfo describing the crash; otherwise null.
     */
    public CrashInfo crashInfo;

    /**
     * If this report is of type {@link #TYPE_ANR}, contains an instance
     * of AnrInfo describing the ANR; otherwise null.
     */
    public AnrInfo anrInfo;

    /**
     * Create an uninitialized instance of {@link ApplicationErrorReport}.
     */
    public ApplicationErrorReport() {
    }

    /**
     * Create an instance of {@link ApplicationErrorReport} initialized from
     * a parcel.
     */
    ApplicationErrorReport(Parcel in) {
        type = in.readInt();
        packageName = in.readString();
        installerPackageName = in.readString();
        processName = in.readString();
        time = in.readLong();

        switch (type) {
            case TYPE_CRASH:
                crashInfo = new CrashInfo(in);
                break;
            case TYPE_ANR:
                anrInfo = new AnrInfo(in);
                break;
        }
    }

    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(type);
        dest.writeString(packageName);
        dest.writeString(installerPackageName);
        dest.writeString(processName);
        dest.writeLong(time);

        switch (type) {
            case TYPE_CRASH:
                crashInfo.writeToParcel(dest, flags);
                break;
            case TYPE_ANR:
                anrInfo.writeToParcel(dest, flags);
                break;
        }
    }

    /**
     * Describes an application crash.
     */
    public static class CrashInfo {
        /**
         * Class name of the exception that caused the crash.
         */
        public String exceptionClassName;

        /**
         * File which the exception was thrown from.
         */
        public String throwFileName;

        /**
         * Class which the exception was thrown from.
         */
        public String throwClassName;

        /**
         * Method which the exception was thrown from.
         */
        public String throwMethodName;

        /**
         * Stack trace.
         */
        public String stackTrace;

        /**
         * Create an uninitialized instance of CrashInfo.
         */
        public CrashInfo() {
        }

        /**
         * Create an instance of CrashInfo initialized from a Parcel.
         */
        public CrashInfo(Parcel in) {
            exceptionClassName = in.readString();
            throwFileName = in.readString();
            throwClassName = in.readString();
            throwMethodName = in.readString();
            stackTrace = in.readString();
        }

        /**
         * Save a CrashInfo instance to a parcel.
         */
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(exceptionClassName);
            dest.writeString(throwFileName);
            dest.writeString(throwClassName);
            dest.writeString(throwMethodName);
            dest.writeString(stackTrace);
        }

        /**
         * Dump a CrashInfo instance to a Printer.
         */
        public void dump(Printer pw, String prefix) {
            pw.println(prefix + "exceptionClassName: " + exceptionClassName);
            pw.println(prefix + "throwFileName: " + throwFileName);
            pw.println(prefix + "throwClassName: " + throwClassName);
            pw.println(prefix + "throwMethodName: " + throwMethodName);
            pw.println(prefix + "stackTrace: " + stackTrace);
        }
    }

    /**
     * Describes an application not responding error.
     */
    public static class AnrInfo {
        /**
         * Activity name.
         */
        public String activity;

        /**
         * Description of the operation that timed out.
         */
        public String cause;

        /**
         * Additional info, including CPU stats.
         */
        public String info;

        /**
         * Create an uninitialized instance of AnrInfo.
         */
        public AnrInfo() {
        }

        /**
         * Create an instance of AnrInfo initialized from a Parcel.
         */
        public AnrInfo(Parcel in) {
            activity = in.readString();
            cause = in.readString();
            info = in.readString();
        }

        /**
         * Save an AnrInfo instance to a parcel.
         */
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(activity);
            dest.writeString(cause);
            dest.writeString(info);
        }

        /**
         * Dump an AnrInfo instance to a Printer.
         */
        public void dump(Printer pw, String prefix) {
            pw.println(prefix + "activity: " + activity);
            pw.println(prefix + "cause: " + cause);
            pw.println(prefix + "info: " + info);
        }
    }

    public static final Parcelable.Creator<ApplicationErrorReport> CREATOR
            = new Parcelable.Creator<ApplicationErrorReport>() {
        public ApplicationErrorReport createFromParcel(Parcel source) {
            return new ApplicationErrorReport(source);
        }

        public ApplicationErrorReport[] newArray(int size) {
            return new ApplicationErrorReport[size];
        }
    };

    public int describeContents() {
        return 0;
    }

    /**
     * Dump the report to a Printer.
     */
    public void dump(Printer pw, String prefix) {
        pw.println(prefix + "type: " + type);
        pw.println(prefix + "packageName: " + packageName);
        pw.println(prefix + "installerPackageName: " + installerPackageName);
        pw.println(prefix + "processName: " + processName);
        pw.println(prefix + "time: " + time);

        switch (type) {
            case TYPE_CRASH:
                crashInfo.dump(pw, prefix);
                break;
            case TYPE_ANR:
                anrInfo.dump(pw, prefix);
                break;
        }
    }
}
+2 −0
Original line number Original line Diff line number Diff line
@@ -1761,6 +1761,8 @@
    <string name="anr_process">Process <xliff:g id="process">%1$s</xliff:g> is not responding.</string>
    <string name="anr_process">Process <xliff:g id="process">%1$s</xliff:g> is not responding.</string>
    <!-- Button allowing the user to close an application that is not responding. This will kill the application. -->
    <!-- Button allowing the user to close an application that is not responding. This will kill the application. -->
    <string name="force_close">Force close</string>
    <string name="force_close">Force close</string>
    <!-- Button allowing the user to send a bug report for application which has encountered an error. -->
    <string name="report">Report</string>
    <!-- Button allowing the user to choose to wait for an application that is not responding to become responsive again. -->
    <!-- Button allowing the user to choose to wait for an application that is not responding to become responsive again. -->
    <string name="wait">Wait</string>
    <string name="wait">Wait</string>
    <!-- Button allowing a developer to connect a debugger to an application that is not responding. -->
    <!-- Button allowing a developer to connect a debugger to an application that is not responding. -->
+114 −1
Original line number Original line Diff line number Diff line
@@ -30,6 +30,7 @@ import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.ActivityManagerNative;
import android.app.ActivityThread;
import android.app.ActivityThread;
import android.app.AlertDialog;
import android.app.AlertDialog;
import android.app.ApplicationErrorReport;
import android.app.Dialog;
import android.app.Dialog;
import android.app.IActivityWatcher;
import android.app.IActivityWatcher;
import android.app.IApplicationThread;
import android.app.IApplicationThread;
@@ -41,6 +42,7 @@ import android.app.IThumbnailReceiver;
import android.app.Instrumentation;
import android.app.Instrumentation;
import android.app.PendingIntent;
import android.app.PendingIntent;
import android.app.ResultInfo;
import android.app.ResultInfo;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Context;
@@ -78,10 +80,14 @@ import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.SystemProperties;
import android.provider.Checkin;
import android.provider.Checkin;
import android.provider.Settings;
import android.provider.Settings;
import android.server.data.CrashData;
import android.server.data.StackTraceElementData;
import android.server.data.ThrowableData;
import android.text.TextUtils;
import android.text.TextUtils;
import android.util.Config;
import android.util.Config;
import android.util.EventLog;
import android.util.EventLog;
import android.util.Log;
import android.util.Log;
import android.util.LogPrinter;
import android.util.PrintWriterPrinter;
import android.util.PrintWriterPrinter;
import android.util.SparseArray;
import android.util.SparseArray;
import android.view.Gravity;
import android.view.Gravity;
@@ -92,10 +98,13 @@ import android.view.WindowManagerPolicy;


import dalvik.system.Zygote;
import dalvik.system.Zygote;


import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.PrintWriter;
import java.lang.IllegalStateException;
import java.lang.IllegalStateException;
import java.lang.ref.WeakReference;
import java.lang.ref.WeakReference;
@@ -7809,6 +7818,30 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
        return handleAppCrashLocked(app);
        return handleAppCrashLocked(app);
    }
    }


    private ComponentName getErrorReportReceiver(ProcessRecord app) {
        IPackageManager pm = ActivityThread.getPackageManager();
        try {
            // was an installer package name specified when this app was
            // installed?
            String installerPackageName = pm.getInstallerPackageName(app.info.packageName);
            if (installerPackageName == null) {
                return null;
            }

            // is there an Activity in this package that handles ACTION_APP_ERROR?
            Intent intent = new Intent(Intent.ACTION_APP_ERROR);
            ResolveInfo info = pm.resolveIntentForPackage(intent, null, 0, installerPackageName);
            if (info == null || info.activityInfo == null) {
                return null;
            }

            return new ComponentName(installerPackageName, info.activityInfo.name);
        } catch (RemoteException e) {
            // will return null and no error report will be delivered
        }
        return null;
    }

    void makeAppNotRespondingLocked(ProcessRecord app,
    void makeAppNotRespondingLocked(ProcessRecord app,
            String tag, String shortMsg, String longMsg, byte[] crashData) {
            String tag, String shortMsg, String longMsg, byte[] crashData) {
        app.notResponding = true;
        app.notResponding = true;
@@ -7927,6 +7960,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
    }
    }


    void startAppProblemLocked(ProcessRecord app) {
    void startAppProblemLocked(ProcessRecord app) {
        app.errorReportReceiver = getErrorReportReceiver(app);
        skipCurrentReceiverLocked(app);
        skipCurrentReceiverLocked(app);
    }
    }


@@ -7959,7 +7993,6 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
    public int handleApplicationError(IBinder app, int flags,
    public int handleApplicationError(IBinder app, int flags,
            String tag, String shortMsg, String longMsg, byte[] crashData) {
            String tag, String shortMsg, String longMsg, byte[] crashData) {
        AppErrorResult result = new AppErrorResult();
        AppErrorResult result = new AppErrorResult();

        ProcessRecord r = null;
        ProcessRecord r = null;
        synchronized (this) {
        synchronized (this) {
            if (app != null) {
            if (app != null) {
@@ -8048,16 +8081,96 @@ public final class ActivityManagerService extends ActivityManagerNative implemen


        int res = result.get();
        int res = result.get();


        Intent appErrorIntent = null;
        synchronized (this) {
        synchronized (this) {
            if (r != null) {
            if (r != null) {
                mProcessCrashTimes.put(r.info.processName, r.info.uid,
                mProcessCrashTimes.put(r.info.processName, r.info.uid,
                        SystemClock.uptimeMillis());
                        SystemClock.uptimeMillis());
            }
            }
            if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) {
                appErrorIntent = createAppErrorIntentLocked(r);
                res = AppErrorDialog.FORCE_QUIT;
            }
        }

        if (appErrorIntent != null) {
            try {
                mContext.startActivity(appErrorIntent);
            } catch (ActivityNotFoundException e) {
                Log.w(TAG, "bug report receiver dissappeared", e);
            }
        }
        }


        return res;
        return res;
    }
    }
    
    
    Intent createAppErrorIntentLocked(ProcessRecord r) {
        ApplicationErrorReport report = createAppErrorReportLocked(r);
        if (report == null) {
            return null;
        }
        Intent result = new Intent(Intent.ACTION_APP_ERROR);
        result.setComponent(r.errorReportReceiver);
        result.putExtra(Intent.EXTRA_BUG_REPORT, report);
        result.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        return result;
    }

    ApplicationErrorReport createAppErrorReportLocked(ProcessRecord r) {
        if (r.errorReportReceiver == null) {
            return null;
        }

        if (!r.crashing && !r.notResponding) {
            return null;
        }

        try {
            ApplicationErrorReport report = new ApplicationErrorReport();
            report.packageName = r.info.packageName;
            report.installerPackageName = r.errorReportReceiver.getPackageName();
            report.processName = r.processName;

            if (r.crashing) {
                report.type = ApplicationErrorReport.TYPE_CRASH;
                report.crashInfo = new ApplicationErrorReport.CrashInfo();

                ByteArrayInputStream byteStream = new ByteArrayInputStream(
                        r.crashingReport.crashData);
                DataInputStream dataStream = new DataInputStream(byteStream);
                CrashData crashData = new CrashData(dataStream);
                ThrowableData throwData = crashData.getThrowableData();

                report.time = crashData.getTime();
                report.crashInfo.stackTrace = throwData.toString();

                // extract the source of the exception, useful for report
                // clustering
                while (throwData.getCause() != null) {
                    throwData = throwData.getCause();
                }
                StackTraceElementData trace = throwData.getStackTrace()[0];
                report.crashInfo.exceptionClassName = throwData.getType();
                report.crashInfo.throwFileName = trace.getFileName();
                report.crashInfo.throwClassName = trace.getClassName();
                report.crashInfo.throwMethodName = trace.getMethodName();
            } else if (r.notResponding) {
                report.type = ApplicationErrorReport.TYPE_ANR;
                report.anrInfo = new ApplicationErrorReport.AnrInfo();

                report.anrInfo.activity = r.notRespondingReport.tag;
                report.anrInfo.cause = r.notRespondingReport.shortMsg;
                report.anrInfo.info = r.notRespondingReport.longMsg;
            }

            return report;
        } catch (IOException e) {
            // we don't send it
        }

        return null;
    }

    public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState() {
    public List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState() {
        // assume our apps are happy - lazy create the list
        // assume our apps are happy - lazy create the list
        List<ActivityManager.ProcessErrorStateInfo> errList = null;
        List<ActivityManager.ProcessErrorStateInfo> errList = null;
+18 −3
Original line number Original line Diff line number Diff line
@@ -19,17 +19,22 @@ package com.android.server.am;
import static android.view.WindowManager.LayoutParams.FLAG_SYSTEM_ERROR;
import static android.view.WindowManager.LayoutParams.FLAG_SYSTEM_ERROR;


import android.content.Context;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.content.res.Resources;
import android.os.Handler;
import android.os.Handler;
import android.os.Message;
import android.os.Message;
import android.util.Log;


class AppErrorDialog extends BaseErrorDialog {
class AppErrorDialog extends BaseErrorDialog {
    private final static String TAG = "AppErrorDialog";

    private final AppErrorResult mResult;
    private final AppErrorResult mResult;
    private final ProcessRecord mProc;
    private final ProcessRecord mProc;


    // Event 'what' codes
    // Event 'what' codes
    static final int FORCE_QUIT = 0;
    static final int FORCE_QUIT = 0;
    static final int DEBUG = 1;
    static final int DEBUG = 1;
    static final int FORCE_QUIT_AND_REPORT = 2;


    // 5-minute timeout, then we automatically dismiss the crash dialog
    // 5-minute timeout, then we automatically dismiss the crash dialog
    static final long DISMISS_TIMEOUT = 1000 * 60 * 5;
    static final long DISMISS_TIMEOUT = 1000 * 60 * 5;
@@ -58,12 +63,22 @@ class AppErrorDialog extends BaseErrorDialog {


        setCancelable(false);
        setCancelable(false);


        setButton(res.getText(com.android.internal.R.string.force_close),
        setButton(DialogInterface.BUTTON_POSITIVE,
                res.getText(com.android.internal.R.string.force_close),
                mHandler.obtainMessage(FORCE_QUIT));
                mHandler.obtainMessage(FORCE_QUIT));

        if ((flags&1) != 0) {
        if ((flags&1) != 0) {
            setButton(res.getText(com.android.internal.R.string.debug),
            setButton(DialogInterface.BUTTON_NEUTRAL,
                    res.getText(com.android.internal.R.string.debug),
                    mHandler.obtainMessage(DEBUG));
                    mHandler.obtainMessage(DEBUG));
        }
        }

        if (app.errorReportReceiver != null) {
            setButton(DialogInterface.BUTTON_NEGATIVE,
                    res.getText(com.android.internal.R.string.report),
                    mHandler.obtainMessage(FORCE_QUIT_AND_REPORT));
        }

        setTitle(res.getText(com.android.internal.R.string.aerr_title));
        setTitle(res.getText(com.android.internal.R.string.aerr_title));
        getWindow().addFlags(FLAG_SYSTEM_ERROR);
        getWindow().addFlags(FLAG_SYSTEM_ERROR);
        getWindow().setTitle("Application Error: " + app.info.processName);
        getWindow().setTitle("Application Error: " + app.info.processName);
+40 −6
Original line number Original line Diff line number Diff line
@@ -18,7 +18,10 @@ package com.android.server.am;


import static android.view.WindowManager.LayoutParams.FLAG_SYSTEM_ERROR;
import static android.view.WindowManager.LayoutParams.FLAG_SYSTEM_ERROR;


import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.content.res.Resources;
import android.os.Handler;
import android.os.Handler;
import android.os.Message;
import android.os.Message;
@@ -26,6 +29,13 @@ import android.os.Process;
import android.util.Log;
import android.util.Log;


class AppNotRespondingDialog extends BaseErrorDialog {
class AppNotRespondingDialog extends BaseErrorDialog {
    private static final String TAG = "AppNotRespondingDialog";

    // Event 'what' codes
    static final int FORCE_CLOSE = 1;
    static final int WAIT = 2;
    static final int WAIT_AND_REPORT = 3;

    private final ActivityManagerService mService;
    private final ActivityManagerService mService;
    private final ProcessRecord mProc;
    private final ProcessRecord mProc;
    
    
@@ -67,10 +77,19 @@ class AppNotRespondingDialog extends BaseErrorDialog {
                ? res.getString(resid, name1.toString(), name2.toString())
                ? res.getString(resid, name1.toString(), name2.toString())
                : res.getString(resid, name1.toString()));
                : res.getString(resid, name1.toString()));


        setButton(res.getText(com.android.internal.R.string.force_close),
        setButton(DialogInterface.BUTTON_POSITIVE,
                mHandler.obtainMessage(1));
                res.getText(com.android.internal.R.string.force_close),
        setButton2(res.getText(com.android.internal.R.string.wait),
                mHandler.obtainMessage(FORCE_CLOSE));
                mHandler.obtainMessage(2));
        setButton(DialogInterface.BUTTON_NEUTRAL,
                res.getText(com.android.internal.R.string.wait),
                mHandler.obtainMessage(WAIT));

        if (app.errorReportReceiver != null) {
            setButton(DialogInterface.BUTTON_NEGATIVE,
                    res.getText(com.android.internal.R.string.report),
                    mHandler.obtainMessage(WAIT_AND_REPORT));
        }

        setTitle(res.getText(com.android.internal.R.string.anr_title));
        setTitle(res.getText(com.android.internal.R.string.anr_title));
        getWindow().addFlags(FLAG_SYSTEM_ERROR);
        getWindow().addFlags(FLAG_SYSTEM_ERROR);
        getWindow().setTitle("Application Not Responding: " + app.info.processName);
        getWindow().setTitle("Application Not Responding: " + app.info.processName);
@@ -81,16 +100,23 @@ class AppNotRespondingDialog extends BaseErrorDialog {


    private final Handler mHandler = new Handler() {
    private final Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
        public void handleMessage(Message msg) {
            Intent appErrorIntent = null;
            switch (msg.what) {
            switch (msg.what) {
                case 1:
                case FORCE_CLOSE:
                    // Kill the application.
                    // Kill the application.
                    mService.killAppAtUsersRequest(mProc,
                    mService.killAppAtUsersRequest(mProc,
                            AppNotRespondingDialog.this, true);
                            AppNotRespondingDialog.this, true);
                    break;
                    break;
                case 2:
                case WAIT_AND_REPORT:
                case WAIT:
                    // Continue waiting for the application.
                    // Continue waiting for the application.
                    synchronized (mService) {
                    synchronized (mService) {
                        ProcessRecord app = mProc;
                        ProcessRecord app = mProc;

                        if (msg.what == WAIT_AND_REPORT) {
                            appErrorIntent = mService.createAppErrorIntentLocked(app);
                        }

                        app.notResponding = false;
                        app.notResponding = false;
                        app.notRespondingReport = null;
                        app.notRespondingReport = null;
                        if (app.anrDialog == AppNotRespondingDialog.this) {
                        if (app.anrDialog == AppNotRespondingDialog.this) {
@@ -99,6 +125,14 @@ class AppNotRespondingDialog extends BaseErrorDialog {
                    }
                    }
                    break;
                    break;
            }
            }

            if (appErrorIntent != null) {
                try {
                    getContext().startActivity(appErrorIntent);
                } catch (ActivityNotFoundException e) {
                    Log.w(TAG, "bug report receiver dissappeared", e);
                }
            }
        }
        }
    };
    };
}
}
Loading