Add support for JVMTI monitor events.

Adds support for the JVMTI can_generate_monitor_events capability and
all associated events. This adds support for the
JVMTI_EVENT_MONITOR_WAIT, JVMTI_EVENT_MONITOR_WAITED,
JVMTI_EVENT_MONITOR_CONTENDED_ENTER, and
JVMTI_EVENT_MONITOR_CONTENDED_ENTERED events.

Bug: 65558434
Bug: 62821960
Bug: 34415266

Test: ./test.py --host -j50

Change-Id: I0fe8038e6c4249e77d37a67e5056b5d2a94b6f48
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index af56810..2868180 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -2212,6 +2212,8 @@
     case kTerminated:
       return JDWP::TS_ZOMBIE;
     case kTimedWaiting:
+    case kWaitingForTaskProcessor:
+    case kWaitingForLockInflation:
     case kWaitingForCheckPointsToRun:
     case kWaitingForDebuggerSend:
     case kWaitingForDebuggerSuspension:
diff --git a/runtime/entrypoints/quick/quick_lock_entrypoints.cc b/runtime/entrypoints/quick/quick_lock_entrypoints.cc
index b4f945a..4bcce21 100644
--- a/runtime/entrypoints/quick/quick_lock_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_lock_entrypoints.cc
@@ -29,15 +29,22 @@
     ThrowNullPointerException("Null reference used for synchronization (monitor-enter)");
     return -1;  // Failure.
   } else {
-    if (kIsDebugBuild) {
-      obj = obj->MonitorEnter(self);  // May block
-      CHECK(self->HoldsLock(obj));
-      CHECK(!self->IsExceptionPending());
+    obj = obj->MonitorEnter(self);  // May block
+    DCHECK(self->HoldsLock(obj));
+    // Exceptions can be thrown by monitor event listeners. This is expected to be rare however.
+    if (UNLIKELY(self->IsExceptionPending())) {
+      // TODO Remove this DCHECK if we expand the use of monitor callbacks.
+      DCHECK(Runtime::Current()->HasLoadedPlugins())
+          << "Exceptions are only expected to be thrown by plugin code which doesn't seem to be "
+          << "loaded.";
+      // We need to get rid of the lock
+      bool unlocked = obj->MonitorExit(self);
+      DCHECK(unlocked);
+      return -1;  // Failure.
     } else {
-      obj->MonitorEnter(self);  // May block
+      DCHECK(self->HoldsLock(obj));
+      return 0;  // Success.
     }
-    return 0;  // Success.
-    // Only possible exception is NPE and is handled before entry
   }
 }
 
diff --git a/runtime/gc/task_processor.cc b/runtime/gc/task_processor.cc
index 0704a68..e928644 100644
--- a/runtime/gc/task_processor.cc
+++ b/runtime/gc/task_processor.cc
@@ -34,14 +34,14 @@
 }
 
 void TaskProcessor::AddTask(Thread* self, HeapTask* task) {
-  ScopedThreadStateChange tsc(self, kBlocked);
+  ScopedThreadStateChange tsc(self, kWaitingForTaskProcessor);
   MutexLock mu(self, *lock_);
   tasks_.insert(task);
   cond_->Signal(self);
 }
 
 HeapTask* TaskProcessor::GetTask(Thread* self) {
-  ScopedThreadStateChange tsc(self, kBlocked);
+  ScopedThreadStateChange tsc(self, kWaitingForTaskProcessor);
   MutexLock mu(self, *lock_);
   while (true) {
     if (tasks_.empty()) {
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index 3ccab85..50bd7e7 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -65,9 +65,16 @@
 static inline void DoMonitorEnter(Thread* self, ShadowFrame* frame, ObjPtr<mirror::Object> ref)
     NO_THREAD_SAFETY_ANALYSIS
     REQUIRES(!Roles::uninterruptible_) {
+  DCHECK(!ref.IsNull());
   StackHandleScope<1> hs(self);
   Handle<mirror::Object> h_ref(hs.NewHandle(ref));
   h_ref->MonitorEnter(self);
+  DCHECK(self->HoldsLock(h_ref.Get()));
+  if (UNLIKELY(self->IsExceptionPending())) {
+    bool unlocked = h_ref->MonitorExit(self);
+    DCHECK(unlocked);
+    return;
+  }
   if (kMonitorCounting && frame->GetMethod()->MustCountLocks()) {
     frame->GetLockCountData().AddMonitor(self, h_ref.Get());
   }
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index f96792d..d74cec3 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -2398,10 +2398,12 @@
     ScopedObjectAccess soa(env);
     ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(java_object);
     o = o->MonitorEnter(soa.Self());
+    if (soa.Self()->HoldsLock(o)) {
+      soa.Env()->monitors.Add(o);
+    }
     if (soa.Self()->IsExceptionPending()) {
       return JNI_ERR;
     }
-    soa.Env()->monitors.Add(o);
     return JNI_OK;
   }
 
@@ -2409,11 +2411,14 @@
     CHECK_NON_NULL_ARGUMENT_RETURN(java_object, JNI_ERR);
     ScopedObjectAccess soa(env);
     ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(java_object);
+    bool remove_mon = soa.Self()->HoldsLock(o);
     o->MonitorExit(soa.Self());
+    if (remove_mon) {
+      soa.Env()->monitors.Remove(o);
+    }
     if (soa.Self()->IsExceptionPending()) {
       return JNI_ERR;
     }
-    soa.Env()->monitors.Remove(o);
     return JNI_OK;
   }
 
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index 80e6ad3..051e015 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -346,11 +346,31 @@
   return TryLockLocked(self);
 }
 
+// Asserts that a mutex isn't held when the class comes into and out of scope.
+class ScopedAssertNotHeld {
+ public:
+  ScopedAssertNotHeld(Thread* self, Mutex& mu) : self_(self), mu_(mu) {
+    mu_.AssertNotHeld(self_);
+  }
+
+  ~ScopedAssertNotHeld() {
+    mu_.AssertNotHeld(self_);
+  }
+
+ private:
+  Thread* const self_;
+  Mutex& mu_;
+  DISALLOW_COPY_AND_ASSIGN(ScopedAssertNotHeld);
+};
+
+template <LockReason reason>
 void Monitor::Lock(Thread* self) {
-  MutexLock mu(self, monitor_lock_);
+  ScopedAssertNotHeld sanh(self, monitor_lock_);
+  bool called_monitors_callback = false;
+  monitor_lock_.Lock(self);
   while (true) {
     if (TryLockLocked(self)) {
-      return;
+      break;
     }
     // Contended.
     const bool log_contention = (lock_profiling_threshold_ != 0);
@@ -389,6 +409,12 @@
     }
 
     monitor_lock_.Unlock(self);  // Let go of locks in order.
+    // Call the contended locking cb once and only once. Also only call it if we are locking for
+    // the first time, not during a Wait wakeup.
+    if (reason == LockReason::kForLock && !called_monitors_callback) {
+      called_monitors_callback = true;
+      Runtime::Current()->GetRuntimeCallbacks()->MonitorContendedLocking(this);
+    }
     self->SetMonitorEnterObject(GetObject());
     {
       ScopedThreadSuspension tsc(self, kBlocked);  // Change to blocked and give up mutator_lock_.
@@ -492,10 +518,10 @@
                     << PrettyDuration(MsToNs(wait_ms));
               }
               LogContentionEvent(self,
-                                 wait_ms,
-                                 sample_percent,
-                                 owners_method,
-                                 owners_dex_pc);
+                                wait_ms,
+                                sample_percent,
+                                owners_method,
+                                owners_dex_pc);
             }
           }
         }
@@ -508,8 +534,18 @@
     monitor_lock_.Lock(self);  // Reacquire locks in order.
     --num_waiters_;
   }
+  monitor_lock_.Unlock(self);
+  // We need to pair this with a single contended locking call. NB we match the RI behavior and call
+  // this even if MonitorEnter failed.
+  if (called_monitors_callback) {
+    CHECK(reason == LockReason::kForLock);
+    Runtime::Current()->GetRuntimeCallbacks()->MonitorContendedLocked(this);
+  }
 }
 
+template void Monitor::Lock<LockReason::kForLock>(Thread* self);
+template void Monitor::Lock<LockReason::kForWait>(Thread* self);
+
 static void ThrowIllegalMonitorStateExceptionF(const char* fmt, ...)
                                               __attribute__((format(printf, 1, 2)));
 
@@ -690,6 +726,7 @@
   AtraceMonitorLock(self, GetObject(), true /* is_wait */);
 
   bool was_interrupted = false;
+  bool timed_out = false;
   {
     // Update thread state. If the GC wakes up, it'll ignore us, knowing
     // that we won't touch any references in this state, and we'll check
@@ -718,7 +755,7 @@
         self->GetWaitConditionVariable()->Wait(self);
       } else {
         DCHECK(why == kTimedWaiting || why == kSleeping) << why;
-        self->GetWaitConditionVariable()->TimedWait(self, ms, ns);
+        timed_out = self->GetWaitConditionVariable()->TimedWait(self, ms, ns);
       }
       was_interrupted = self->IsInterrupted();
     }
@@ -751,8 +788,11 @@
 
   AtraceMonitorUnlock();  // End Wait().
 
+  // We just slept, tell the runtime callbacks about this.
+  Runtime::Current()->GetRuntimeCallbacks()->MonitorWaitFinished(this, timed_out);
+
   // Re-acquire the monitor and lock.
-  Lock(self);
+  Lock<LockReason::kForWait>(self);
   monitor_lock_.Lock(self);
   self->GetWaitMutex()->AssertNotHeld(self);
 
@@ -897,7 +937,7 @@
     bool timed_out;
     Thread* owner;
     {
-      ScopedThreadSuspension sts(self, kBlocked);
+      ScopedThreadSuspension sts(self, kWaitingForLockInflation);
       owner = thread_list->SuspendThreadByThreadId(owner_thread_id,
                                                    SuspendReason::kInternal,
                                                    &timed_out);
@@ -989,10 +1029,10 @@
           contention_count++;
           Runtime* runtime = Runtime::Current();
           if (contention_count <= runtime->GetMaxSpinsBeforeThinLockInflation()) {
-            // TODO: Consider switching the thread state to kBlocked when we are yielding.
-            // Use sched_yield instead of NanoSleep since NanoSleep can wait much longer than the
-            // parameter you pass in. This can cause thread suspension to take excessively long
-            // and make long pauses. See b/16307460.
+            // TODO: Consider switching the thread state to kWaitingForLockInflation when we are
+            // yielding.  Use sched_yield instead of NanoSleep since NanoSleep can wait much longer
+            // than the parameter you pass in. This can cause thread suspension to take excessively
+            // long and make long pauses. See b/16307460.
             // TODO: We should literally spin first, without sched_yield. Sched_yield either does
             // nothing (at significant expense), or guarantees that we wait at least microseconds.
             // If the owner is running, I would expect the median lock hold time to be hundreds
@@ -1098,7 +1138,16 @@
                    bool interruptShouldThrow, ThreadState why) {
   DCHECK(self != nullptr);
   DCHECK(obj != nullptr);
-  LockWord lock_word = obj->GetLockWord(true);
+  StackHandleScope<1> hs(self);
+  Handle<mirror::Object> h_obj(hs.NewHandle(obj));
+
+  Runtime::Current()->GetRuntimeCallbacks()->ObjectWaitStart(h_obj, ms);
+  if (UNLIKELY(self->IsExceptionPending())) {
+    // See b/65558434 for information on handling of exceptions here.
+    return;
+  }
+
+  LockWord lock_word = h_obj->GetLockWord(true);
   while (lock_word.GetState() != LockWord::kFatLocked) {
     switch (lock_word.GetState()) {
       case LockWord::kHashCode:
@@ -1115,8 +1164,8 @@
         } else {
           // We own the lock, inflate to enqueue ourself on the Monitor. May fail spuriously so
           // re-load.
-          Inflate(self, self, obj, 0);
-          lock_word = obj->GetLockWord(true);
+          Inflate(self, self, h_obj.Get(), 0);
+          lock_word = h_obj->GetLockWord(true);
         }
         break;
       }
@@ -1203,8 +1252,9 @@
     if (monitor != nullptr) {
       pretty_object = monitor->GetObject();
     }
-  } else if (state == kBlocked) {
-    wait_message = "  - waiting to lock ";
+  } else if (state == kBlocked || state == kWaitingForLockInflation) {
+    wait_message = (state == kBlocked) ? "  - waiting to lock "
+                                       : "  - waiting for lock inflation of ";
     pretty_object = thread->GetMonitorEnterObject();
     if (pretty_object != nullptr) {
       if (kUseReadBarrier && Thread::Current()->GetIsGcMarking()) {
diff --git a/runtime/monitor.h b/runtime/monitor.h
index 09faeba..d7aef34 100644
--- a/runtime/monitor.h
+++ b/runtime/monitor.h
@@ -31,6 +31,7 @@
 #include "gc_root.h"
 #include "lock_word.h"
 #include "read_barrier_option.h"
+#include "runtime_callbacks.h"
 #include "thread_state.h"
 
 namespace art {
@@ -47,6 +48,11 @@
   class Object;
 }  // namespace mirror
 
+enum class LockReason {
+  kForWait,
+  kForLock,
+};
+
 class Monitor {
  public:
   // The default number of spins that are done before thread suspension is used to forcibly inflate
@@ -205,9 +211,11 @@
       REQUIRES(monitor_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  template<LockReason reason = LockReason::kForLock>
   void Lock(Thread* self)
       REQUIRES(!monitor_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
+
   bool Unlock(Thread* thread)
       REQUIRES(!monitor_lock_)
       REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc
index 4fbbb72..94007ff 100644
--- a/runtime/native/java_lang_Thread.cc
+++ b/runtime/native/java_lang_Thread.cc
@@ -86,6 +86,8 @@
     case kWaiting:                        return kJavaWaiting;
     case kStarting:                       return kJavaNew;
     case kNative:                         return kJavaRunnable;
+    case kWaitingForTaskProcessor:        return kJavaWaiting;
+    case kWaitingForLockInflation:        return kJavaWaiting;
     case kWaitingForGcToComplete:         return kJavaWaiting;
     case kWaitingPerformingGc:            return kJavaWaiting;
     case kWaitingForCheckPointsToRun:     return kJavaWaiting;
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 4e84468..2eb4411 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -658,6 +658,10 @@
 
   RuntimeCallbacks* GetRuntimeCallbacks();
 
+  bool HasLoadedPlugins() const {
+    return !plugins_.empty();
+  }
+
   void InitThreadGroups(Thread* self);
 
   void SetDumpGCPerformanceOnShutdown(bool value) {
diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc
index 16d6c13..88d3f28 100644
--- a/runtime/runtime_callbacks.cc
+++ b/runtime/runtime_callbacks.cc
@@ -21,6 +21,7 @@
 #include "art_method.h"
 #include "base/macros.h"
 #include "class_linker.h"
+#include "monitor.h"
 #include "thread.h"
 
 namespace art {
@@ -38,6 +39,38 @@
   }
 }
 
+void RuntimeCallbacks::MonitorContendedLocking(Monitor* m) {
+  for (MonitorCallback* cb : monitor_callbacks_) {
+    cb->MonitorContendedLocking(m);
+  }
+}
+
+void RuntimeCallbacks::MonitorContendedLocked(Monitor* m) {
+  for (MonitorCallback* cb : monitor_callbacks_) {
+    cb->MonitorContendedLocked(m);
+  }
+}
+
+void RuntimeCallbacks::ObjectWaitStart(Handle<mirror::Object> m, int64_t timeout) {
+  for (MonitorCallback* cb : monitor_callbacks_) {
+    cb->ObjectWaitStart(m, timeout);
+  }
+}
+
+void RuntimeCallbacks::MonitorWaitFinished(Monitor* m, bool timeout) {
+  for (MonitorCallback* cb : monitor_callbacks_) {
+    cb->MonitorWaitFinished(m, timeout);
+  }
+}
+
+void RuntimeCallbacks::AddMonitorCallback(MonitorCallback* cb) {
+  monitor_callbacks_.push_back(cb);
+}
+
+void RuntimeCallbacks::RemoveMonitorCallback(MonitorCallback* cb) {
+  Remove(cb, &monitor_callbacks_);
+}
+
 void RuntimeCallbacks::RemoveThreadLifecycleCallback(ThreadLifecycleCallback* cb) {
   Remove(cb, &thread_callbacks_);
 }
diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h
index e8f1824..fa686d3 100644
--- a/runtime/runtime_callbacks.h
+++ b/runtime/runtime_callbacks.h
@@ -29,12 +29,14 @@
 namespace mirror {
 class Class;
 class ClassLoader;
+class Object;
 }  // namespace mirror
 
 class ArtMethod;
 class ClassLoadCallback;
 class Thread;
 class MethodCallback;
+class Monitor;
 class ThreadLifecycleCallback;
 
 // Note: RuntimeCallbacks uses the mutator lock to synchronize the callback lists. A thread must
@@ -73,6 +75,25 @@
   virtual void NextRuntimePhase(RuntimePhase phase) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
 };
 
+class MonitorCallback {
+ public:
+  // Called just before the thread goes to sleep to wait for the monitor to become unlocked.
+  virtual void MonitorContendedLocking(Monitor* mon) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+  // Called just after the monitor has been successfully acquired when it was already locked.
+  virtual void MonitorContendedLocked(Monitor* mon) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+  // Called on entry to the Object#wait method regardless of whether or not the call is valid.
+  virtual void ObjectWaitStart(Handle<mirror::Object> obj, int64_t millis_timeout)
+      REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+
+  // Called just after the monitor has woken up from going to sleep for a wait(). At this point the
+  // thread does not possess a lock on the monitor. This will only be called for threads wait calls
+  // where the thread did (or at least could have) gone to sleep.
+  virtual void MonitorWaitFinished(Monitor* m, bool timed_out)
+      REQUIRES_SHARED(Locks::mutator_lock_) = 0;
+
+  virtual ~MonitorCallback() {}
+};
+
 class RuntimeCallbacks {
  public:
   void AddThreadLifecycleCallback(ThreadLifecycleCallback* cb) REQUIRES(Locks::mutator_lock_);
@@ -120,6 +141,16 @@
                             /*out*/void** new_implementation)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  void MonitorContendedLocking(Monitor* m) REQUIRES_SHARED(Locks::mutator_lock_);
+  void MonitorContendedLocked(Monitor* m) REQUIRES_SHARED(Locks::mutator_lock_);
+  void ObjectWaitStart(Handle<mirror::Object> m, int64_t timeout)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+  void MonitorWaitFinished(Monitor* m, bool timed_out)
+      REQUIRES_SHARED(Locks::mutator_lock_);
+
+  void AddMonitorCallback(MonitorCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
+  void RemoveMonitorCallback(MonitorCallback* cb) REQUIRES_SHARED(Locks::mutator_lock_);
+
  private:
   std::vector<ThreadLifecycleCallback*> thread_callbacks_
       GUARDED_BY(Locks::mutator_lock_);
@@ -131,6 +162,8 @@
       GUARDED_BY(Locks::mutator_lock_);
   std::vector<MethodCallback*> method_callbacks_
       GUARDED_BY(Locks::mutator_lock_);
+  std::vector<MonitorCallback*> monitor_callbacks_
+      GUARDED_BY(Locks::mutator_lock_);
 };
 
 }  // namespace art
diff --git a/runtime/runtime_callbacks_test.cc b/runtime/runtime_callbacks_test.cc
index ac2ed9e..ef17258 100644
--- a/runtime/runtime_callbacks_test.cc
+++ b/runtime/runtime_callbacks_test.cc
@@ -22,6 +22,7 @@
 
 #include <initializer_list>
 #include <memory>
+#include <mutex>
 #include <string>
 
 #include "jni.h"
@@ -29,12 +30,14 @@
 #include "art_method-inl.h"
 #include "base/mutex.h"
 #include "class_linker.h"
+#include "class_reference.h"
 #include "common_runtime_test.h"
 #include "handle.h"
 #include "handle_scope-inl.h"
 #include "mem_map.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
+#include "monitor.h"
 #include "nativehelper/ScopedLocalRef.h"
 #include "obj_ptr.h"
 #include "runtime.h"
@@ -433,4 +436,87 @@
   ASSERT_EQ(1u, cb_.death_seen);
 }
 
+class MonitorWaitCallbacksTest : public RuntimeCallbacksTest {
+ protected:
+  void AddListener() OVERRIDE REQUIRES(Locks::mutator_lock_) {
+    Runtime::Current()->GetRuntimeCallbacks()->AddMonitorCallback(&cb_);
+  }
+  void RemoveListener() OVERRIDE REQUIRES(Locks::mutator_lock_) {
+    Runtime::Current()->GetRuntimeCallbacks()->RemoveMonitorCallback(&cb_);
+  }
+
+  struct Callback : public MonitorCallback {
+    bool IsInterestingObject(mirror::Object* obj) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+      if (!obj->IsClass()) {
+        return false;
+      }
+      std::lock_guard<std::mutex> lock(ref_guard_);
+      mirror::Class* k = obj->AsClass();
+      ClassReference test = { &k->GetDexFile(), k->GetDexClassDefIndex() };
+      return ref_ == test;
+    }
+
+    void SetInterestingObject(mirror::Object* obj) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+      std::lock_guard<std::mutex> lock(ref_guard_);
+      mirror::Class* k = obj->AsClass();
+      ref_ = { &k->GetDexFile(), k->GetDexClassDefIndex() };
+    }
+
+    void MonitorContendedLocking(Monitor* mon ATTRIBUTE_UNUSED)
+        REQUIRES_SHARED(Locks::mutator_lock_) { }
+
+    void MonitorContendedLocked(Monitor* mon ATTRIBUTE_UNUSED)
+        REQUIRES_SHARED(Locks::mutator_lock_) { }
+
+    void ObjectWaitStart(Handle<mirror::Object> obj, int64_t millis ATTRIBUTE_UNUSED)
+        REQUIRES_SHARED(Locks::mutator_lock_) {
+      if (IsInterestingObject(obj.Get())) {
+        saw_wait_start_ = true;
+      }
+    }
+
+    void MonitorWaitFinished(Monitor* m, bool timed_out ATTRIBUTE_UNUSED)
+        REQUIRES_SHARED(Locks::mutator_lock_) {
+      if (IsInterestingObject(m->GetObject())) {
+        saw_wait_finished_ = true;
+      }
+    }
+
+    std::mutex ref_guard_;
+    ClassReference ref_ = {nullptr, 0};
+    bool saw_wait_start_ = false;
+    bool saw_wait_finished_ = false;
+  };
+
+  Callback cb_;
+};
+
+// TODO It would be good to have more tests for this but due to the multi-threaded nature of the
+// callbacks this is difficult. For now the run-tests 1931 & 1932 should be sufficient.
+TEST_F(MonitorWaitCallbacksTest, WaitUnlocked) {
+  ASSERT_FALSE(cb_.saw_wait_finished_);
+  ASSERT_FALSE(cb_.saw_wait_start_);
+  {
+    Thread* self = Thread::Current();
+    self->TransitionFromSuspendedToRunnable();
+    bool started = runtime_->Start();
+    ASSERT_TRUE(started);
+    {
+      ScopedObjectAccess soa(self);
+      cb_.SetInterestingObject(
+          soa.Decode<mirror::Class>(WellKnownClasses::java_util_Collections).Ptr());
+      Monitor::Wait(
+          self,
+          // Just a random class
+          soa.Decode<mirror::Class>(WellKnownClasses::java_util_Collections).Ptr(),
+          /*ms*/0,
+          /*ns*/0,
+          /*interruptShouldThrow*/false,
+          /*why*/kWaiting);
+    }
+  }
+  ASSERT_TRUE(cb_.saw_wait_start_);
+  ASSERT_FALSE(cb_.saw_wait_finished_);
+}
+
 }  // namespace art
diff --git a/runtime/thread_state.h b/runtime/thread_state.h
index 8f2f70f..8edfeec 100644
--- a/runtime/thread_state.h
+++ b/runtime/thread_state.h
@@ -29,6 +29,8 @@
   kSleeping,                        // TIMED_WAITING  TS_SLEEPING  in Thread.sleep()
   kBlocked,                         // BLOCKED        TS_MONITOR   blocked on a monitor
   kWaiting,                         // WAITING        TS_WAIT      in Object.wait()
+  kWaitingForLockInflation,         // WAITING        TS_WAIT      blocked inflating a thin-lock
+  kWaitingForTaskProcessor,         // WAITING        TS_WAIT      blocked waiting for taskProcessor
   kWaitingForGcToComplete,          // WAITING        TS_WAIT      blocked waiting for GC
   kWaitingForCheckPointsToRun,      // WAITING        TS_WAIT      GC waiting for checkpoints to run
   kWaitingPerformingGc,             // WAITING        TS_WAIT      performing GC