resolved conflicts for merge of c16da6aa to master

Change-Id: Ibaaf2995def15573609562162902b3d73dd78ec0
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 46f9015..6cd67d6 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -862,6 +862,11 @@
             public void run() {
                 Slog.i(TAG, "Making services ready");
 
+                try {
+                    ActivityManagerService.self().startObservingNativeCrashes();
+                } catch (Throwable e) {
+                    reportWtf("observing native crashes", e);
+                }
                 if (!headless) {
                     startSystemUi(contextF);
                 }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index c24a8b9..de3d09f 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1443,6 +1443,11 @@
         wm.createStack(HOME_ACTIVITY_STACK, -1, StackBox.TASK_STACK_GOES_OVER, 1.0f);
     }
 
+    public void startObservingNativeCrashes() {
+        final NativeCrashListener ncl = new NativeCrashListener();
+        ncl.start();
+    }
+
     public static final Context main(int factoryTest) {
         AThread thr = new AThread();
         thr.start();
@@ -8157,6 +8162,14 @@
         final String processName = app == null ? "system_server"
                 : (r == null ? "unknown" : r.processName);
 
+        handleApplicationCrashInner(r, processName, crashInfo);
+    }
+
+    /* Native crash reporting uses this inner version because it needs to be somewhat
+     * decoupled from the AM-managed cleanup lifecycle
+     */
+    void handleApplicationCrashInner(ProcessRecord r, String processName,
+            ApplicationErrorReport.CrashInfo crashInfo) {
         EventLog.writeEvent(EventLogTags.AM_CRASH, Binder.getCallingPid(),
                 UserHandle.getUserId(Binder.getCallingUid()), processName,
                 r == null ? -1 : r.info.flags,
@@ -8670,7 +8683,7 @@
             return null;
         }
 
-        if (!r.crashing && !r.notResponding) {
+        if (!r.crashing && !r.notResponding && !r.forceCrashReport) {
             return null;
         }
 
@@ -8681,7 +8694,7 @@
         report.time = timeMillis;
         report.systemApp = (r.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
 
-        if (r.crashing) {
+        if (r.crashing || r.forceCrashReport) {
             report.type = ApplicationErrorReport.TYPE_CRASH;
             report.crashInfo = crashInfo;
         } else if (r.notResponding) {
@@ -10559,7 +10572,7 @@
         mProcessesToGc.remove(app);
         
         // Dismiss any open dialogs.
-        if (app.crashDialog != null) {
+        if (app.crashDialog != null && !app.forceCrashReport) {
             app.crashDialog.dismiss();
             app.crashDialog = null;
         }
diff --git a/services/java/com/android/server/am/NativeCrashListener.java b/services/java/com/android/server/am/NativeCrashListener.java
new file mode 100644
index 0000000..e83433f
--- /dev/null
+++ b/services/java/com/android/server/am/NativeCrashListener.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2013 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 com.android.server.am;
+
+import android.app.ApplicationErrorReport.CrashInfo;
+import android.util.Slog;
+
+import libcore.io.ErrnoException;
+import libcore.io.Libcore;
+import libcore.io.StructTimeval;
+import libcore.io.StructUcred;
+
+import static libcore.io.OsConstants.*;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.net.InetSocketAddress;
+import java.net.InetUnixAddress;
+
+/**
+ * Set up a Unix domain socket that debuggerd will connect() to in
+ * order to write a description of a native crash.  The crash info is
+ * then parsed and forwarded to the ActivityManagerService's normal
+ * crash handling code.
+ *
+ * Note that this component runs in a separate thread.
+ */
+class NativeCrashListener extends Thread {
+    static final String TAG = "NativeCrashListener";
+    static final boolean DEBUG = false;
+
+    // Must match the path defined in debuggerd.c.
+    static final String DEBUGGERD_SOCKET_PATH = "/data/system/ndebugsocket";
+
+    // Use a short timeout on socket operations and abandon the connection
+    // on hard errors
+    static final long SOCKET_TIMEOUT_MILLIS = 1000;  // 1 second
+
+    final ActivityManagerService mAm;
+
+    /*
+     * Spin the actual work of handling a debuggerd crash report into a
+     * separate thread so that the listener can go immediately back to
+     * accepting incoming connections.
+     */
+    class NativeCrashReporter extends Thread {
+        ProcessRecord mApp;
+        int mSignal;
+        String mCrashReport;
+
+        NativeCrashReporter(ProcessRecord app, int signal, String report) {
+            super("NativeCrashReport");
+            mApp = app;
+            mSignal = signal;
+            mCrashReport = report;
+        }
+
+        @Override
+        public void run() {
+            try {
+                CrashInfo ci = new CrashInfo();
+                ci.exceptionClassName = "Native crash";
+                ci.exceptionMessage = Libcore.os.strsignal(mSignal);
+                ci.throwFileName = "unknown";
+                ci.throwClassName = "unknown";
+                ci.throwMethodName = "unknown";
+                ci.stackTrace = mCrashReport;
+
+                if (DEBUG) Slog.v(TAG, "Calling handleApplicationCrash()");
+                mAm.handleApplicationCrashInner(mApp, mApp.processName, ci);
+                if (DEBUG) Slog.v(TAG, "<-- handleApplicationCrash() returned");
+            } catch (Exception e) {
+                Slog.e(TAG, "Unable to report native crash", e);
+            }
+        }
+    }
+
+    /*
+     * Daemon thread that accept()s incoming domain socket connections from debuggerd
+     * and processes the crash dump that is passed through.
+     */
+    NativeCrashListener() {
+        mAm = ActivityManagerService.self();
+    }
+
+    @Override
+    public void run() {
+        final byte[] ackSignal = new byte[1];
+
+        if (DEBUG) Slog.i(TAG, "Starting up");
+
+        // The file system entity for this socket is created with 0700 perms, owned
+        // by system:system.  debuggerd runs as root, so is capable of connecting to
+        // it, but 3rd party apps cannot.
+        {
+            File socketFile = new File(DEBUGGERD_SOCKET_PATH);
+            if (socketFile.exists()) {
+                socketFile.delete();
+            }
+        }
+
+        try {
+            FileDescriptor serverFd = Libcore.os.socket(AF_UNIX, SOCK_STREAM, 0);
+            final InetUnixAddress sockAddr = new InetUnixAddress(DEBUGGERD_SOCKET_PATH);
+            Libcore.os.bind(serverFd, sockAddr, 0);
+            Libcore.os.listen(serverFd, 1);
+
+            while (true) {
+                InetSocketAddress peer = new InetSocketAddress();
+                FileDescriptor peerFd = null;
+                try {
+                    if (DEBUG) Slog.v(TAG, "Waiting for debuggerd connection");
+                    peerFd = Libcore.os.accept(serverFd, peer);
+                    if (DEBUG) Slog.v(TAG, "Got debuggerd socket " + peerFd);
+                    if (peerFd != null) {
+                        // Only the superuser is allowed to talk to us over this socket
+                        StructUcred credentials =
+                                Libcore.os.getsockoptUcred(peerFd, SOL_SOCKET, SO_PEERCRED);
+                        if (credentials.uid == 0) {
+                            // the reporting thread may take responsibility for
+                            // acking the debugger; make sure we play along.
+                            consumeNativeCrashData(peerFd);
+                        }
+                    }
+                } catch (Exception e) {
+                    Slog.w(TAG, "Error handling connection", e);
+                } finally {
+                    // Always ack debuggerd's connection to us.  The actual
+                    // byte written is irrelevant.
+                    if (peerFd != null) {
+                        try {
+                            Libcore.os.write(peerFd, ackSignal, 0, 1);
+                        } catch (Exception e) { /* we don't care about failures here */ }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            Slog.e(TAG, "Unable to init native debug socket!", e);
+        }
+    }
+
+    static int unpackInt(byte[] buf, int offset) {
+        int b0, b1, b2, b3;
+
+        b0 = ((int) buf[offset]) & 0xFF; // mask against sign extension
+        b1 = ((int) buf[offset+1]) & 0xFF;
+        b2 = ((int) buf[offset+2]) & 0xFF;
+        b3 = ((int) buf[offset+3]) & 0xFF;
+        return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
+    }
+
+    static int readExactly(FileDescriptor fd, byte[] buffer, int offset, int numBytes)
+            throws ErrnoException {
+        int totalRead = 0;
+        while (numBytes > 0) {
+            int n = Libcore.os.read(fd, buffer, offset + totalRead, numBytes);
+            if (n <= 0) {
+                if (DEBUG) {
+                    Slog.w(TAG, "Needed " + numBytes + " but saw " + n);
+                }
+                return -1;  // premature EOF or timeout
+            }
+            numBytes -= n;
+            totalRead += n;
+        }
+        return totalRead;
+    }
+
+    // Read the crash report from the debuggerd connection
+    void consumeNativeCrashData(FileDescriptor fd) {
+        if (DEBUG) Slog.i(TAG, "debuggerd connected");
+        final byte[] buf = new byte[4096];
+        final ByteArrayOutputStream os = new ByteArrayOutputStream(4096);
+
+        try {
+            StructTimeval timeout = StructTimeval.fromMillis(SOCKET_TIMEOUT_MILLIS);
+            Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, timeout);
+            Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, timeout);
+
+            // first, the pid and signal number
+            int headerBytes = readExactly(fd, buf, 0, 8);
+            if (headerBytes != 8) {
+                // protocol failure; give up
+                Slog.e(TAG, "Unable to read from debuggerd");
+                return;
+            }
+
+            int pid = unpackInt(buf, 0);
+            int signal = unpackInt(buf, 4);
+            if (DEBUG) {
+                Slog.v(TAG, "Read pid=" + pid + " signal=" + signal);
+            }
+
+            // now the text of the dump
+            if (pid > 0) {
+                final ProcessRecord pr;
+                synchronized (mAm.mPidsSelfLocked) {
+                    pr = mAm.mPidsSelfLocked.get(pid);
+                }
+                if (pr != null) {
+                    int bytes;
+                    do {
+                        // get some data
+                        bytes = Libcore.os.read(fd, buf, 0, buf.length);
+                        if (bytes > 0) {
+                            if (DEBUG) {
+                                String s = new String(buf, 0, bytes, "UTF-8");
+                                Slog.v(TAG, "READ=" + bytes + "> " + s);
+                            }
+                            // did we just get the EOD null byte?
+                            if (buf[bytes-1] == 0) {
+                                os.write(buf, 0, bytes-1);  // exclude the EOD token
+                                break;
+                            }
+                            // no EOD, so collect it and read more
+                            os.write(buf, 0, bytes);
+                        }
+                    } while (bytes > 0);
+
+                    // Okay, we've got the report.
+                    if (DEBUG) Slog.v(TAG, "processing");
+
+                    // Mark the process record as being a native crash so that the
+                    // cleanup mechanism knows we're still submitting the report
+                    // even though the process will vanish as soon as we let
+                    // debuggerd proceed.
+                    synchronized (mAm) {
+                        pr.crashing = true;
+                        pr.forceCrashReport = true;
+                    }
+
+                    // Crash reporting is synchronous but we want to let debuggerd
+                    // go about it business right away, so we spin off the actual
+                    // reporting logic on a thread and let it take it's time.
+                    final String reportString = new String(os.toByteArray(), "UTF-8");
+                    (new NativeCrashReporter(pr, signal, reportString)).start();
+                } else {
+                    Slog.w(TAG, "Couldn't find ProcessRecord for pid " + pid);
+                }
+            } else {
+                Slog.e(TAG, "Bogus pid!");
+            }
+        } catch (Exception e) {
+            Slog.e(TAG, "Exception dealing with report", e);
+            // ugh, fail.
+        }
+    }
+
+}
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index a32af2f..7929f96 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -138,6 +138,7 @@
     boolean persistent;         // always keep this application running?
     boolean crashing;           // are we in the process of crashing?
     Dialog crashDialog;         // dialog being displayed due to crash.
+    boolean forceCrashReport;   // suppress normal auto-dismiss of crash dialog & report UI?
     boolean notResponding;      // does the app have a not responding dialog?
     Dialog anrDialog;           // dialog being displayed due to app not resp.
     boolean removed;            // has app package been removed from device?