Use JNI to access activity manager process state.

After a GC occurs, we use JNI to access activity manager process
state to decide whether or not we will trim.

Change-Id: Iad981e3a7cdc694729d8792cc0f19e0262154388
(cherry picked from commit c39e342317d77e701b4cd01cd5b05902e6512f4b)
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 47e9b75..a9e5b08 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -85,6 +85,14 @@
       max_allowed_footprint_(initial_size),
       native_footprint_gc_watermark_(initial_size),
       native_footprint_limit_(2 * initial_size),
+      activity_thread_class_(NULL),
+      application_thread_class_(NULL),
+      activity_thread_(NULL),
+      application_thread_(NULL),
+      last_process_state_id_(NULL),
+      // Initially care about pauses in case we never get notified of process states, or if the JNI
+      // code becomes broken.
+      care_about_pause_times_(true),
       concurrent_start_bytes_(concurrent_gc ? initial_size - (kMinConcurrentRemainingBytes)
                                             :  std::numeric_limits<size_t>::max()),
       total_bytes_freed_ever_(0),
@@ -92,7 +100,6 @@
       large_object_threshold_(3 * kPageSize),
       num_bytes_allocated_(0),
       native_bytes_allocated_(0),
-      process_state_(PROCESS_STATE_TOP),
       gc_memory_overhead_(0),
       verify_missing_card_marks_(false),
       verify_system_weaks_(false),
@@ -246,8 +253,122 @@
   }
 };
 
-void Heap::UpdateProcessState(ProcessState process_state) {
-  process_state_ = process_state;
+static bool ReadStaticInt(JNIEnvExt* env, jclass clz, const char* name, int* out_value) {
+  CHECK(out_value != NULL);
+  jfieldID field = env->GetStaticFieldID(clz, name, "I");
+  if (field == NULL) {
+    env->ExceptionClear();
+    return false;
+  }
+  *out_value = env->GetStaticIntField(clz, field);
+  return true;
+}
+
+void Heap::ListenForProcessStateChange() {
+  VLOG(gc) << "Heap notified of process state change";
+
+  Thread* self = Thread::Current();
+  JNIEnvExt* env = self->GetJniEnv();
+
+  if (!have_zygote_space_) {
+    return;
+  }
+
+  if (activity_thread_class_ == NULL) {
+    jclass clz = env->FindClass("android/app/ActivityThread");
+    if (clz == NULL) {
+      env->ExceptionClear();
+      LOG(WARNING) << "Could not find activity thread class in process state change";
+      return;
+    }
+    activity_thread_class_ = reinterpret_cast<jclass>(env->NewGlobalRef(clz));
+  }
+
+  if (activity_thread_class_ != NULL && activity_thread_ == NULL) {
+    jmethodID current_activity_method = env->GetStaticMethodID(activity_thread_class_,
+                                                               "currentActivityThread",
+                                                               "()Landroid/app/ActivityThread;");
+    if (current_activity_method == NULL) {
+      env->ExceptionClear();
+      LOG(WARNING) << "Could not get method for currentActivityThread";
+      return;
+    }
+
+    jobject obj = env->CallStaticObjectMethod(activity_thread_class_, current_activity_method);
+    if (obj == NULL) {
+      env->ExceptionClear();
+      LOG(WARNING) << "Could not get current activity";
+      return;
+    }
+    activity_thread_ = env->NewGlobalRef(obj);
+  }
+
+  if (process_state_cares_about_pause_time_.empty()) {
+    // Just attempt to do this the first time.
+    jclass clz = env->FindClass("android/app/ActivityManager");
+    if (clz == NULL) {
+      LOG(WARNING) << "Activity manager class is null";
+      return;
+    }
+    ScopedLocalRef<jclass> activity_manager(env, clz);
+    std::vector<const char*> care_about_pauses;
+    care_about_pauses.push_back("PROCESS_STATE_TOP");
+    care_about_pauses.push_back("PROCESS_STATE_IMPORTANT_BACKGROUND");
+    // Attempt to read the constants and classify them as whether or not we care about pause times.
+    for (size_t i = 0; i < care_about_pauses.size(); ++i) {
+      int process_state = 0;
+      if (ReadStaticInt(env, activity_manager.get(), care_about_pauses[i], &process_state)) {
+        process_state_cares_about_pause_time_.insert(process_state);
+        VLOG(gc) << "Adding process state " << process_state
+                 << " to set of states which care about pause time";
+      }
+    }
+  }
+
+  if (application_thread_class_ == NULL) {
+    jclass clz = env->FindClass("android/app/ActivityThread$ApplicationThread");
+    if (clz == NULL) {
+      env->ExceptionClear();
+      LOG(WARNING) << "Could not get application thread class";
+      return;
+    }
+    application_thread_class_ = reinterpret_cast<jclass>(env->NewGlobalRef(clz));
+    last_process_state_id_ = env->GetFieldID(application_thread_class_, "mLastProcessState", "I");
+    if (last_process_state_id_ == NULL) {
+      env->ExceptionClear();
+      LOG(WARNING) << "Could not get last process state member";
+      return;
+    }
+  }
+
+  if (application_thread_class_ != NULL && application_thread_ == NULL) {
+    jmethodID get_application_thread =
+        env->GetMethodID(activity_thread_class_, "getApplicationThread",
+                         "()Landroid/app/ActivityThread$ApplicationThread;");
+    if (get_application_thread == NULL) {
+      LOG(WARNING) << "Could not get method ID for get application thread";
+      return;
+    }
+
+    jobject obj = env->CallObjectMethod(activity_thread_, get_application_thread);
+    if (obj == NULL) {
+      LOG(WARNING) << "Could not get application thread";
+      return;
+    }
+
+    application_thread_ = env->NewGlobalRef(obj);
+  }
+
+  if (application_thread_ != NULL && last_process_state_id_ != NULL) {
+    int process_state = env->GetIntField(application_thread_, last_process_state_id_);
+    env->ExceptionClear();
+
+    care_about_pause_times_ = process_state_cares_about_pause_time_.find(process_state) !=
+        process_state_cares_about_pause_time_.end();
+
+    VLOG(gc) << "New process state " << process_state
+             << " care about pauses " << care_about_pause_times_;
+  }
 }
 
 void Heap::AddContinuousSpace(space::ContinuousSpace* space) {
@@ -1874,20 +1995,18 @@
     }
   }
 
-  SchedPolicy policy;
-  get_sched_policy(self->GetTid(), &policy);
-  if (policy == SP_FOREGROUND || policy == SP_AUDIO_APP) {
-    // Don't trim the heap if we are a foreground or audio app.
-    return;
-  }
-
   last_trim_time_ms_ = ms_time;
-  JNIEnv* env = self->GetJniEnv();
-  DCHECK(WellKnownClasses::java_lang_Daemons != NULL);
-  DCHECK(WellKnownClasses::java_lang_Daemons_requestHeapTrim != NULL);
-  env->CallStaticVoidMethod(WellKnownClasses::java_lang_Daemons,
-                            WellKnownClasses::java_lang_Daemons_requestHeapTrim);
-  CHECK(!env->ExceptionCheck());
+  ListenForProcessStateChange();
+
+  // Trim only if we do not currently care about pause times.
+  if (!care_about_pause_times_) {
+    JNIEnv* env = self->GetJniEnv();
+    DCHECK(WellKnownClasses::java_lang_Daemons != NULL);
+    DCHECK(WellKnownClasses::java_lang_Daemons_requestHeapTrim != NULL);
+    env->CallStaticVoidMethod(WellKnownClasses::java_lang_Daemons,
+                              WellKnownClasses::java_lang_Daemons_requestHeapTrim);
+    CHECK(!env->ExceptionCheck());
+  }
 }
 
 size_t Heap::Trim() {
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 3f91553..c1cff43 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -28,6 +28,7 @@
 #include "gc/collector/gc_type.h"
 #include "globals.h"
 #include "gtest/gtest.h"
+#include "jni.h"
 #include "locks.h"
 #include "offsets.h"
 #include "safe_map.h"
@@ -100,24 +101,6 @@
 };
 const HeapVerificationMode kDesiredHeapVerification = kNoHeapVerification;
 
-// This comes from ActivityManager and needs to be kept in sync.
-enum ProcessState {
-  PROCESS_STATE_PERSISTENT = 0,
-  PROCESS_STATE_PERSISTENT_UI = 1,
-  PROCESS_STATE_TOP = 2,
-  PROCESS_STATE_IMPORTANT_FOREGROUND = 3,
-  PROCESS_STATE_IMPORTANT_BACKGROUND = 4,
-  PROCESS_STATE_BACKUP = 5,
-  PROCESS_STATE_HEAVY_WEIGHT = 6,
-  PROCESS_STATE_SERVICE = 7,
-  PROCESS_STATE_RECEIVER = 8,
-  PROCESS_STATE_HOME = 9,
-  PROCESS_STATE_LAST_ACTIVITY = 10,
-  PROCESS_STATE_CACHED_ACTIVITY = 11,
-  PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 12,
-  PROCESS_STATE_CACHED_EMPTY = 13,
-};
-
 class Heap {
  public:
   static const size_t kDefaultInitialSize = 2 * MB;
@@ -387,8 +370,8 @@
                              collector::GcType gc_type)
       EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_);
 
-  // Update process state to let the heap know which type of GC to do.
-  void UpdateProcessState(ProcessState process_state);
+  // Gets called when we get notified by ActivityThread that the process state has changed.
+  void ListenForProcessStateChange();
 
   // DEPRECATED: Should remove in "near" future when support for multiple image spaces is added.
   // Assumes there is only one image space.
@@ -542,6 +525,19 @@
   // The watermark at which a GC is performed inside of registerNativeAllocation.
   size_t native_footprint_limit_;
 
+  // Activity manager members.
+  jclass activity_thread_class_;
+  jclass application_thread_class_;
+  jobject activity_thread_;
+  jobject application_thread_;
+  jfieldID last_process_state_id_;
+
+  // Process states which care about pause times.
+  std::set<int> process_state_cares_about_pause_time_;
+
+  // Whether or not we currently care about pause times.
+  bool care_about_pause_times_;
+
   // When num_bytes_allocated_ exceeds this amount then a concurrent GC should be requested so that
   // it completes ahead of an allocation failing.
   size_t concurrent_start_bytes_;
@@ -561,9 +557,6 @@
   // Bytes which are allocated and managed by native code but still need to be accounted for.
   AtomicInteger native_bytes_allocated_;
 
-  // Current process state, updated by activity manager.
-  ProcessState process_state_;
-
   // Data structure GC overhead.
   AtomicInteger gc_memory_overhead_;
 
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index b352d08..4ef7607 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -216,10 +216,6 @@
   Runtime::Current()->GetHeap()->ConcurrentGC(self);
 }
 
-static void VMRuntime_updateProcessState(JNIEnv* env, jobject, jint processState) {
-  Runtime::Current()->GetHeap()->UpdateProcessState(static_cast<gc::ProcessState>(processState));
-}
-
 static JNINativeMethod gMethods[] = {
   NATIVE_METHOD(VMRuntime, addressOf, "(Ljava/lang/Object;)J"),
   NATIVE_METHOD(VMRuntime, bootClassPath, "()Ljava/lang/String;"),
@@ -239,7 +235,6 @@
   NATIVE_METHOD(VMRuntime, trimHeap, "()V"),
   NATIVE_METHOD(VMRuntime, vmVersion, "()Ljava/lang/String;"),
   NATIVE_METHOD(VMRuntime, vmLibrary, "()Ljava/lang/String;"),
-  NATIVE_METHOD(VMRuntime, updateProcessState, "(I)V"),
 };
 
 void register_dalvik_system_VMRuntime(JNIEnv* env) {