Add support for dalvik.system.VMDebug functions am: b4095656e5
am: fa36837dd2

Change-Id: Ieab3b31bbe46fd68751cd72d9508268a2463303d
diff --git a/src/share/back/debugInit.c b/src/share/back/debugInit.c
index 474c197..043e639 100644
--- a/src/share/back/debugInit.c
+++ b/src/share/back/debugInit.c
@@ -39,6 +39,9 @@
 #include "invoker.h"
 #include "sys.h"
 
+// ANDROID-CHANGED: Allow us to initialize VMDebug apis.
+#include "vmDebug.h"
+
 /* How the options get to OnLoad: */
 #define XDEBUG "-Xdebug"
 #define XRUN "-Xrunjdwp"
@@ -776,6 +779,9 @@
     classTrack_initialize(env);
     debugLoop_initialize();
 
+    // ANDROID-CHANGED: Take over relevant VMDebug APIs.
+    vmDebug_initalize(env);
+
     initMonitor = debugMonitorCreate("JDWP Initialization Monitor");
 
 
diff --git a/src/share/back/debugLoop.c b/src/share/back/debugLoop.c
index 55d4894..765f836 100644
--- a/src/share/back/debugLoop.c
+++ b/src/share/back/debugLoop.c
@@ -23,6 +23,9 @@
  * questions.
  */
 
+// ANDROID-CHANGED: Include stdatomic so we can safely update and read the lastDebuggerActivity.
+#include <stdatomic.h>
+
 #include "util.h"
 #include "transport.h"
 #include "debugLoop.h"
@@ -48,6 +51,17 @@
 static jrawMonitorID vmDeathLock;
 static jboolean transportError;
 
+// ANDROID-CHANGED: The time the last debugger activity occurred or '0' if a debugger command is
+// currently in progress or the debugger has not started.
+static _Atomic(jlong) lastDebuggerActivity = ATOMIC_VAR_INIT(0LL);
+
+// ANDROID-CHANGED: Accessor for lastDebuggerActivity.
+jlong
+debugLoop_lastDebuggerActivity(void)
+{
+    return atomic_load(&lastDebuggerActivity);
+}
+
 static jboolean
 lastCommand(jdwpCmdPacket *cmd)
 {
@@ -64,6 +78,8 @@
 debugLoop_initialize(void)
 {
     vmDeathLock = debugMonitorCreate("JDWP VM_DEATH Lock");
+    // Android Changed: Set lastDebuggerActivity to 0 since we have not done anything yet.
+    atomic_store(&lastDebuggerActivity, 0LL);
 }
 
 void
@@ -133,6 +149,13 @@
              */
             debugMonitorEnter(vmDeathLock);
 
+            // ANDROID-CHANGED: Set lastDebuggerActivity to zero to notify that we are doing
+            // debugger activity. We only do this if the cmdSet is not DDMS for historical reasons.
+            jboolean is_ddms = (cmd->cmdSet == JDWP_COMMAND_SET(DDM));
+            if (!is_ddms) {
+                atomic_store(&lastDebuggerActivity, 0LL);
+            }
+
             /* Initialize the input and output streams */
             inStream_init(&in, p);
             outStream_initReply(&out, inStream_id(&in));
@@ -159,6 +182,13 @@
                 replyToSender = func(&in, &out);
             }
 
+            // ANDROID-CHANGED: Set lastDebuggerActivity to the current milli-time to notify VMDebug
+            // that we did something. We only do this if the cmdSet is not DDMS for historical
+            // reasons.
+            if (!is_ddms) {
+                atomic_store(&lastDebuggerActivity, milliTime());
+            }
+
             /* Reply to the sender */
             if (replyToSender) {
                 if (inStream_error(&in)) {
diff --git a/src/share/back/debugLoop.h b/src/share/back/debugLoop.h
index 47695b7..7e3db4a 100644
--- a/src/share/back/debugLoop.h
+++ b/src/share/back/debugLoop.h
@@ -30,4 +30,9 @@
 void debugLoop_run(void);
 void debugLoop_sync(void);
 
+// ANDROID-CHANGED: getter for last debugger activity time. Value is the CLOCK_MONOTONIC
+// millisecond time the last debugger action completed or 0 if a debugger action is ongoing.
+// A debugger action is any JDWP command packet (except for those in JDWP_COMMAND_SET(DDM)).
+jlong debugLoop_lastDebuggerActivity(void);
+
 #endif
diff --git a/src/share/back/util.c b/src/share/back/util.c
index f99f0b4..eece730 100644
--- a/src/share/back/util.c
+++ b/src/share/back/util.c
@@ -24,6 +24,8 @@
  */
 
 #include <ctype.h>
+// ANDROID-CHANGED: Include time.h so we can use clock_gettime to implement milliTime.
+#include <time.h>
 
 #include "util.h"
 #include "transport.h"
@@ -41,6 +43,17 @@
 static jboolean isArrayClass(jclass clazz);
 static char * getPropertyUTF8(JNIEnv *env, char *propertyName);
 
+// ANDROID-CHANGED: Implement a helper to get the current time in milliseconds according to
+// CLOCK_MONOTONIC.
+jlong
+milliTime(void)
+{
+  struct timespec now;
+  memset(&now, 0, sizeof(now));
+  (void)clock_gettime(CLOCK_MONOTONIC, &now);
+  return ((jlong)now.tv_sec) * 1000LL + ((jlong)now.tv_nsec) / 1000000LL;
+}
+
 /* Save an object reference for use later (create a NewGlobalRef) */
 void
 saveGlobalRef(JNIEnv *env, jobject obj, jobject *pobj)
diff --git a/src/share/back/util.h b/src/share/back/util.h
index 3b67bfa..68174fd 100644
--- a/src/share/back/util.h
+++ b/src/share/back/util.h
@@ -329,6 +329,9 @@
 jvmtiError classInstanceCounts(jint classCount, jclass *classes, jlong *counts);
 jvmtiError objectReferrers(jobject obj, ObjectBatch *referrers, int maxObjects);
 
+// ANDROID-CHANGED: Helper function to get current time in milliseconds on CLOCK_MONOTONIC
+jlong milliTime(void);
+
 /*
  * Command handling helpers shared among multiple command sets
  */
diff --git a/src/share/back/vmDebug.c b/src/share/back/vmDebug.c
new file mode 100644
index 0000000..7509620
--- /dev/null
+++ b/src/share/back/vmDebug.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "vmDebug.h"
+
+#include "JDWP.h"
+#include "debugLoop.h"
+#include "transport.h"
+#include "util.h"
+
+static jboolean JNICALL
+VMDebug_isDebuggerConnected(JNIEnv* env, jclass klass)
+{
+    return transport_is_open();
+}
+
+static jboolean JNICALL
+VMDebug_isDebuggingEnabled(JNIEnv* env, jclass klass)
+{
+    // We are running the debugger so debugging is definitely enabled.
+    return JNI_TRUE;
+}
+
+static jlong JNICALL
+VMDebug_lastDebuggerActivity(JNIEnv* env, jclass klass)
+{
+    if (!transport_is_open()) {
+        LOG_ERROR(("VMDebug.lastDebuggerActivity called without active debugger"));
+        return -1;
+    }
+    jlong last_time = debugLoop_lastDebuggerActivity();
+    if (last_time == 0) {
+        LOG_MISC(("debugger is performing an action"));
+        return 0;
+    }
+
+    jlong cur_time = milliTime();
+
+    if (cur_time < last_time) {
+        LOG_ERROR(("Time seemed to go backwards: last was %lld, current is %lld",
+                   last_time, cur_time));
+        return 0;
+    }
+    jlong res = cur_time - last_time;
+    LOG_MISC(("Debugger interval is %lld", res));
+    return res;
+}
+
+void
+vmDebug_initalize(JNIEnv* env)
+{
+    WITH_LOCAL_REFS(env, 1) {
+        jclass vmdebug_class = JNI_FUNC_PTR(env,FindClass)(env, "dalvik/system/VMDebug");
+        if (vmdebug_class == NULL) {
+            // The VMDebug class isn't available. We don't need to do anything.
+            LOG_MISC(("dalvik.system.VMDebug does not seem to be available on this runtime."));
+            // Get rid of the ClassNotFoundException.
+            JNI_FUNC_PTR(env,ExceptionClear)(env);
+            goto finish;
+        }
+
+        JNINativeMethod methods[3];
+
+        // Take over the implementation of these three functions.
+        methods[0].name = "lastDebuggerActivity";
+        methods[0].signature = "()J";
+        methods[0].fnPtr = (void*)VMDebug_lastDebuggerActivity;
+
+        methods[1].name = "isDebuggingEnabled";
+        methods[1].signature = "()Z";
+        methods[1].fnPtr = (void*)VMDebug_isDebuggingEnabled;
+
+        methods[2].name = "isDebuggerConnected";
+        methods[2].signature = "()Z";
+        methods[2].fnPtr = (void*)VMDebug_isDebuggerConnected;
+
+        jint res = JNI_FUNC_PTR(env,RegisterNatives)(env,
+                                                     vmdebug_class,
+                                                     methods,
+                                                     sizeof(methods) / sizeof(JNINativeMethod));
+        if (res != JNI_OK) {
+            EXIT_ERROR(JVMTI_ERROR_INTERNAL,
+                       "RegisterNatives returned failure for VMDebug class");
+        }
+
+        finish: ;
+    } END_WITH_LOCAL_REFS(env);
+}
+
diff --git a/src/share/back/vmDebug.h b/src/share/back/vmDebug.h
new file mode 100644
index 0000000..f14bed2
--- /dev/null
+++ b/src/share/back/vmDebug.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <jni.h>
+
+#ifndef JDWP_VMDEBUG_H
+#define JDWP_VMDEBUG_H
+
+void vmDebug_initalize(JNIEnv* env);
+
+#endif
+