Detect system-wide safe mode and configure the VM accordingly.

Use pthread_cond_wait instead of timed wait for the system_server process to
wait on the go-ahead JIT signal. If the phone is booted under system-wide safe
mode, such signal will never come so all VM instances (including the system
server) will run in the interpreter-only mode.

Bug: 2267590
diff --git a/libcore/dalvik/src/main/java/dalvik/system/VMRuntime.java b/libcore/dalvik/src/main/java/dalvik/system/VMRuntime.java
index 398e7fc..e05e489 100644
--- a/libcore/dalvik/src/main/java/dalvik/system/VMRuntime.java
+++ b/libcore/dalvik/src/main/java/dalvik/system/VMRuntime.java
@@ -195,4 +195,11 @@
      */
     public native void startJitCompilation();
 
+    /**
+     * Tells the VM to disable the JIT compiler. If the VM does not have a JIT
+     * implementation, calling this method should have no effect.
+     *
+     * {@hide}
+     */
+    public native void disableJitCompilation();
 }
diff --git a/libcore/dalvik/src/main/java/dalvik/system/Zygote.java b/libcore/dalvik/src/main/java/dalvik/system/Zygote.java
index 4f83d28..641e856 100644
--- a/libcore/dalvik/src/main/java/dalvik/system/Zygote.java
+++ b/libcore/dalvik/src/main/java/dalvik/system/Zygote.java
@@ -38,6 +38,14 @@
     /** disable the JIT compiler */
     public static final int DEBUG_ENABLE_SAFEMODE   = 1 << 3;
 
+    /**
+     * When set by the system server, all subsequent apps will be launched in
+     * VM safe mode.
+     *
+     * @hide
+     */
+    public static boolean systemInSafeMode = false;
+
     private Zygote() {}
 
     /**
diff --git a/vm/Globals.h b/vm/Globals.h
index 81bcefc..ddcd2db 100644
--- a/vm/Globals.h
+++ b/vm/Globals.h
@@ -794,6 +794,12 @@
     /* Framework or stand-alone? */
     bool runningInAndroidFramework;
 
+    /* Framework callback happened? */
+    bool alreadyEnabledViaFramework;
+
+    /* Framework requests to disable the JIT for good */
+    bool disableJit;
+
     /* Place arrays at the end to ease the display in gdb sessions */
 
     /* Work order queue for compilations */
diff --git a/vm/compiler/Compiler.c b/vm/compiler/Compiler.c
index 484f9a3..8297131 100644
--- a/vm/compiler/Compiler.c
+++ b/vm/compiler/Compiler.c
@@ -397,13 +397,41 @@
      * up code isn't worth compiling.  We'll resume when the framework
      * signals us that the first screen draw has happened, or the timer
      * below expires (to catch daemons).
+     *
+     * There is a theoretical race between the callback to
+     * VMRuntime.startJitCompiation and when the compiler thread reaches this
+     * point. In case the callback happens earlier, in order not to permanently
+     * hold the system_server (which is not using the timed wait) in
+     * interpreter-only mode we bypass the delay here.
      */
-    if (gDvmJit.runningInAndroidFramework) {
-        dvmLockMutex(&gDvmJit.compilerLock);
-        // TUNING: experiment with the delay & perhaps make it target-specific
-        dvmRelativeCondWait(&gDvmJit.compilerQueueActivity,
-                             &gDvmJit.compilerLock, 3000, 0);
-        dvmUnlockMutex(&gDvmJit.compilerLock);
+    if (gDvmJit.runningInAndroidFramework &&
+        !gDvmJit.alreadyEnabledViaFramework) {
+        /*
+         * If the current VM instance is the system server (detected by having
+         * 0 in gDvm.systemServerPid), we will use the indefinite wait on the
+         * conditional variable to determine whether to start the JIT or not.
+         * If the system server detects that the whole system is booted in
+         * safe mode, the conditional variable will never be signaled and the
+         * system server will remain in the interpreter-only mode. All
+         * subsequent apps will be started with the --enable-safemode flag
+         * explicitly appended.
+         */
+        if (gDvm.systemServerPid == 0) {
+            dvmLockMutex(&gDvmJit.compilerLock);
+            pthread_cond_wait(&gDvmJit.compilerQueueActivity,
+                              &gDvmJit.compilerLock);
+            dvmUnlockMutex(&gDvmJit.compilerLock);
+            LOGD("JIT started for system_server");
+        } else {
+            dvmLockMutex(&gDvmJit.compilerLock);
+            /*
+             * TUNING: experiment with the delay & perhaps make it
+             * target-specific
+             */
+            dvmRelativeCondWait(&gDvmJit.compilerQueueActivity,
+                                 &gDvmJit.compilerLock, 3000, 0);
+            dvmUnlockMutex(&gDvmJit.compilerLock);
+        }
         if (gDvmJit.haltCompilerThread) {
              return NULL;
         }
diff --git a/vm/native/dalvik_system_VMRuntime.c b/vm/native/dalvik_system_VMRuntime.c
index 5a22120..c020f8a 100644
--- a/vm/native/dalvik_system_VMRuntime.c
+++ b/vm/native/dalvik_system_VMRuntime.c
@@ -188,8 +188,10 @@
     JValue* pResult)
 {
 #if defined(WITH_JIT)
-    if (gDvm.executionMode == kExecutionModeJit) {
+    if (gDvm.executionMode == kExecutionModeJit &&
+        gDvmJit.disableJit == false) {
         dvmLockMutex(&gDvmJit.compilerLock);
+        gDvmJit.alreadyEnabledViaFramework = true;
         pthread_cond_signal(&gDvmJit.compilerQueueActivity);
         dvmUnlockMutex(&gDvmJit.compilerLock);
     }
@@ -197,6 +199,24 @@
     RETURN_VOID();
 }
 
+/*
+ * public native void disableJitCompilation()
+ *
+ * Callback function from the framework to indicate that a VM instance wants to
+ * permanently disable the JIT compiler. Currently only the system server uses
+ * this interface when it detects system-wide safe mode is enabled.
+ */
+static void Dalvik_dalvik_system_VMRuntime_disableJitCompilation(const u4* args,
+    JValue* pResult)
+{
+#if defined(WITH_JIT)
+    if (gDvm.executionMode == kExecutionModeJit) {
+        gDvmJit.disableJit = true;
+    }
+#endif
+    RETURN_VOID();
+}
+
 const DalvikNativeMethod dvm_dalvik_system_VMRuntime[] = {
     { "getTargetHeapUtilization", "()F",
         Dalvik_dalvik_system_VMRuntime_getTargetHeapUtilization },
@@ -216,5 +236,7 @@
         Dalvik_dalvik_system_VMRuntime_getExternalBytesAllocated },
     { "startJitCompilation", "()V",
         Dalvik_dalvik_system_VMRuntime_startJitCompilation },
+    { "disableJitCompilation", "()V",
+        Dalvik_dalvik_system_VMRuntime_disableJitCompilation },
     { NULL, NULL, NULL },
 };