Merge change 2075 into donut

* changes:
  Fix bad filtering of error code when set_position_mode() is being called.
diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java
new file mode 100644
index 0000000..72cbff4
--- /dev/null
+++ b/core/java/android/app/ApplicationErrorReport.java
@@ -0,0 +1,295 @@
+/*
+ * 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;
+        }
+    }
+}
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 4a6a564..0f146e5 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1757,6 +1757,8 @@
     <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. -->
     <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. -->
     <string name="wait">Wait</string>
     <!-- Button allowing a developer to connect a debugger to an application that is not responding. -->
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index fd37cc2..f2959e3 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -30,6 +30,7 @@
 import android.app.ActivityManagerNative;
 import android.app.ActivityThread;
 import android.app.AlertDialog;
+import android.app.ApplicationErrorReport;
 import android.app.Dialog;
 import android.app.IActivityWatcher;
 import android.app.IApplicationThread;
@@ -41,6 +42,7 @@
 import android.app.Instrumentation;
 import android.app.PendingIntent;
 import android.app.ResultInfo;
+import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -78,10 +80,14 @@
 import android.os.SystemProperties;
 import android.provider.Checkin;
 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.util.Config;
 import android.util.EventLog;
 import android.util.Log;
+import android.util.LogPrinter;
 import android.util.PrintWriterPrinter;
 import android.util.SparseArray;
 import android.view.Gravity;
@@ -92,10 +98,13 @@
 
 import dalvik.system.Zygote;
 
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.lang.IllegalStateException;
 import java.lang.ref.WeakReference;
@@ -7809,6 +7818,30 @@
         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,
             String tag, String shortMsg, String longMsg, byte[] crashData) {
         app.notResponding = true;
@@ -7927,6 +7960,7 @@
     }
 
     void startAppProblemLocked(ProcessRecord app) {
+        app.errorReportReceiver = getErrorReportReceiver(app);
         skipCurrentReceiverLocked(app);
     }
 
@@ -7959,7 +7993,6 @@
     public int handleApplicationError(IBinder app, int flags,
             String tag, String shortMsg, String longMsg, byte[] crashData) {
         AppErrorResult result = new AppErrorResult();
-
         ProcessRecord r = null;
         synchronized (this) {
             if (app != null) {
@@ -8048,16 +8081,96 @@
 
         int res = result.get();
 
+        Intent appErrorIntent = null;
         synchronized (this) {
             if (r != null) {
                 mProcessCrashTimes.put(r.info.processName, r.info.uid,
                         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;
     }
     
+    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() {
         // assume our apps are happy - lazy create the list
         List<ActivityManager.ProcessErrorStateInfo> errList = null;
diff --git a/services/java/com/android/server/am/AppErrorDialog.java b/services/java/com/android/server/am/AppErrorDialog.java
index 3fcfad0..33894d6 100644
--- a/services/java/com/android/server/am/AppErrorDialog.java
+++ b/services/java/com/android/server/am/AppErrorDialog.java
@@ -19,17 +19,22 @@
 import static android.view.WindowManager.LayoutParams.FLAG_SYSTEM_ERROR;
 
 import android.content.Context;
+import android.content.DialogInterface;
 import android.content.res.Resources;
 import android.os.Handler;
 import android.os.Message;
+import android.util.Log;
 
 class AppErrorDialog extends BaseErrorDialog {
+    private final static String TAG = "AppErrorDialog";
+
     private final AppErrorResult mResult;
     private final ProcessRecord mProc;
 
     // Event 'what' codes
     static final int FORCE_QUIT = 0;
     static final int DEBUG = 1;
+    static final int FORCE_QUIT_AND_REPORT = 2;
 
     // 5-minute timeout, then we automatically dismiss the crash dialog
     static final long DISMISS_TIMEOUT = 1000 * 60 * 5;
@@ -58,12 +63,22 @@
 
         setCancelable(false);
 
-        setButton(res.getText(com.android.internal.R.string.force_close),
-                    mHandler.obtainMessage(FORCE_QUIT));
+        setButton(DialogInterface.BUTTON_POSITIVE,
+                res.getText(com.android.internal.R.string.force_close),
+                mHandler.obtainMessage(FORCE_QUIT));
+
         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));
         }
+
+        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));
         getWindow().addFlags(FLAG_SYSTEM_ERROR);
         getWindow().setTitle("Application Error: " + app.info.processName);
diff --git a/services/java/com/android/server/am/AppNotRespondingDialog.java b/services/java/com/android/server/am/AppNotRespondingDialog.java
index 7390ed0..03c2a04 100644
--- a/services/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/java/com/android/server/am/AppNotRespondingDialog.java
@@ -18,7 +18,10 @@
 
 import static android.view.WindowManager.LayoutParams.FLAG_SYSTEM_ERROR;
 
+import android.content.ActivityNotFoundException;
 import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
 import android.content.res.Resources;
 import android.os.Handler;
 import android.os.Message;
@@ -26,6 +29,13 @@
 import android.util.Log;
 
 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 ProcessRecord mProc;
     
@@ -67,10 +77,19 @@
                 ? res.getString(resid, name1.toString(), name2.toString())
                 : res.getString(resid, name1.toString()));
 
-        setButton(res.getText(com.android.internal.R.string.force_close),
-                mHandler.obtainMessage(1));
-        setButton2(res.getText(com.android.internal.R.string.wait),
-                mHandler.obtainMessage(2));
+        setButton(DialogInterface.BUTTON_POSITIVE,
+                res.getText(com.android.internal.R.string.force_close),
+                mHandler.obtainMessage(FORCE_CLOSE));
+        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));
         getWindow().addFlags(FLAG_SYSTEM_ERROR);
         getWindow().setTitle("Application Not Responding: " + app.info.processName);
@@ -81,16 +100,23 @@
 
     private final Handler mHandler = new Handler() {
         public void handleMessage(Message msg) {
+            Intent appErrorIntent = null;
             switch (msg.what) {
-                case 1:
+                case FORCE_CLOSE:
                     // Kill the application.
                     mService.killAppAtUsersRequest(mProc,
                             AppNotRespondingDialog.this, true);
                     break;
-                case 2:
+                case WAIT_AND_REPORT:
+                case WAIT:
                     // Continue waiting for the application.
                     synchronized (mService) {
                         ProcessRecord app = mProc;
+
+                        if (msg.what == WAIT_AND_REPORT) {
+                            appErrorIntent = mService.createAppErrorIntentLocked(app);
+                        }
+
                         app.notResponding = false;
                         app.notRespondingReport = null;
                         if (app.anrDialog == AppNotRespondingDialog.this) {
@@ -99,6 +125,14 @@
                     }
                     break;
             }
+
+            if (appErrorIntent != null) {
+                try {
+                    getContext().startActivity(appErrorIntent);
+                } catch (ActivityNotFoundException e) {
+                    Log.w(TAG, "bug report receiver dissappeared", e);
+                }
+            }
         }
     };
 }
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 68aebc3..419dadf 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -107,6 +107,10 @@
     ActivityManager.ProcessErrorStateInfo crashingReport;
     ActivityManager.ProcessErrorStateInfo notRespondingReport;
 
+    // Who will be notified of the error. This is usually an activity in the
+    // app that installed the package.
+    ComponentName errorReportReceiver;
+
     void dump(PrintWriter pw, String prefix) {
         if (info.className != null) {
             pw.print(prefix); pw.print("class="); pw.println(info.className);
@@ -157,7 +161,14 @@
                     pw.print(" "); pw.print(crashDialog);
                     pw.print(" notResponding="); pw.print(notResponding);
                     pw.print(" " ); pw.print(anrDialog);
-                    pw.print(" bad="); pw.println(bad);
+                    pw.print(" bad="); pw.print(bad);
+
+                    // crashing or notResponding is always set before errorReportReceiver
+                    if (errorReportReceiver != null) {
+                        pw.print(" errorReportReceiver=");
+                        pw.print(errorReportReceiver.flattenToShortString());
+                    }
+                    pw.println();
         }
         if (activities.size() > 0) {
             pw.print(prefix); pw.print("activities="); pw.println(activities);