Add flag to "am start" to enable native debugging

Specifying the new flag will enable several features in the runtime
required by the native debugger to debug Java and C++ code at the same
time.

The enabled features:
* Force JIT (never use the interpreter)
* Debug info generation
* Disbale some optimizations

Change-Id: Iaf5ab649715a0c274bd1b0fc64e483705da53cd0
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index ba93b2a..8187468 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -121,7 +121,7 @@
         PrintWriter pw = new PrintWriter(out);
         pw.println(
                 "usage: am [subcommand] [options]\n" +
-                "usage: am start [-D] [-W] [-P <FILE>] [--start-profiler <FILE>]\n" +
+                "usage: am start [-D] [-N] [-W] [-P <FILE>] [--start-profiler <FILE>]\n" +
                 "               [--sampling INTERVAL] [-R COUNT] [-S]\n" +
                 "               [--track-allocation] [--user <USER_ID> | current] <INTENT>\n" +
                 "       am startservice [--user <USER_ID> | current] <INTENT>\n" +
@@ -180,6 +180,7 @@
                 "\n" +
                 "am start: start an Activity.  Options are:\n" +
                 "    -D: enable debugging\n" +
+                "    -N: enable native debugging\n" +
                 "    -W: wait for launch to complete\n" +
                 "    --start-profiler <FILE>: start profiler and send results to <FILE>\n" +
                 "    --sampling INTERVAL: use sample profiling with INTERVAL microseconds\n" +
@@ -473,6 +474,8 @@
             public boolean handleOption(String opt, ShellCommand cmd) {
                 if (opt.equals("-D")) {
                     mStartFlags |= ActivityManager.START_FLAG_DEBUG;
+                } else if (opt.equals("-N")) {
+                    mStartFlags |= ActivityManager.START_FLAG_NATIVE_DEBUGGING;
                 } else if (opt.equals("-W")) {
                     mWaitOption = true;
                 } else if (opt.equals("-P")) {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 82170d1..dc37a57 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -245,6 +245,13 @@
     public static final int START_FLAG_TRACK_ALLOCATION = 1<<2;
 
     /**
+     * Flag for IActivityManaqer.startActivity: launch the app with
+     * native debugging support.
+     * @hide
+     */
+    public static final int START_FLAG_NATIVE_DEBUGGING = 1<<3;
+
+    /**
      * Result for IActivityManaqer.broadcastIntent: success!
      * @hide
      */
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 1e4ee4b..fe2dfdf 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -647,6 +647,12 @@
             if ((debugFlags & Zygote.DEBUG_GENERATE_DEBUG_INFO) != 0) {
                 argsForZygote.add("--generate-debug-info");
             }
+            if ((debugFlags & Zygote.DEBUG_ALWAYS_JIT) != 0) {
+                argsForZygote.add("--always-jit");
+            }
+            if ((debugFlags & Zygote.DEBUG_NATIVE_DEBUGGABLE) != 0) {
+                argsForZygote.add("--native-debuggable");
+            }
             if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
                 argsForZygote.add("--enable-assert");
             }
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index d23f26d..919254a 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -41,6 +41,10 @@
     public static final int DEBUG_ENABLE_JNI_LOGGING = 1 << 4;
     /** Force generation of native debugging information. */
     public static final int DEBUG_GENERATE_DEBUG_INFO = 1 << 5;
+    /** Always use JIT-ed code. */
+    public static final int DEBUG_ALWAYS_JIT = 1 << 6;
+    /** Make the code debuggable with turning off some optimizations. */
+    public static final int DEBUG_NATIVE_DEBUGGABLE = 1 << 7;
 
     /** No external storage should be mounted. */
     public static final int MOUNT_EXTERNAL_NONE = 0;
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index a40f9a8..85d84bb 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -434,6 +434,10 @@
                     debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
                 } else if (arg.equals("--generate-debug-info")) {
                     debugFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;
+                } else if (arg.equals("--always-jit")) {
+                    debugFlags |= Zygote.DEBUG_ALWAYS_JIT;
+                } else if (arg.equals("--native-debuggable")) {
+                    debugFlags |= Zygote.DEBUG_NATIVE_DEBUGGABLE;
                 } else if (arg.equals("--enable-jni-logging")) {
                     debugFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
                 } else if (arg.equals("--enable-assert")) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 978e69c..c55e713 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1299,6 +1299,7 @@
     int mMemWatchDumpPid;
     int mMemWatchDumpUid;
     String mTrackAllocationApp = null;
+    String mNativeDebuggingApp = null;
 
     final long[] mTmpLong = new long[2];
 
@@ -3468,6 +3469,13 @@
             if ("1".equals(SystemProperties.get("debug.assert"))) {
                 debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;
             }
+            if (mNativeDebuggingApp != null && mNativeDebuggingApp.equals(app.processName)) {
+                // Enable all debug flags required by the native debugger.
+                debugFlags |= Zygote.DEBUG_ALWAYS_JIT;          // Don't interpret anything
+                debugFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO; // Generate debug info
+                debugFlags |= Zygote.DEBUG_NATIVE_DEBUGGABLE;   // Disbale optimizations
+                mNativeDebuggingApp = null;
+            }
 
             String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;
             if (requiredAbi == null) {
@@ -11240,6 +11248,16 @@
         }
     }
 
+    void setNativeDebuggingAppLocked(ApplicationInfo app, String processName) {
+        boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
+        if (!isDebuggable) {
+            if ((app.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
+                throw new SecurityException("Process not debuggable: " + app.packageName);
+            }
+        }
+        mNativeDebuggingApp = processName;
+    }
+
     @Override
     public void setAlwaysFinish(boolean enabled) {
         enforceCallingPermission(android.Manifest.permission.SET_ALWAYS_FINISH,
@@ -13934,6 +13952,15 @@
                 pw.println("  mProfileType=" + mProfileType);
             }
         }
+        if (mNativeDebuggingApp != null) {
+            if (dumpPackage == null || dumpPackage.equals(mNativeDebuggingApp)) {
+                if (needSep) {
+                    pw.println();
+                    needSep = false;
+                }
+                pw.println("  mNativeDebuggingApp=" + mNativeDebuggingApp);
+            }
+        }
         if (dumpPackage == null) {
             if (mAlwaysFinishActivities || mController != null) {
                 pw.println("  mAlwaysFinishActivities=" + mAlwaysFinishActivities
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 31712d9..5f65b3d 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1018,6 +1018,10 @@
                     mService.setDebugApp(aInfo.processName, true, false);
                 }
 
+                if ((startFlags & ActivityManager.START_FLAG_NATIVE_DEBUGGING) != 0) {
+                    mService.setNativeDebuggingAppLocked(aInfo.applicationInfo, aInfo.processName);
+                }
+
                 if ((startFlags & ActivityManager.START_FLAG_TRACK_ALLOCATION) != 0) {
                     mService.setTrackAllocationApp(aInfo.applicationInfo, aInfo.processName);
                 }