Add parceling support for HIDL memory in Java

This change adds support for reading/writing a HidlMemory
instance into / out of a HwParcel and HwBlob, in a format that
is compatible with the hidl_memory C++ type.
This paves the way to be able to exchange shared memory blocks
between native and Java via HwBinder, which will be exposed
as adding Java support for the HIDL 'memory' type.

Change-Id: I6cbbf852218c8a631f9014e7caa7a97d17e11889
Bug: 143566068
diff --git a/Android.bp b/Android.bp
index ea14b9d..c9c9a9a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -870,11 +870,13 @@
     srcs: [
         "core/java/android/os/HidlSupport.java",
         "core/java/android/annotation/IntDef.java",
+        "core/java/android/annotation/IntRange.java",
         "core/java/android/annotation/NonNull.java",
         "core/java/android/annotation/Nullable.java",
         "core/java/android/annotation/SystemApi.java",
         "core/java/android/annotation/TestApi.java",
         "core/java/android/annotation/UnsupportedAppUsage.java",
+        "core/java/android/os/HidlMemory.java",
         "core/java/android/os/HwBinder.java",
         "core/java/android/os/HwBlob.java",
         "core/java/android/os/HwParcel.java",
@@ -1405,8 +1407,10 @@
     srcs: [
         "core/java/android/os/HidlSupport.java",
         "core/java/android/annotation/IntDef.java",
+        "core/java/android/annotation/IntRange.java",
         "core/java/android/annotation/NonNull.java",
         "core/java/android/annotation/SystemApi.java",
+        "core/java/android/os/HidlMemory.java",
         "core/java/android/os/HwBinder.java",
         "core/java/android/os/HwBlob.java",
         "core/java/android/os/HwParcel.java",
diff --git a/api/system-current.txt b/api/system-current.txt
index cb327eb..0f05739 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5489,6 +5489,17 @@
     method @NonNull public static java.io.File getVendorDirectory();
   }
 
+  public class HidlMemory implements java.io.Closeable {
+    ctor public HidlMemory(@NonNull String, @IntRange(from=0) long, @Nullable android.os.NativeHandle);
+    method public void close() throws java.io.IOException;
+    method @NonNull public android.os.HidlMemory dup() throws java.io.IOException;
+    method protected void finalize();
+    method @Nullable public android.os.NativeHandle getHandle();
+    method @NonNull public String getName();
+    method public long getSize();
+    method @NonNull public android.os.NativeHandle releaseHandle();
+  }
+
   public class HidlSupport {
     method public static boolean deepEquals(Object, Object);
     method public static int deepHashCode(Object);
@@ -5519,6 +5530,7 @@
     method public final void copyToInt8Array(long, byte[], int);
     method public final boolean getBool(long);
     method public final double getDouble(long);
+    method public final long getFieldHandle(long);
     method public final float getFloat(long);
     method public final short getInt16(long);
     method public final int getInt32(long);
@@ -5533,6 +5545,7 @@
     method public final void putDoubleArray(long, double[]);
     method public final void putFloat(long, float);
     method public final void putFloatArray(long, float[]);
+    method public final void putHidlMemory(long, @NonNull android.os.HidlMemory);
     method public final void putInt16(long, short);
     method public final void putInt16Array(long, short[]);
     method public final void putInt32(long, int);
@@ -5561,9 +5574,11 @@
     method public final double readDouble();
     method public final java.util.ArrayList<java.lang.Double> readDoubleVector();
     method public final android.os.HwBlob readEmbeddedBuffer(long, long, long, boolean);
+    method @NonNull @Nullable public final android.os.HidlMemory readEmbeddedHidlMemory(long, long, long);
     method @Nullable public final android.os.NativeHandle readEmbeddedNativeHandle(long, long);
     method public final float readFloat();
     method public final java.util.ArrayList<java.lang.Float> readFloatVector();
+    method @NonNull public final android.os.HidlMemory readHidlMemory();
     method public final short readInt16();
     method public final java.util.ArrayList<java.lang.Short> readInt16Vector();
     method public final int readInt32();
@@ -5588,6 +5603,7 @@
     method public final void writeDoubleVector(java.util.ArrayList<java.lang.Double>);
     method public final void writeFloat(float);
     method public final void writeFloatVector(java.util.ArrayList<java.lang.Float>);
+    method public final void writeHidlMemory(@NonNull android.os.HidlMemory);
     method public final void writeInt16(short);
     method public final void writeInt16Vector(java.util.ArrayList<java.lang.Short>);
     method public final void writeInt32(int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 897e825..1b8c372 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1808,6 +1808,17 @@
     method public static boolean contains(java.io.File, java.io.File);
   }
 
+  public class HidlMemory implements java.io.Closeable {
+    ctor public HidlMemory(@NonNull String, @IntRange(from=0) long, @Nullable android.os.NativeHandle);
+    method public void close() throws java.io.IOException;
+    method @NonNull public android.os.HidlMemory dup() throws java.io.IOException;
+    method protected void finalize();
+    method @Nullable public android.os.NativeHandle getHandle();
+    method @NonNull public String getName();
+    method public long getSize();
+    method @NonNull public android.os.NativeHandle releaseHandle();
+  }
+
   public abstract class HwBinder implements android.os.IHwBinder {
     ctor public HwBinder();
     method public static final void configureRpcThreadpool(long, boolean);
@@ -1831,6 +1842,7 @@
     method public final void copyToInt8Array(long, byte[], int);
     method public final boolean getBool(long);
     method public final double getDouble(long);
+    method public final long getFieldHandle(long);
     method public final float getFloat(long);
     method public final short getInt16(long);
     method public final int getInt32(long);
@@ -1845,6 +1857,7 @@
     method public final void putDoubleArray(long, double[]);
     method public final void putFloat(long, float);
     method public final void putFloatArray(long, float[]);
+    method public final void putHidlMemory(long, @NonNull android.os.HidlMemory);
     method public final void putInt16(long, short);
     method public final void putInt16Array(long, short[]);
     method public final void putInt32(long, int);
@@ -1873,9 +1886,11 @@
     method public final double readDouble();
     method public final java.util.ArrayList<java.lang.Double> readDoubleVector();
     method public final android.os.HwBlob readEmbeddedBuffer(long, long, long, boolean);
+    method @NonNull @Nullable public final android.os.HidlMemory readEmbeddedHidlMemory(long, long, long);
     method @Nullable public final android.os.NativeHandle readEmbeddedNativeHandle(long, long);
     method public final float readFloat();
     method public final java.util.ArrayList<java.lang.Float> readFloatVector();
+    method @NonNull public final android.os.HidlMemory readHidlMemory();
     method public final short readInt16();
     method public final java.util.ArrayList<java.lang.Short> readInt16Vector();
     method public final int readInt32();
@@ -1900,6 +1915,7 @@
     method public final void writeDoubleVector(java.util.ArrayList<java.lang.Double>);
     method public final void writeFloat(float);
     method public final void writeFloatVector(java.util.ArrayList<java.lang.Float>);
+    method public final void writeHidlMemory(@NonNull android.os.HidlMemory);
     method public final void writeInt16(short);
     method public final void writeInt16Vector(java.util.ArrayList<java.lang.Short>);
     method public final void writeInt32(int);
diff --git a/core/java/android/os/HidlMemory.java b/core/java/android/os/HidlMemory.java
new file mode 100644
index 0000000..aeb6589
--- /dev/null
+++ b/core/java/android/os/HidlMemory.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+package android.os;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+/**
+ * An abstract representation of a memory block, as representing by the HIDL system.
+ *
+ * The block is defined by a {name, size, handle} tuple, where the name is used to determine how to
+ * interpret the handle. The underlying handle is assumed to be owned by this instance and will be
+ * closed as soon as {@link #close()} is called on this instance, or this instance has been
+ * finalized (the latter supports using it in a shared manner without having to worry about who owns
+ * this instance, the former is more efficient resource-wise and is recommended for most use-cases).
+ * Note, however, that ownership of the handle does not necessarily imply ownership of the
+ * underlying file descriptors - the underlying handle may or may not own them. If you want the
+ * underlying handle to outlive this instance, call {@link #releaseHandle()} to obtain the handle
+ * and detach the ownership relationship.
+ *
+ * @hide
+ */
+@SystemApi
+@TestApi
+public class HidlMemory implements Closeable {
+    private final @NonNull String mName;
+    private final long mSize;
+    private @Nullable NativeHandle mHandle;
+    private long mNativeContext;  // For use of native code.
+
+    /**
+     * Constructor.
+     *
+     * @param name      The name of the IMapper service used to resolve the handle (e.g. "ashmem").
+     * @param size      The (non-negative) size in bytes of the memory block.
+     * @param handle    The handle. May be null. This instance will own the handle and will close it
+     *                  as soon as {@link #close()} is called or the object is destroyed. This, this
+     *                  handle instance should generally not be shared with other clients.
+     */
+    public HidlMemory(@NonNull String name, @IntRange(from = 0) long size,
+            @Nullable NativeHandle handle) {
+        mName = name;
+        mSize = size;
+        mHandle = handle;
+    }
+
+    /**
+     * Create a copy of this instance, where the underlying handle (and its file descriptors) have
+     * been duplicated.
+     */
+    @NonNull
+    public HidlMemory dup() throws IOException {
+        return new HidlMemory(mName, mSize, mHandle != null ? mHandle.dup() : null);
+    }
+
+    /**
+     * Close the underlying native handle. No-op if handle is null or has been released using {@link
+     * #releaseHandle()}.
+     */
+    @Override
+    public void close() throws IOException {
+        if (mHandle != null) {
+            mHandle.close();
+        }
+    }
+
+    /**
+     * Disowns the underlying handle and returns it. This object becomes invalid.
+     *
+     * @return The underlying handle.
+     */
+    @NonNull
+    public NativeHandle releaseHandle() {
+        NativeHandle handle = mHandle;
+        mHandle = null;
+        return handle;
+    }
+
+    /**
+     * Gets the name, which represents how the handle is to be interpreted.
+     *
+     * @return The name.
+     */
+    @NonNull
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * Gets the size of the block, in bytes.
+     *
+     * @return The size.
+     */
+    public long getSize() {
+        return mSize;
+    }
+
+    /**
+     * Gets a native handle. The actual interpretation depends on the name and is implementation
+     * defined.
+     *
+     * @return The native handle.
+     */
+    @Nullable
+    public NativeHandle getHandle() {
+        return mHandle;
+    }
+
+    @Override
+    protected void finalize() {
+        try {
+            close();
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            nativeFinalize();
+        }
+    }
+
+    private native void nativeFinalize();
+}
diff --git a/core/java/android/os/HwBlob.java b/core/java/android/os/HwBlob.java
index 2c453bf..154227b2 100644
--- a/core/java/android/os/HwBlob.java
+++ b/core/java/android/os/HwBlob.java
@@ -92,6 +92,14 @@
      * @throws IndexOutOfBoundsException when offset is out of this HwBlob
      */
     public native final String getString(long offset);
+    /**
+     * For embedded fields that follow a two-step approach for reading, first obtain their field
+     * handle using this method, and pass that field handle to the respective
+     * HwParcel.readEmbedded*() method.
+     * @param offset The field offset.
+     * @return The field handle.
+     */
+    public native final long getFieldHandle(long offset);
 
     /**
      * Copy the blobs data starting from the given byte offset into the range, copying
@@ -312,6 +320,20 @@
     public native final void putBlob(long offset, HwBlob blob);
 
     /**
+     * Writes a HidlMemory instance (without duplicating the underlying file descriptors) at an
+     * offset.
+     *
+     * @param offset location to write value
+     * @param mem    a {@link HidlMemory} instance to write
+     * @throws IndexOutOfBoundsException when [offset, offset + sizeof(jobject)] is out of range
+     */
+    public final void putHidlMemory(long offset, @NonNull HidlMemory mem) {
+        putNativeHandle(offset + 0  /* offset of 'handle' field. */, mem.getHandle());
+        putInt64(offset + 16  /* offset of 'size' field. */, mem.getSize());
+        putString(offset + 24  /* offset of 'name' field. */, mem.getName());
+    }
+
+    /**
      * @return current handle of HwBlob for reference in a parcelled binder transaction
      */
     public native final long handle();
diff --git a/core/java/android/os/HwParcel.java b/core/java/android/os/HwParcel.java
index 5e8929c..9786f16 100644
--- a/core/java/android/os/HwParcel.java
+++ b/core/java/android/os/HwParcel.java
@@ -324,6 +324,15 @@
     public native final void writeStrongBinder(IHwBinder binder);
 
     /**
+     * Write a HidlMemory object (without duplicating the underlying file descriptors) to the end
+     * of the parcel.
+     *
+     * @param memory value to write
+     */
+    @FastNative
+    public native final void writeHidlMemory(@NonNull HidlMemory memory);
+
+    /**
      * Checks to make sure that the interface name matches the name written by the parcel
      * sender by writeInterfaceToken
      *
@@ -582,6 +591,38 @@
     public native final IHwBinder readStrongBinder();
 
     /**
+     * Reads a HidlMemory value (without duplicating the underlying file
+     * descriptors) from the parcel. These file descriptors will only
+     * be open for the duration that the binder window is open. If they
+     * are needed further, you must call {@link HidlMemory#dup()}, which makes you also
+     * responsible for calling {@link HidlMemory#close()}.
+     *
+     * @return HidlMemory object read from parcel.
+     * @throws IllegalArgumentException if the parcel has no more data or is otherwise corrupt.
+     */
+    @FastNative
+    @NonNull
+    public native final HidlMemory readHidlMemory();
+
+    /**
+     * Reads an embedded HidlMemory (without duplicating the underlying
+     * file descriptors) from the parcel. These file descriptors will only
+     * be open for the duration that the binder window is open. If they
+     * are needed further, you must call {@link HidlMemory#dup()}. You
+     * do not need to call close on the HidlMemory returned from this.
+     *
+     * @param fieldHandle  handle of the field, obtained from the {@link HwBlob}.
+     * @param parentHandle parentHandle from which to read the embedded object
+     * @param offset       offset into parent
+     * @return a {@link HidlMemory} instance parsed from the parcel
+     * @throws IllegalArgumentException if the parcel has no more data
+     */
+    @FastNative
+    @NonNull
+    public native final @Nullable
+    HidlMemory readEmbeddedHidlMemory(long fieldHandle, long parentHandle, long offset);
+
+    /**
      * Read opaque segment of data as a blob.
      * @return blob of size expectedSize
      * @throws IllegalArgumentException if the parcel has no more data
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index ea10949..51f8fcc 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -125,6 +125,7 @@
                 "android_text_Hyphenator.cpp",
                 "android_os_Debug.cpp",
                 "android_os_GraphicsEnvironment.cpp",
+                "android_os_HidlMemory.cpp",
                 "android_os_HidlSupport.cpp",
                 "android_os_HwBinder.cpp",
                 "android_os_HwBlob.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 32d62fe..378e125 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -147,6 +147,7 @@
 extern int register_android_os_Trace(JNIEnv* env);
 extern int register_android_os_FileObserver(JNIEnv *env);
 extern int register_android_os_UEventObserver(JNIEnv* env);
+extern int register_android_os_HidlMemory(JNIEnv* env);
 extern int register_android_os_MemoryFile(JNIEnv* env);
 extern int register_android_os_SharedMemory(JNIEnv* env);
 extern int register_android_net_LocalSocketImpl(JNIEnv* env);
@@ -1432,6 +1433,7 @@
     REG_JNI(register_android_os_SystemProperties),
     REG_JNI(register_android_os_Binder),
     REG_JNI(register_android_os_Parcel),
+    REG_JNI(register_android_os_HidlMemory),
     REG_JNI(register_android_os_HidlSupport),
     REG_JNI(register_android_os_HwBinder),
     REG_JNI(register_android_os_HwBlob),
diff --git a/core/jni/android_os_HidlMemory.cpp b/core/jni/android_os_HidlMemory.cpp
new file mode 100644
index 0000000..69e4818
--- /dev/null
+++ b/core/jni/android_os_HidlMemory.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2019 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 "android_os_HidlMemory.h"
+#include "core_jni_helpers.h"
+#include "android_os_NativeHandle.h"
+
+#define PACKAGE_PATH    "android/os"
+#define CLASS_NAME      "HidlMemory"
+#define CLASS_PATH      PACKAGE_PATH "/" CLASS_NAME
+
+namespace android {
+
+namespace {
+
+static struct {
+    jclass clazz;
+    jfieldID nativeContext;  // long
+    jmethodID constructor;   // HidlMemory(String, long, NativeHandle)
+    jmethodID getName;       // String HidlMemory.getName()
+    jmethodID getSize;       // int HidlMemory.getSize()
+    jmethodID getHandle;     // NativeHandle HidlMemory.getHandle()
+} gFields;
+
+std::string stringFromJava(JNIEnv* env, jstring jstr) {
+    ScopedUtfChars s(env, jstr);
+    return s.c_str();
+}
+
+jstring stringToJava(JNIEnv* env, const std::string& cstr) {
+    return env->NewStringUTF(cstr.c_str());
+}
+
+static void nativeFinalize(JNIEnv* env, jobject jobj) {
+    jlong jNativeContext = env->GetLongField(jobj, gFields.nativeContext);
+    JHidlMemory* native = reinterpret_cast<JHidlMemory*>(jNativeContext);
+    delete native;
+}
+
+static JNINativeMethod gMethods[] = {
+        {"nativeFinalize", "()V", (void*) nativeFinalize},
+};
+
+}  // namespace
+
+JHidlMemory::~JHidlMemory() {
+    if (mObj) {
+        // Must manually delete the underlying handle - hidl_memory doesn't own
+        // it.
+        native_handle_delete(const_cast<native_handle_t*>(mObj->handle()));
+    }
+}
+
+/* static */ const hardware::hidl_memory* JHidlMemory::fromJava(JNIEnv* env,
+                                                                jobject jobj) {
+    // Try to get the result from cache.
+    env->MonitorEnter(jobj);
+    JHidlMemory* obj = getNativeContext(env, jobj);
+    if (!obj->mObj) {
+        // Create and cache.
+        obj->mObj = javaToNative(env, jobj);
+    }
+    env->MonitorExit(jobj);
+    return obj->mObj.get();
+}
+
+/* static */ jobject JHidlMemory::toJava(JNIEnv* env,
+                                         const hardware::hidl_memory& cobj) {
+    if (cobj.size() > std::numeric_limits<jlong>::max()) {
+        return nullptr;
+    }
+    jstring jname = stringToJava(env, cobj.name());
+    jlong jsize = static_cast<jlong>(cobj.size());
+    jobject jhandle =
+            JNativeHandle::MakeJavaNativeHandleObj(env, cobj.handle());
+
+    // We're sharing the handle of cobj, so the Java instance doesn't own it.
+    return env->NewObject(gFields.clazz,
+                          gFields.constructor,
+                          jname,
+                          jsize,
+                          jhandle,
+                          false);
+}
+
+/* static */ std::unique_ptr<hardware::hidl_memory> JHidlMemory::javaToNative(
+        JNIEnv* env,
+        jobject jobj) {
+    jstring jname =
+            static_cast<jstring>(env->CallObjectMethod(jobj, gFields.getName));
+    jlong jsize = env->CallLongMethod(jobj, gFields.getSize);
+    jobject jhandle = env->CallObjectMethod(jobj, gFields.getHandle);
+
+    if (jsize > std::numeric_limits<size_t>::max()) {
+        return nullptr;
+    }
+
+    std::string cname = stringFromJava(env, jname);
+    size_t csize = jsize;
+    // We created the handle here, we're responsible to call
+    // native_handle_delete() on it. However, we don't assume ownership of the
+    // underlying fd, so we shouldn't call native_handle_close() on it.
+    native_handle_t* chandle = JNativeHandle::MakeCppNativeHandle(env, jhandle,
+                                                                  nullptr);
+    // hidl_memory doesn't take ownership of the handle here, so won't delete
+    // or close it.
+    return std::make_unique<hardware::hidl_memory>(cname, chandle, csize);
+}
+
+/* static */ JHidlMemory* JHidlMemory::getNativeContext(JNIEnv* env,
+                                                        jobject jobj) {
+    env->MonitorEnter(jobj);
+    jlong jNativeContext = env->GetLongField(jobj, gFields.nativeContext);
+    JHidlMemory* native = reinterpret_cast<JHidlMemory*>(jNativeContext);
+    if (!native) {
+        native = new JHidlMemory();
+        env->SetLongField(jobj,
+                          gFields.nativeContext,
+                          reinterpret_cast<jlong>(native));
+    }
+    env->MonitorExit(jobj);
+    return native;
+}
+
+int register_android_os_HidlMemory(JNIEnv* env) {
+    jclass clazz = FindClassOrDie(env, CLASS_PATH);
+    gFields.clazz = MakeGlobalRefOrDie(env, clazz);
+
+    gFields.nativeContext = GetFieldIDOrDie(env, clazz, "mNativeContext", "J");
+
+    gFields.constructor = GetMethodIDOrDie(env,
+                                           clazz,
+                                           "<init>",
+                                           "(Ljava/lang/String;JL" PACKAGE_PATH "/NativeHandle;)V");
+    gFields.getName =
+            GetMethodIDOrDie(env, clazz, "getName", "()Ljava/lang/String;");
+    gFields.getSize = GetMethodIDOrDie(env, clazz, "getSize", "()J");
+    gFields.getHandle = GetMethodIDOrDie(env,
+                                         clazz,
+                                         "getHandle",
+                                         "()L" PACKAGE_PATH "/NativeHandle;");
+
+    RegisterMethodsOrDie(env, CLASS_PATH, gMethods, NELEM(gMethods));
+
+    return 0;
+}
+
+}  // namespace android
+
diff --git a/core/jni/android_os_HidlMemory.h b/core/jni/android_os_HidlMemory.h
new file mode 100644
index 0000000..993a132
--- /dev/null
+++ b/core/jni/android_os_HidlMemory.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 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 ANDROID_OS_HIDL_MEMORY_H
+#define ANDROID_OS_HIDL_MEMORY_H
+
+#include <jni.h>
+#include <hidl/HidlSupport.h>
+
+namespace android {
+
+// A utility class for handling the android.os.HidlMemory class from JNI code.
+class JHidlMemory final {
+ public:
+    // Convert an android.os.HidlMemory object to its C++ counterpart,
+    // hardware::hidl_memory.
+    // No duplication of file descriptors is performed.
+    // The returned reference is owned by the underlying Java object.
+    // Returns nullptr if conversion cannot be done.
+    static const hardware::hidl_memory* fromJava(JNIEnv* env,
+                                                 jobject jobj);
+
+    // Convert a hardware::hidl_memory object to its Java counterpart,
+    // android.os.HidlMemory.
+    // No duplication of file descriptors is performed.
+    // Returns nullptr if conversion cannot be done.
+    static jobject toJava(JNIEnv* env,
+                          const hardware::hidl_memory& cobj);
+
+    ~JHidlMemory();
+
+ private:
+    // We store an instance of type JHidlMemory attached to every Java object
+    // of type HidlMemory, for holding any native context we need. This instance
+    // will get deleted when finalize() is called on the Java object.
+    // This method either extracts the native object from the Java object, or
+    // attached a new one if it doesn't yet exist.
+    static JHidlMemory* getNativeContext(JNIEnv* env, jobject obj);
+
+    // Convert an android.os.HidlMemory object to its C++ counterpart,
+    // hardware::hidl_memory.
+    // No duplication of file descriptors is performed.
+    // IMPORTANT: caller is responsible to native_handle_delete() the handle of the
+    // returned object. This is due to an underlying limitation of the hidl_handle
+    // type, where ownership of the handle implies ownership of the fd and we don't
+    // want the latter.
+    // Returns nullptr if conversion cannot be done.
+    static std::unique_ptr<hardware::hidl_memory> javaToNative(JNIEnv* env,
+                                                               jobject jobj);
+
+    std::unique_ptr<hardware::hidl_memory> mObj;
+};
+
+int register_android_os_HidlMemory(JNIEnv* env);
+
+}  // namespace android
+
+#endif //ANDROID_OS_HIDL_MEMORY_H
diff --git a/core/jni/android_os_HwBlob.cpp b/core/jni/android_os_HwBlob.cpp
index e5b72ca..0fb2911 100644
--- a/core/jni/android_os_HwBlob.cpp
+++ b/core/jni/android_os_HwBlob.cpp
@@ -340,6 +340,14 @@
     return env->NewStringUTF(s->c_str());
 }
 
+static jlong JHwBlob_native_getFieldHandle(JNIEnv* env,
+                                           jobject thiz,
+                                           jlong offset) {
+    sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);
+
+    return reinterpret_cast<jlong>(blob->data()) + offset;
+}
+
 #define DEFINE_BLOB_ARRAY_COPIER(Suffix,Type,NewType)                          \
 static void JHwBlob_native_copyTo ## Suffix ## Array(                          \
         JNIEnv *env,                                                           \
@@ -593,6 +601,7 @@
     { "getFloat", "(J)F", (void *)JHwBlob_native_getFloat },
     { "getDouble", "(J)D", (void *)JHwBlob_native_getDouble },
     { "getString", "(J)Ljava/lang/String;", (void *)JHwBlob_native_getString },
+    { "getFieldHandle", "(J)J", (void*) JHwBlob_native_getFieldHandle},
 
     { "copyToBoolArray", "(J[ZI)V", (void *)JHwBlob_native_copyToBoolArray },
     { "copyToInt8Array", "(J[BI)V", (void *)JHwBlob_native_copyToInt8Array },
diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp
index f437a78..151dbfc 100644
--- a/core/jni/android_os_HwParcel.cpp
+++ b/core/jni/android_os_HwParcel.cpp
@@ -20,6 +20,7 @@
 
 #include "android_os_HwParcel.h"
 
+#include "android_os_HidlMemory.h"
 #include "android_os_HwBinder.h"
 #include "android_os_HwBlob.h"
 #include "android_os_NativeHandle.h"
@@ -27,6 +28,8 @@
 
 #include <nativehelper/JNIHelp.h>
 #include <android_runtime/AndroidRuntime.h>
+#include <hidl/HidlBinderSupport.h>
+#include <hidl/HidlSupport.h>
 #include <hidl/HidlTransportSupport.h>
 #include <hidl/Status.h>
 #include <nativehelper/ScopedLocalRef.h>
@@ -650,6 +653,36 @@
     signalExceptionForError(env, err);
 }
 
+static void JHwParcel_native_writeHidlMemory(
+    JNIEnv *env, jobject thiz, jobject jmem) {
+
+    if (jmem == nullptr) {
+        jniThrowException(env, "java/lang/NullPointerException", nullptr);
+        return;
+    }
+
+    status_t err = OK;
+
+    // Convert the Java object to its C++ counterpart.
+    const hardware::hidl_memory* cmem = JHidlMemory::fromJava(env, jmem);
+    if (cmem == nullptr) {
+        err = BAD_VALUE;
+    }
+
+    if (err == OK) {
+        // Write it to the parcel.
+        hardware::Parcel* parcel =
+            JHwParcel::GetNativeContext(env, thiz)->getParcel();
+
+        size_t parentHandle;
+        err = parcel->writeBuffer(cmem, sizeof(*cmem), &parentHandle);
+        if (err == OK) {
+            err = hardware::writeEmbeddedToParcel(*cmem, parcel, parentHandle, 0);
+        }
+    }
+    signalExceptionForError(env, err);
+}
+
 static jstring MakeStringObjFromHidlString(JNIEnv *env, const hidl_string &s) {
     String16 utf16String(s.c_str(), s.size());
 
@@ -877,6 +910,74 @@
     return objArray;
 }
 
+static status_t readEmbeddedHidlMemory(JNIEnv* env,
+                                       hardware::Parcel* parcel,
+                                       const hardware::hidl_memory& mem,
+                                       size_t parentHandle,
+                                       size_t parentOffset,
+                                       jobject* result) {
+    status_t err = hardware::readEmbeddedFromParcel(mem,
+                                                    *parcel,
+                                                    parentHandle,
+                                                    parentOffset);
+    if (err == OK) {
+        // Convert to Java.
+        *result = JHidlMemory::toJava(env, mem);
+        if (*result == nullptr) {
+            err = BAD_VALUE;
+        }
+    }
+    return err;
+}
+
+static jobject JHwParcel_native_readHidlMemory(
+        JNIEnv* env, jobject thiz) {
+    hardware::Parcel* parcel =
+            JHwParcel::GetNativeContext(env, thiz)->getParcel();
+
+    jobject result = nullptr;
+
+    const hardware::hidl_memory* mem;
+    size_t parentHandle;
+
+    status_t err = parcel->readBuffer(sizeof(*mem),
+                                      &parentHandle,
+                                      reinterpret_cast<const void**>(&mem));
+    if (err == OK) {
+        err = readEmbeddedHidlMemory(env,
+                                     parcel,
+                                     *mem,
+                                     parentHandle,
+                                     0,
+                                     &result);
+    }
+
+    signalExceptionForError(env, err);
+    return result;
+}
+
+static jobject JHwParcel_native_readEmbeddedHidlMemory(
+        JNIEnv* env,
+        jobject thiz,
+        jlong fieldHandle,
+        jlong parentHandle,
+        jlong offset) {
+    hardware::Parcel* parcel =
+            JHwParcel::GetNativeContext(env, thiz)->getParcel();
+
+    jobject result = nullptr;
+    const hardware::hidl_memory* mem =
+            reinterpret_cast<const hardware::hidl_memory*>(fieldHandle);
+    status_t err = readEmbeddedHidlMemory(env,
+                                          parcel,
+                                          *mem,
+                                          parentHandle,
+                                          offset,
+                                          &result);
+    signalExceptionForError(env, err);
+    return result;
+}
+
 static jobject JHwParcel_native_readStrongBinder(JNIEnv *env, jobject thiz) {
     hardware::Parcel *parcel =
         JHwParcel::GetNativeContext(env, thiz)->getParcel();
@@ -1075,6 +1176,14 @@
     { "release", "()V",
         (void *)JHwParcel_native_release },
 
+    {"writeHidlMemory", "(L" PACKAGE_PATH "/HidlMemory;)V",
+     (void*) JHwParcel_native_writeHidlMemory},
+
+    {"readHidlMemory", "()L" PACKAGE_PATH "/HidlMemory;",
+     (void*) JHwParcel_native_readHidlMemory},
+
+    {"readEmbeddedHidlMemory", "(JJJ)L" PACKAGE_PATH "/HidlMemory;",
+     (void*) JHwParcel_native_readEmbeddedHidlMemory},
 };
 
 namespace android {