Add support for selecting alternate JDWP implementations

Change JDWP options parsing to take place later and add a
-XjdwpProvider:_ option that can be used by the runtime to select an
appropriate JDWP provider. The argument is a string.

If 'none' is given JDWP will be totally disabled.

If 'internal' is given the current internal JDWP implementation is
used.

If 'default' is given the 'internal' JDWP implementation will
currently be used.

Other values will be added in the future.

Also adds a runtime callback that will be invoked when the runtime
wants to start or stop the debugger (namely at the post-zygote fork
and just before exit) and check if a debugger is availible.

Also add '-XjdwpOptions:_' in preparation for the eventual removal of
the existing -Xrunjdwp=_ and -Xagentlib:jdwp=_ as top-level options.
All of these options now store their arguments as a std::string to be
interpreted by the JDWP implementation as it sees fit. Also change the
jdwpOptions to default to transport=dt_android_adb if there is not one
specified and it is available. This will make changing the default
transport based on the JDWP provider easier.

These new options are needed to allow us to support both the old,
internal, JDWP implementation as its replacement is tested and
verified. This lets us switch between them with little difficulty.

We will probably remove one or both of these options once we have
confidence that the new jdwp implementation has stuck.

Test: ./test.py --host -j50
Test: ./test/run-test --host --debug 001-HelloWorld
Test: Manual, flash walleye, debug app

Bug: 62821960

Change-Id: Ie31db6b6f7d76a03d4ab8e178fcf298ed0eec203
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 6477347..1e5fe16 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -470,6 +470,7 @@
         "instrumentation.h",
         "indirect_reference_table.h",
         "invoke_type.h",
+        "jdwp_provider.h",
         "jdwp/jdwp.h",
         "jdwp/jdwp_constants.h",
         "lock_word.h",
@@ -602,6 +603,7 @@
         "intern_table_test.cc",
         "interpreter/safe_math_test.cc",
         "interpreter/unstarted_runtime_test.cc",
+        "jdwp/jdwp_options_test.cc",
         "java_vm_ext_test.cc",
         "jit/profile_compilation_info_test.cc",
         "leb128_test.cc",
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index b5ae09f..c85c233 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -328,6 +328,7 @@
 ObjectRegistry* Dbg::gRegistry = nullptr;
 DebuggerActiveMethodInspectionCallback Dbg::gDebugActiveCallback;
 DebuggerDdmCallback Dbg::gDebugDdmCallback;
+InternalDebuggerControlCallback Dbg::gDebuggerControlCallback;
 
 // Deoptimization support.
 std::vector<DeoptimizationRequest> Dbg::deoptimization_requests_;
@@ -364,6 +365,20 @@
   return !Dbg::MethodHasAnyBreakpoints(m);
 }
 
+void InternalDebuggerControlCallback::StartDebugger() {
+  // Release the mutator lock.
+  ScopedThreadStateChange stsc(art::Thread::Current(), kNative);
+  Dbg::StartJdwp();
+}
+
+void InternalDebuggerControlCallback::StopDebugger() {
+  Dbg::StopJdwp();
+}
+
+bool InternalDebuggerControlCallback::IsDebuggerConfigured() {
+  return Dbg::IsJdwpConfigured();
+}
+
 // Breakpoints.
 static std::vector<Breakpoint> gBreakpoints GUARDED_BY(Locks::breakpoint_lock_);
 
@@ -736,6 +751,7 @@
   CHECK_NE(jdwp_options.transport, JDWP::kJdwpTransportUnknown);
   gJdwpOptions = jdwp_options;
   gJdwpConfigured = true;
+  Runtime::Current()->GetRuntimeCallbacks()->AddDebuggerControlCallback(&gDebuggerControlCallback);
 }
 
 bool Dbg::IsJdwpConfigured() {
diff --git a/runtime/debugger.h b/runtime/debugger.h
index d5bad8d..7401813 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -53,18 +53,22 @@
 class StackVisitor;
 class Thread;
 
+struct DebuggerActiveMethodInspectionCallback : public MethodInspectionCallback {
+  bool IsMethodBeingInspected(ArtMethod* method) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
+  bool IsMethodSafeToJit(ArtMethod* method) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
+};
+
 struct DebuggerDdmCallback : public DdmCallback {
   void DdmPublishChunk(uint32_t type, const ArrayRef<const uint8_t>& data)
       OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
 };
 
-struct DebuggerActiveMethodInspectionCallback : public MethodInspectionCallback {
-  bool IsMethodBeingInspected(ArtMethod* m ATTRIBUTE_UNUSED)
-      OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
-  bool IsMethodSafeToJit(ArtMethod* m) OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_);
+struct InternalDebuggerControlCallback : public DebuggerControlCallback {
+  void StartDebugger() OVERRIDE;
+  void StopDebugger() OVERRIDE;
+  bool IsDebuggerConfigured() OVERRIDE;
 };
 
-
 /*
  * Invoke-during-breakpoint support.
  */
@@ -251,7 +255,8 @@
   }
 
   // Configures JDWP with parsed command-line options.
-  static void ConfigureJdwp(const JDWP::JdwpOptions& jdwp_options);
+  static void ConfigureJdwp(const JDWP::JdwpOptions& jdwp_options)
+      REQUIRES_SHARED(Locks::mutator_lock_);
 
   // Returns true if we had -Xrunjdwp or -agentlib:jdwp= on the command line.
   static bool IsJdwpConfigured();
@@ -789,6 +794,7 @@
 
   static DebuggerActiveMethodInspectionCallback gDebugActiveCallback;
   static DebuggerDdmCallback gDebugDdmCallback;
+  static InternalDebuggerControlCallback gDebuggerControlCallback;
 
   // Indicates whether we should drop the JDWP connection because the runtime stops or the
   // debugger called VirtualMachine.Dispose.
diff --git a/runtime/jdwp/jdwp.h b/runtime/jdwp/jdwp.h
index d712b10..b491c3e 100644
--- a/runtime/jdwp/jdwp.h
+++ b/runtime/jdwp/jdwp.h
@@ -98,14 +98,15 @@
  * How we talk to the debugger.
  */
 enum JdwpTransportType {
-  kJdwpTransportUnknown = 0,
+  kJdwpTransportNone = 0,
+  kJdwpTransportUnknown,      // Unknown tranpsort
   kJdwpTransportSocket,       // transport=dt_socket
   kJdwpTransportAndroidAdb,   // transport=dt_android_adb
 };
 std::ostream& operator<<(std::ostream& os, const JdwpTransportType& rhs);
 
 struct JdwpOptions {
-  JdwpTransportType transport = kJdwpTransportUnknown;
+  JdwpTransportType transport = kJdwpTransportNone;
   bool server = false;
   bool suspend = false;
   std::string host = "";
@@ -114,6 +115,8 @@
 
 bool operator==(const JdwpOptions& lhs, const JdwpOptions& rhs);
 
+bool ParseJdwpOptions(const std::string& options, JdwpOptions* jdwp_options);
+
 struct JdwpEvent;
 class JdwpNetStateBase;
 struct ModBasket;
diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc
index e275554..63f5dc8 100644
--- a/runtime/jdwp/jdwp_main.cc
+++ b/runtime/jdwp/jdwp_main.cc
@@ -37,6 +37,119 @@
 
 static void* StartJdwpThread(void* arg);
 
+
+static bool ParseJdwpOption(const std::string& name,
+                            const std::string& value,
+                            JdwpOptions* jdwp_options) {
+  if (name == "transport") {
+    if (value == "dt_socket") {
+      jdwp_options->transport = JDWP::kJdwpTransportSocket;
+    } else if (value == "dt_android_adb") {
+      jdwp_options->transport = JDWP::kJdwpTransportAndroidAdb;
+    } else {
+      jdwp_options->transport = JDWP::kJdwpTransportUnknown;
+      LOG(ERROR) << "JDWP transport not supported: " << value;
+      return false;
+    }
+  } else if (name == "server") {
+    if (value == "n") {
+      jdwp_options->server = false;
+    } else if (value == "y") {
+      jdwp_options->server = true;
+    } else {
+      LOG(ERROR) << "JDWP option 'server' must be 'y' or 'n'";
+      return false;
+    }
+  } else if (name == "suspend") {
+    if (value == "n") {
+      jdwp_options->suspend = false;
+    } else if (value == "y") {
+      jdwp_options->suspend = true;
+    } else {
+      LOG(ERROR) << "JDWP option 'suspend' must be 'y' or 'n'";
+      return false;
+    }
+  } else if (name == "address") {
+    /* this is either <port> or <host>:<port> */
+    std::string port_string;
+    jdwp_options->host.clear();
+    std::string::size_type colon = value.find(':');
+    if (colon != std::string::npos) {
+      jdwp_options->host = value.substr(0, colon);
+      port_string = value.substr(colon + 1);
+    } else {
+      port_string = value;
+    }
+    if (port_string.empty()) {
+      LOG(ERROR) << "JDWP address missing port: " << value;
+      return false;
+    }
+    char* end;
+    uint64_t port = strtoul(port_string.c_str(), &end, 10);
+    if (*end != '\0' || port > 0xffff) {
+      LOG(ERROR) << "JDWP address has junk in port field: " << value;
+      return false;
+    }
+    jdwp_options->port = port;
+  } else if (name == "launch" || name == "onthrow" || name == "oncaught" || name == "timeout") {
+    /* valid but unsupported */
+    LOG(INFO) << "Ignoring JDWP option '" << name << "'='" << value << "'";
+  } else {
+    LOG(INFO) << "Ignoring unrecognized JDWP option '" << name << "'='" << value << "'";
+  }
+
+  return true;
+}
+
+bool ParseJdwpOptions(const std::string& options, JdwpOptions* jdwp_options) {
+  VLOG(jdwp) << "ParseJdwpOptions: " << options;
+
+  if (options == "help") {
+    LOG(ERROR) << "Example: -XjdwpOptions:transport=dt_socket,address=8000,server=y\n"
+               << "Example: -Xrunjdwp:transport=dt_socket,address=8000,server=y\n"
+               << "Example: -Xrunjdwp:transport=dt_socket,address=localhost:6500,server=n\n";
+    return false;
+  }
+
+  const std::string s;
+
+  std::vector<std::string> pairs;
+  Split(options, ',', &pairs);
+
+  for (const std::string& jdwp_option : pairs) {
+    std::string::size_type equals_pos = jdwp_option.find('=');
+    if (equals_pos == std::string::npos) {
+      LOG(ERROR) << s << "Can't parse JDWP option '" << jdwp_option << "' in '" << options << "'";
+      return false;
+    }
+
+    bool parse_attempt = ParseJdwpOption(jdwp_option.substr(0, equals_pos),
+                                         jdwp_option.substr(equals_pos + 1),
+                                         jdwp_options);
+    if (!parse_attempt) {
+      // We fail to parse this JDWP option.
+      return parse_attempt;
+    }
+  }
+
+  if (jdwp_options->transport == JDWP::kJdwpTransportUnknown) {
+    LOG(ERROR) << s << "Must specify JDWP transport: " << options;
+    return false;
+  }
+#if ART_TARGET_ANDROID
+  if (jdwp_options->transport == JDWP::kJdwpTransportNone) {
+    jdwp_options->transport = JDWP::kJdwpTransportAndroidAdb;
+    LOG(WARNING) << "no JDWP transport specified. Defaulting to dt_android_adb";
+  }
+#endif
+  if (!jdwp_options->server && (jdwp_options->host.empty() || jdwp_options->port == 0)) {
+    LOG(ERROR) << s << "Must specify JDWP host and port when server=n: " << options;
+    return false;
+  }
+
+  return true;
+}
+
 /*
  * JdwpNetStateBase class implementation
  */
diff --git a/runtime/jdwp/jdwp_options_test.cc b/runtime/jdwp/jdwp_options_test.cc
new file mode 100644
index 0000000..10c52e8
--- /dev/null
+++ b/runtime/jdwp/jdwp_options_test.cc
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+#include "jdwp.h"
+
+#include "gtest/gtest.h"
+
+namespace art {
+namespace JDWP {
+
+TEST(JdwpOptionsTest, Options) {
+  {
+    /*
+     * "Example: -Xrunjdwp:transport=dt_socket,address=8000,server=y\n"
+     */
+    JDWP::JdwpOptions opt = JDWP::JdwpOptions();
+    const char *opt_args = "transport=dt_socket,address=8000,server=y";
+
+    EXPECT_TRUE(ParseJdwpOptions(opt_args, &opt));
+    EXPECT_EQ(opt.transport, JdwpTransportType::kJdwpTransportSocket);
+    EXPECT_EQ(opt.port, 8000u);
+    EXPECT_EQ(opt.server, true);
+    EXPECT_EQ(opt.suspend, false);
+  }
+
+  {
+    /*
+     * Example: transport=dt_socket,address=localhost:6500,server=n
+     */
+    JDWP::JdwpOptions opt = JDWP::JdwpOptions();
+    const char *opt_args = "transport=dt_socket,address=localhost:6500,server=y";
+
+    EXPECT_TRUE(ParseJdwpOptions(opt_args, &opt));
+    EXPECT_EQ(opt.transport, JdwpTransportType::kJdwpTransportSocket);
+    EXPECT_EQ(opt.port, 6500u);
+    EXPECT_EQ(opt.host, "localhost");
+    EXPECT_EQ(opt.server, true);
+    EXPECT_EQ(opt.suspend, false);
+  }
+
+  {
+    /*
+     * Example: transport=dt_android_adb,server=n,suspend=y;
+     */
+    JDWP::JdwpOptions opt = JDWP::JdwpOptions();
+    const char *opt_args = "transport=dt_android_adb,server=y";
+
+    EXPECT_TRUE(ParseJdwpOptions(opt_args, &opt));
+    EXPECT_EQ(opt.transport, JdwpTransportType::kJdwpTransportAndroidAdb);
+    EXPECT_EQ(opt.port, 0xFFFF);
+    EXPECT_EQ(opt.host, "");
+    EXPECT_EQ(opt.server, true);
+    EXPECT_EQ(opt.suspend, false);
+  }
+
+  /*
+   * Test failures
+  */
+  JDWP::JdwpOptions opt = JDWP::JdwpOptions();
+  EXPECT_FALSE(ParseJdwpOptions("help", &opt));
+  EXPECT_FALSE(ParseJdwpOptions("blabla", &opt));
+  EXPECT_FALSE(ParseJdwpOptions("transport=dt_android_adb,server=n", &opt));
+}
+
+}  // namespace JDWP
+}  // namespace art
diff --git a/runtime/jdwp_provider.h b/runtime/jdwp_provider.h
new file mode 100644
index 0000000..849ba21
--- /dev/null
+++ b/runtime/jdwp_provider.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2017 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.
+ */
+
+#ifndef ART_RUNTIME_JDWP_PROVIDER_H_
+#define ART_RUNTIME_JDWP_PROVIDER_H_
+
+#include <ios>
+
+#include "base/macros.h"
+#include "base/logging.h"
+
+namespace art {
+
+enum class JdwpProvider {
+  kNone,
+  kInternal,
+};
+
+std::ostream& operator<<(std::ostream& os, const JdwpProvider& rhs);
+
+}  // namespace art
+#endif  // ART_RUNTIME_JDWP_PROVIDER_H_
diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc
index 88a78ab..787646d 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -157,8 +157,9 @@
   return Dbg::IsDebuggerActive();
 }
 
-static jboolean VMDebug_isDebuggingEnabled(JNIEnv*, jclass) {
-  return Dbg::IsJdwpConfigured();
+static jboolean VMDebug_isDebuggingEnabled(JNIEnv* env, jclass) {
+  ScopedObjectAccess soa(env);
+  return Runtime::Current()->GetRuntimeCallbacks()->IsDebuggerConfigured();
 }
 
 static jlong VMDebug_lastDebuggerActivity(JNIEnv*, jclass) {
diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc
index a3c0036..47309ed 100644
--- a/runtime/parsed_options.cc
+++ b/runtime/parsed_options.cc
@@ -92,8 +92,11 @@
           .IntoKey(M::CheckJni)
       .Define("-Xjniopts:forcecopy")
           .IntoKey(M::JniOptsForceCopy)
-      .Define({"-Xrunjdwp:_", "-agentlib:jdwp=_"})
-          .WithType<JDWP::JdwpOptions>()
+      .Define("-XjdwpProvider:_")
+          .WithType<JdwpProvider>()
+          .IntoKey(M::JdwpProvider)
+      .Define({"-Xrunjdwp:_", "-agentlib:jdwp=_", "-XjdwpOptions:_"})
+          .WithType<std::string>()
           .IntoKey(M::JdwpOptions)
       // TODO Re-enable -agentlib: once I have a good way to transform the values.
       // .Define("-agentlib:_")
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 1cdeb7c..9c527e7f 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -355,7 +355,7 @@
   }
 
   // Make sure our internal threads are dead before we start tearing down things they're using.
-  Dbg::StopJdwp();
+  GetRuntimeCallbacks()->StopDebugger();
   delete signal_catcher_;
 
   // Make sure all other non-daemon threads have terminated, and all daemon threads are suspended.
@@ -871,8 +871,10 @@
   StartSignalCatcher();
 
   // Start the JDWP thread. If the command-line debugger flags specified "suspend=y",
-  // this will pause the runtime, so we probably want this to come last.
-  Dbg::StartJdwp();
+  // this will pause the runtime (in the internal debugger implementation), so we probably want
+  // this to come last.
+  ScopedObjectAccess soa(Thread::Current());
+  GetRuntimeCallbacks()->StartDebugger();
 }
 
 void Runtime::StartSignalCatcher() {
@@ -1221,8 +1223,24 @@
 
   dump_gc_performance_on_shutdown_ = runtime_options.Exists(Opt::DumpGCPerformanceOnShutdown);
 
-  if (runtime_options.Exists(Opt::JdwpOptions)) {
-    Dbg::ConfigureJdwp(runtime_options.GetOrDefault(Opt::JdwpOptions));
+  jdwp_options_ = runtime_options.GetOrDefault(Opt::JdwpOptions);
+  jdwp_provider_ = runtime_options.GetOrDefault(Opt::JdwpProvider);
+  switch (jdwp_provider_) {
+    case JdwpProvider::kNone: {
+      LOG(WARNING) << "Disabling all JDWP support.";
+      break;
+    }
+    case JdwpProvider::kInternal: {
+      if (runtime_options.Exists(Opt::JdwpOptions)) {
+        JDWP::JdwpOptions ops;
+        if (!JDWP::ParseJdwpOptions(runtime_options.GetOrDefault(Opt::JdwpOptions), &ops)) {
+          LOG(ERROR) << "failed to parse jdwp options!";
+          return false;
+        }
+        Dbg::ConfigureJdwp(ops);
+      }
+      break;
+    }
   }
   callbacks_->AddThreadLifecycleCallback(Dbg::GetThreadLifecycleCallback());
   callbacks_->AddClassLoadCallback(Dbg::GetClassLoadCallback());
@@ -1509,6 +1527,7 @@
   }
 
   // Is the process debuggable? Otherwise, do not attempt to load the plugin.
+  // TODO Support a crimped jvmti for non-debuggable runtimes.
   if (!runtime->IsJavaDebuggable()) {
     *error_msg = "Process is not debuggable.";
     return false;
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 476b71f..89caac4 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -35,6 +35,7 @@
 #include "experimental_flags.h"
 #include "gc_root.h"
 #include "instrumentation.h"
+#include "jdwp_provider.h"
 #include "obj_ptr.h"
 #include "offsets.h"
 #include "process_state.h"
@@ -696,6 +697,14 @@
     return madvise_random_access_;
   }
 
+  const std::string& GetJdwpOptions() {
+    return jdwp_options_;
+  }
+
+  JdwpProvider GetJdwpProvider() const {
+    return jdwp_provider_;
+  }
+
  private:
   static void InitPlatformSignalHandlers();
 
@@ -953,6 +962,12 @@
   // Whether zygote code is in a section that should not start threads.
   bool zygote_no_threads_;
 
+  // The string containing requested jdwp options
+  std::string jdwp_options_;
+
+  // The jdwp provider we were configured with.
+  JdwpProvider jdwp_provider_;
+
   // Saved environment.
   class EnvSnapshot {
    public:
diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc
index 40d7889..cd3c0b7 100644
--- a/runtime/runtime_callbacks.cc
+++ b/runtime/runtime_callbacks.cc
@@ -49,6 +49,35 @@
   }
 }
 
+void RuntimeCallbacks::AddDebuggerControlCallback(DebuggerControlCallback* cb) {
+  debugger_control_callbacks_.push_back(cb);
+}
+
+void RuntimeCallbacks::RemoveDebuggerControlCallback(DebuggerControlCallback* cb) {
+  Remove(cb, &debugger_control_callbacks_);
+}
+
+bool RuntimeCallbacks::IsDebuggerConfigured() {
+  for (DebuggerControlCallback* cb : debugger_control_callbacks_) {
+    if (cb->IsDebuggerConfigured()) {
+      return true;
+    }
+  }
+  return false;
+}
+
+void RuntimeCallbacks::StartDebugger() {
+  for (DebuggerControlCallback* cb : debugger_control_callbacks_) {
+    cb->StartDebugger();
+  }
+}
+
+void RuntimeCallbacks::StopDebugger() {
+  for (DebuggerControlCallback* cb : debugger_control_callbacks_) {
+    cb->StopDebugger();
+  }
+}
+
 void RuntimeCallbacks::AddMethodInspectionCallback(MethodInspectionCallback* cb) {
   method_inspection_callbacks_.push_back(cb);
 }
diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h
index baf941a..f405c9f 100644
--- a/runtime/runtime_callbacks.h
+++ b/runtime/runtime_callbacks.h
@@ -62,6 +62,19 @@
       REQUIRES_SHARED(Locks::mutator_lock_) = 0;
 };
 
+class DebuggerControlCallback {
+ public:
+  virtual ~DebuggerControlCallback() {}
+
+  // Begin running the debugger.
+  virtual void StartDebugger() = 0;
+  // The debugger should begin shutting down since the runtime is ending. This is just advisory
+  virtual void StopDebugger() = 0;
+
+  // This allows the debugger to tell the runtime if it is configured.
+  virtual bool IsDebuggerConfigured() = 0;
+};
+
 class RuntimeSigQuitCallback {
  public:
   virtual ~RuntimeSigQuitCallback() {}
@@ -197,6 +210,17 @@
   void AddDdmCallback(DdmCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
   void RemoveDdmCallback(DdmCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
 
+  void StartDebugger() REQUIRES_SHARED(Locks::mutator_lock_);
+  // NO_THREAD_SAFETY_ANALYSIS since this is only called when we are in the middle of shutting down
+  // and the mutator_lock_ is no longer acquirable.
+  void StopDebugger() NO_THREAD_SAFETY_ANALYSIS;
+  bool IsDebuggerConfigured() REQUIRES_SHARED(Locks::mutator_lock_);
+
+  void AddDebuggerControlCallback(DebuggerControlCallback* cb)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+  void RemoveDebuggerControlCallback(DebuggerControlCallback* cb)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
  private:
   std::vector<ThreadLifecycleCallback*> thread_callbacks_
       GUARDED_BY(Locks::mutator_lock_);
@@ -214,6 +238,8 @@
       GUARDED_BY(Locks::mutator_lock_);
   std::vector<DdmCallback*> ddm_callbacks_
       GUARDED_BY(Locks::mutator_lock_);
+  std::vector<DebuggerControlCallback*> debugger_control_callbacks_
+      GUARDED_BY(Locks::mutator_lock_);
 };
 
 }  // namespace art
diff --git a/runtime/runtime_options.def b/runtime/runtime_options.def
index 2e03562..4bc8245 100644
--- a/runtime/runtime_options.def
+++ b/runtime/runtime_options.def
@@ -43,7 +43,8 @@
 RUNTIME_OPTIONS_KEY (std::string,         Image)
 RUNTIME_OPTIONS_KEY (Unit,                CheckJni)
 RUNTIME_OPTIONS_KEY (Unit,                JniOptsForceCopy)
-RUNTIME_OPTIONS_KEY (JDWP::JdwpOptions,   JdwpOptions)
+RUNTIME_OPTIONS_KEY (std::string,         JdwpOptions, "")
+RUNTIME_OPTIONS_KEY (JdwpProvider,        JdwpProvider, JdwpProvider::kInternal)
 RUNTIME_OPTIONS_KEY (MemoryKiB,           MemoryMaximumSize,              gc::Heap::kDefaultMaximumSize)  // -Xmx
 RUNTIME_OPTIONS_KEY (MemoryKiB,           MemoryInitialSize,              gc::Heap::kDefaultInitialSize)  // -Xms
 RUNTIME_OPTIONS_KEY (MemoryKiB,           HeapGrowthLimit)                // Default is 0 for unlimited