Added JNI hack to support JNI hack

Some of our graphics code wants to get a pointer, release it, and then
continue to use it.  The "forcecopy" mode of CheckJNI was designed
to find any code that does this.  It succeeded.

To support the behavior, we provide a JNI helper function that does
the dirty work.  It passes a magic value into the Get and Release calls
that causes "forcecopy" to skip the copy.  When forcecopy is not
enabled, the values are simply ignored.

To avoid any possibility of the function getting published in the NDK,
the function is not described in JNIHelp.h.

Bug 3409356

Change-Id: Ibd20d12ba6d3d3236ebf5760f7ccaa8c557e3774
diff --git a/libnativehelper/JNIHelp.c b/libnativehelper/JNIHelp.c
index ededb4c..e5abc0f 100644
--- a/libnativehelper/JNIHelp.c
+++ b/libnativehelper/JNIHelp.c
@@ -21,6 +21,7 @@
 #include "JNIHelp.h"
 #include "utils/Log.h"
 
+#include <stdlib.h>
 #include <string.h>
 #include <assert.h>
 
@@ -337,3 +338,38 @@
 void jniSetFileDescriptorOfFD(JNIEnv* env, jobject fileDescriptor, int value) {
     (*env)->SetIntField(env, fileDescriptor, gCachedFields.descriptorField, value);
 }
+
+/*
+ * DO NOT USE THIS FUNCTION
+ *
+ * Get a pointer to the elements of a non-movable array.
+ *
+ * The semantics are similar to GetDirectBufferAddress.  Specifically, the VM
+ * guarantees that the array will not move, and the caller must ensure that
+ * it does not continue to use the pointer after the object is collected.
+ *
+ * We currently use an illegal sequence that trips up CheckJNI when
+ * the "forcecopy" mode is enabled.  We pass in a magic value to work
+ * around the problem.
+ *
+ * Returns NULL if the array is movable.
+ */
+jbyte* jniGetNonMovableArrayElements(JNIEnv* env, jarray arrayObj)
+{
+#define kNoCopyMagic 0xd5aab57f     /* also in CheckJni.c */
+
+    /*
+     * Normally the "isCopy" parameter is for a return value only, so the
+     * non-CheckJNI VM will ignore whatever we pass in.
+     */
+    uint32_t noCopy = kNoCopyMagic;
+    jbyte *addr = (*env)->GetByteArrayElements(env, arrayObj,
+            (jboolean*)&noCopy);
+
+    /*
+     * The non-CheckJNI implementation only cares about the array object,
+     * so we can replace the element pointer with the magic value.
+     */
+    (*env)->ReleaseByteArrayElements(env, arrayObj, (jbyte*) kNoCopyMagic, 0);
+    return addr;
+}
diff --git a/vm/CheckJni.c b/vm/CheckJni.c
index f8815fb..fa65b50 100644
--- a/vm/CheckJni.c
+++ b/vm/CheckJni.c
@@ -2121,6 +2121,14 @@
 NEW_PRIMITIVE_ARRAY(jdoubleArray, Double);
 
 
+/*
+ * Hack to allow forcecopy to work with jniGetNonMovableArrayElements.
+ * The code deliberately uses an invalid sequence of operations, so we
+ * need to pass it through unmodified.  Review that code before making
+ * any changes here.
+ */
+#define kNoCopyMagic    0xd5aab57f
+
 #define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname)                        \
     static _ctype* Check_Get##_jname##ArrayElements(JNIEnv* env,            \
         _ctype##Array array, jboolean* isCopy)                              \
@@ -2128,10 +2136,19 @@
         CHECK_ENTER(env, kFlag_Default);                                    \
         CHECK_ARRAY(env, array);                                            \
         _ctype* result;                                                     \
+        u4 noCopy = 0;                                                      \
+        if (((JNIEnvExt*)env)->forceDataCopy && isCopy != NULL) {           \
+            /* capture this before the base call tramples on it */          \
+            noCopy = *(u4*) isCopy;                                         \
+        }                                                                   \
         result = BASE_ENV(env)->Get##_jname##ArrayElements(env,             \
             array, isCopy);                                                 \
         if (((JNIEnvExt*)env)->forceDataCopy && result != NULL) {           \
-            result = (_ctype*) createGuardedPACopy(env, array, isCopy);     \
+            if (noCopy == kNoCopyMagic) {                                   \
+                LOGV("FC: not copying %p %x\n", array, noCopy);             \
+            } else {                                                        \
+                result = (_ctype*) createGuardedPACopy(env, array, isCopy); \
+            }                                                               \
         }                                                                   \
         CHECK_EXIT(env);                                                    \
         return result;                                                      \
@@ -2146,7 +2163,13 @@
         CHECK_NON_NULL(env, elems);                                         \
         CHECK_RELEASE_MODE(env, mode);                                      \
         if (((JNIEnvExt*)env)->forceDataCopy) {                             \
-            elems = (_ctype*) releaseGuardedPACopy(env, array, elems, mode);\
+            if ((uintptr_t)elems == kNoCopyMagic) {                         \
+                LOGV("FC: not freeing %p\n", array);                        \
+                elems = NULL;   /* base JNI call doesn't currently need */  \
+            } else {                                                        \
+                elems = (_ctype*) releaseGuardedPACopy(env, array, elems,   \
+                        mode);                                              \
+            }                                                               \
         }                                                                   \
         BASE_ENV(env)->Release##_jname##ArrayElements(env,                  \
             array, elems, mode);                                            \