diff options
| -rw-r--r-- | Android.bp | 4 | ||||
| -rw-r--r-- | api/system-current.txt | 16 | ||||
| -rw-r--r-- | api/test-current.txt | 16 | ||||
| -rw-r--r-- | core/java/android/os/HidlMemory.java | 141 | ||||
| -rw-r--r-- | core/java/android/os/HidlMemoryUtil.java | 215 | ||||
| -rw-r--r-- | core/java/android/os/HwBlob.java | 22 | ||||
| -rw-r--r-- | core/java/android/os/HwParcel.java | 41 | ||||
| -rw-r--r-- | core/jni/Android.bp | 1 | ||||
| -rw-r--r-- | core/jni/AndroidRuntime.cpp | 2 | ||||
| -rw-r--r-- | core/jni/android_os_HidlMemory.cpp | 162 | ||||
| -rw-r--r-- | core/jni/android_os_HidlMemory.h | 71 | ||||
| -rw-r--r-- | core/jni/android_os_HwBlob.cpp | 9 | ||||
| -rw-r--r-- | core/jni/android_os_HwParcel.cpp | 109 |
13 files changed, 809 insertions, 0 deletions
diff --git a/Android.bp b/Android.bp index 180e1d3c5dea..2a820cb89355 100644 --- a/Android.bp +++ b/Android.bp @@ -871,11 +871,13 @@ java_library { 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", @@ -1406,8 +1408,10 @@ droidstubs { 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 f74b74318180..f3cdac3d20ee 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -5489,6 +5489,17 @@ package android.os { 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 @@ package android.os { 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 @@ package android.os { 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 @@ package android.os { 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 @@ package android.os { 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 95b0caea0d43..a8bd9bed9516 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -1808,6 +1808,17 @@ package android.os { 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 @@ package android.os { 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 @@ package android.os { 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 @@ package android.os { 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 @@ package android.os { 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 000000000000..aeb65892a425 --- /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/HidlMemoryUtil.java b/core/java/android/os/HidlMemoryUtil.java new file mode 100644 index 000000000000..b08822ddd751 --- /dev/null +++ b/core/java/android/os/HidlMemoryUtil.java @@ -0,0 +1,215 @@ +/* + * 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 static android.system.OsConstants.MAP_SHARED; +import static android.system.OsConstants.PROT_READ; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; +import android.annotation.TestApi; +import android.system.ErrnoException; +import android.system.Os; +import android.util.Log; + +import com.android.internal.util.Preconditions; + +import java.nio.ByteBuffer; +import java.nio.DirectByteBuffer; +import java.util.ArrayList; +import java.util.List; + +/** + * Provides utilities for dealing with HidlMemory. + * + * @hide + */ +public final class HidlMemoryUtil { + static private final String TAG = "HidlMemoryUtil"; + + private HidlMemoryUtil() { + } + + /** + * Copies a byte-array into a new Ashmem region and return it as HidlMemory. + * The returned instance owns the underlying file descriptors, and the client should generally + * call close on it when no longer in use (or otherwise, when the object gets destroyed it will + * be closed). + * + * @param input The input byte array. + * @return A HidlMemory instance, containing a copy of the input. + */ + public static @NonNull + HidlMemory byteArrayToHidlMemory(@NonNull byte[] input) { + return byteArrayToHidlMemory(input, null); + } + + /** + * Copies a byte-array into a new Ashmem region and return it as HidlMemory. + * The returned instance owns the underlying file descriptors, and the client should generally + * call close on it when no longer in use (or otherwise, when the object gets destroyed it will + * be closed). + * + * @param input The input byte array. + * @param name An optional name for the ashmem region. + * @return A HidlMemory instance, containing a copy of the input. + */ + public static @NonNull + HidlMemory byteArrayToHidlMemory(@NonNull byte[] input, @Nullable String name) { + Preconditions.checkNotNull(input); + + if (input.length == 0) { + return new HidlMemory("ashmem", 0, null); + } + + try { + SharedMemory shmem = SharedMemory.create(name != null ? name : "", input.length); + ByteBuffer buffer = shmem.mapReadWrite(); + buffer.put(input); + shmem.unmap(buffer); + NativeHandle handle = new NativeHandle(shmem.getFileDescriptor(), true); + return new HidlMemory("ashmem", input.length, handle); + } catch (ErrnoException e) { + throw new RuntimeException(e); + } + } + + /** + * Copies a byte list into a new Ashmem region and return it as HidlMemory. + * The returned instance owns the underlying file descriptors, and the client should generally + * call close on it when no longer in use (or otherwise, when the object gets destroyed it will + * be closed). + * + * @param input The input byte list. + * @return A HidlMemory instance, containing a copy of the input. + */ + public static @NonNull + HidlMemory byteListToHidlMemory(@NonNull List<Byte> input) { + return byteListToHidlMemory(input, null); + } + + /** + * Copies a byte list into a new Ashmem region and return it as HidlMemory. + * The returned instance owns the underlying file descriptors, and the client should generally + * call close on it when no longer in use (or otherwise, when the object gets destroyed it will + * be closed). + * + * @param input The input byte list. + * @param name An optional name for the ashmem region. + * @return A HidlMemory instance, containing a copy of the input. + */ + public static @NonNull + HidlMemory byteListToHidlMemory(@NonNull List<Byte> input, @Nullable String name) { + Preconditions.checkNotNull(input); + + if (input.isEmpty()) { + return new HidlMemory("ashmem", 0, null); + } + + try { + SharedMemory shmem = SharedMemory.create(name != null ? name : "", input.size()); + ByteBuffer buffer = shmem.mapReadWrite(); + for (Byte b : input) { + buffer.put(b); + } + shmem.unmap(buffer); + NativeHandle handle = new NativeHandle(shmem.getFileDescriptor(), true); + return new HidlMemory("ashmem", input.size(), handle); + } catch (ErrnoException e) { + throw new RuntimeException(e); + } + } + + /** + * Copies all data from a HidlMemory instance into a byte array. + * + * @param mem The HidlMemory instance. Must be of name "ashmem" and of size that doesn't exceed + * {@link Integer#MAX_VALUE}. + * @return A byte array, containing a copy of the input. + */ + public static @NonNull + byte[] hidlMemoryToByteArray(@NonNull HidlMemory mem) { + Preconditions.checkNotNull(mem); + Preconditions.checkArgumentInRange(mem.getSize(), 0L, (long) Integer.MAX_VALUE, + "Memory size"); + Preconditions.checkArgument(mem.getSize() == 0 || mem.getName().equals("ashmem"), + "Unsupported memory type: %s", mem.getName()); + + if (mem.getSize() == 0) { + return new byte[0]; + } + + ByteBuffer buffer = getBuffer(mem); + byte[] result = new byte[buffer.remaining()]; + buffer.get(result); + return result; + } + + /** + * Copies all data from a HidlMemory instance into a byte list. + * + * @param mem The HidlMemory instance. Must be of name "ashmem" and of size that doesn't exceed + * {@link Integer#MAX_VALUE}. + * @return A byte list, containing a copy of the input. + */ + @SuppressLint("ConcreteCollection") + public static @NonNull + ArrayList<Byte> hidlMemoryToByteList(@NonNull HidlMemory mem) { + Preconditions.checkNotNull(mem); + Preconditions.checkArgumentInRange(mem.getSize(), 0L, (long) Integer.MAX_VALUE, + "Memory size"); + Preconditions.checkArgument(mem.getSize() == 0 || mem.getName().equals("ashmem"), + "Unsupported memory type: %s", mem.getName()); + + if (mem.getSize() == 0) { + return new ArrayList<>(); + } + + ByteBuffer buffer = getBuffer(mem); + + ArrayList<Byte> result = new ArrayList<>(buffer.remaining()); + while (buffer.hasRemaining()) { + result.add(buffer.get()); + } + return result; + } + + private static ByteBuffer getBuffer(@NonNull HidlMemory mem) { + try { + final int size = (int) mem.getSize(); + + if (size == 0) { + return ByteBuffer.wrap(new byte[0]); + } + + NativeHandle handle = mem.getHandle(); + + final long address = Os.mmap(0, size, PROT_READ, MAP_SHARED, handle.getFileDescriptor(), + 0); + return new DirectByteBuffer(size, address, handle.getFileDescriptor(), () -> { + try { + Os.munmap(address, size); + } catch (ErrnoException e) { + Log.wtf(TAG, e); + } + }, true); + } catch (ErrnoException e) { + throw new RuntimeException(e); + } + } +} diff --git a/core/java/android/os/HwBlob.java b/core/java/android/os/HwBlob.java index 2c453bfc4d42..154227b2a786 100644 --- a/core/java/android/os/HwBlob.java +++ b/core/java/android/os/HwBlob.java @@ -92,6 +92,14 @@ public class HwBlob { * @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 class HwBlob { 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 5e8929c6c999..9786f16bebaa 100644 --- a/core/java/android/os/HwParcel.java +++ b/core/java/android/os/HwParcel.java @@ -324,6 +324,15 @@ public class HwParcel { 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 class HwParcel { 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 ea1094913a26..51f8fcc264dc 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -125,6 +125,7 @@ cc_library_shared { "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 32d62fe2cdba..378e125a3a3e 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -147,6 +147,7 @@ extern int register_android_os_SystemClock(JNIEnv* env); 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 @@ static const RegJNIRec gRegJNI[] = { 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 000000000000..69e48184c0ad --- /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 000000000000..993a1326f0d3 --- /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 e5b72caf2792..0fb29111d043 100644 --- a/core/jni/android_os_HwBlob.cpp +++ b/core/jni/android_os_HwBlob.cpp @@ -340,6 +340,14 @@ static jstring JHwBlob_native_getString( 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 @@ static JNINativeMethod gMethods[] = { { "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 f437a78e35f8..151dbfce7af3 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 @@ static void JHwParcel_native_writeStrongBinder( 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 @@ static jobjectArray JHwParcel_native_readNativeHandleVector( 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 @@ static JNINativeMethod gMethods[] = { { "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 { |