diff --git a/Android.bp b/Android.bp index 8ac027fa620f6..ae0acade27371 100644 --- a/Android.bp +++ b/Android.bp @@ -898,6 +898,7 @@ java_library { "core/java/android/os/IHwInterface.java", "core/java/android/os/DeadObjectException.java", "core/java/android/os/DeadSystemException.java", + "core/java/android/os/NativeHandle.java", "core/java/android/os/RemoteException.java", "core/java/android/util/AndroidException.java", ], @@ -1444,6 +1445,7 @@ droiddoc { "core/java/android/os/IHwInterface.java", "core/java/android/os/DeadObjectException.java", "core/java/android/os/DeadSystemException.java", + "core/java/android/os/NativeHandle.java", "core/java/android/os/RemoteException.java", "core/java/android/util/AndroidException.java", ], diff --git a/api/system-current.txt b/api/system-current.txt index a8e8dcb3b4222..859ee98fc1219 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -3726,6 +3726,7 @@ package android.os { method public final void putInt64Array(long, long[]); method public final void putInt8(long, byte); method public final void putInt8Array(long, byte[]); + method public final void putNativeHandle(long, android.os.NativeHandle); method public final void putString(long, java.lang.String); method public static java.lang.Boolean[] wrapArray(boolean[]); method public static java.lang.Long[] wrapArray(long[]); @@ -3745,6 +3746,7 @@ package android.os { method public final double readDouble(); method public final java.util.ArrayList readDoubleVector(); method public final android.os.HwBlob readEmbeddedBuffer(long, long, long, boolean); + method public final android.os.NativeHandle readEmbeddedNativeHandle(long, long); method public final float readFloat(); method public final java.util.ArrayList readFloatVector(); method public final short readInt16(); @@ -3755,6 +3757,8 @@ package android.os { method public final java.util.ArrayList readInt64Vector(); method public final byte readInt8(); method public final java.util.ArrayList readInt8Vector(); + method public final android.os.NativeHandle readNativeHandle(); + method public final java.util.ArrayList readNativeHandleVector(); method public final java.lang.String readString(); method public final java.util.ArrayList readStringVector(); method public final android.os.IHwBinder readStrongBinder(); @@ -3778,6 +3782,8 @@ package android.os { method public final void writeInt8(byte); method public final void writeInt8Vector(java.util.ArrayList); method public final void writeInterfaceToken(java.lang.String); + method public final void writeNativeHandle(android.os.NativeHandle); + method public final void writeNativeHandleVector(java.util.ArrayList); method public final void writeStatus(int); method public final void writeString(java.lang.String); method public final void writeStringVector(java.util.ArrayList); @@ -3822,6 +3828,18 @@ package android.os { field public static final android.os.Parcelable.Creator CREATOR; } + public final class NativeHandle implements java.io.Closeable { + ctor public NativeHandle(); + ctor public NativeHandle(java.io.FileDescriptor, boolean); + ctor public NativeHandle(java.io.FileDescriptor[], int[], boolean); + method public void close() throws java.io.IOException; + method public android.os.NativeHandle dup() throws java.io.IOException; + method public java.io.FileDescriptor getFileDescriptor(); + method public java.io.FileDescriptor[] getFileDescriptors(); + method public int[] getInts(); + method public boolean hasSingleFileDescriptor(); + } + public final class PowerManager { method public void userActivity(long, int, int); field public static final int USER_ACTIVITY_EVENT_ACCESSIBILITY = 3; // 0x3 diff --git a/core/java/android/os/HwBlob.java b/core/java/android/os/HwBlob.java index 405651e992a3b..6a5bb1c0a988c 100644 --- a/core/java/android/os/HwBlob.java +++ b/core/java/android/os/HwBlob.java @@ -232,6 +232,14 @@ public HwBlob(int size) { * @throws IndexOutOfBoundsException when [offset, offset + sizeof(jstring)] is out of range */ public native final void putString(long offset, String x); + /** + * Writes a native handle (without duplicating the underlying file descriptors) at an offset. + * + * @param offset location to write value + * @param x a {@link NativeHandle} instance to write + * @throws IndexOutOfBoundsException when [offset, offset + sizeof(jobject)] is out of range + */ + public native final void putNativeHandle(long offset, NativeHandle x); /** * Put a boolean array contiguously at an offset in the blob. diff --git a/core/java/android/os/HwParcel.java b/core/java/android/os/HwParcel.java index 0eb62c95ed715..7a51db2dc5f99 100644 --- a/core/java/android/os/HwParcel.java +++ b/core/java/android/os/HwParcel.java @@ -115,6 +115,13 @@ public HwParcel() { * @param val to write */ public native final void writeString(String val); + /** + * Writes a native handle (without duplicating the underlying + * file descriptors) to the end of the parcel. + * + * @param val to write + */ + public native final void writeNativeHandle(NativeHandle val); /** * Writes an array of boolean values to the end of the parcel. @@ -159,6 +166,11 @@ public HwParcel() { * @param val to write */ private native final void writeStringVector(String[] val); + /** + * Writes an array of native handles to the end of the parcel. + * @param val array of {@link NativeHandle} objects to write + */ + private native final void writeNativeHandleVector(NativeHandle[] val); /** * Helper method to write a list of Booleans to val. @@ -266,6 +278,14 @@ public final void writeStringVector(ArrayList val) { writeStringVector(val.toArray(new String[val.size()])); } + /** + * Helper method to write a list of native handles to the end of the parcel. + * @param val list of {@link NativeHandle} objects to write + */ + public final void writeNativeHandleVector(ArrayList val) { + writeNativeHandleVector(val.toArray(new NativeHandle[val.size()])); + } + /** * Write a hwbinder object to the end of the parcel. * @param binder value to write @@ -328,6 +348,30 @@ public final void writeStringVector(ArrayList val) { * @throws IllegalArgumentException if the parcel has no more data */ public native final String readString(); + /** + * Reads a native handle (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 NativeHandle#dup()}. + * + * @return a {@link NativeHandle} instance parsed from the parcel + * @throws IllegalArgumentException if the parcel has no more data + */ + public native final NativeHandle readNativeHandle(); + /** + * Reads an embedded native handle (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 NativeHandle#dup()}. You + * do not need to call close on the NativeHandle returned from this. + * + * @param parentHandle handle from which to read the embedded object + * @param offset offset into parent + * @return a {@link NativeHandle} instance parsed from the parcel + * @throws IllegalArgumentException if the parcel has no more data + */ + public native final NativeHandle readEmbeddedNativeHandle( + long parentHandle, long offset); /** * Reads an array of boolean values from the parcel. @@ -377,6 +421,12 @@ public final void writeStringVector(ArrayList val) { * @throws IllegalArgumentException if the parcel has no more data */ private native final String[] readStringVectorAsArray(); + /** + * Reads an array of native handles from the parcel. + * @return array of {@link NativeHandle} objects + * @throws IllegalArgumentException if the parcel has no more data + */ + private native final NativeHandle[] readNativeHandleAsArray(); /** * Convenience method to read a Boolean vector as an ArrayList. @@ -464,6 +514,15 @@ public final ArrayList readStringVector() { return new ArrayList(Arrays.asList(readStringVectorAsArray())); } + /** + * Convenience method to read a vector of native handles as an ArrayList. + * @return array of {@link NativeHandle} objects. + * @throws IllegalArgumentException if the parcel has no more data + */ + public final ArrayList readNativeHandleVector() { + return new ArrayList(Arrays.asList(readNativeHandleAsArray())); + } + /** * Reads a strong binder value from the parcel. * @return binder object read from parcel or null if no binder can be read diff --git a/core/java/android/os/NativeHandle.java b/core/java/android/os/NativeHandle.java new file mode 100644 index 0000000000000..f3dc5c8bc309b --- /dev/null +++ b/core/java/android/os/NativeHandle.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2018 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.NonNull; +import android.annotation.SystemApi; +import android.system.ErrnoException; +import android.system.Os; + +import java.io.Closeable; +import java.io.FileDescriptor; + +/** + * Collection representing a set of open file descriptors and an opaque data stream. + * + * @hide + */ +@SystemApi +public final class NativeHandle implements Closeable { + // whether this object owns mFds + private boolean mOwn = false; + private FileDescriptor[] mFds; + private int[] mInts; + + /** + * Constructs a {@link NativeHandle} object containing + * zero file descriptors and an empty data stream. + */ + public NativeHandle() { + this(new FileDescriptor[0], new int[0], false); + } + + /** + * Constructs a {@link NativeHandle} object containing the given + * {@link FileDescriptor} object and an empty data stream. + */ + public NativeHandle(@NonNull FileDescriptor descriptor, boolean own) { + this(new FileDescriptor[] {descriptor}, new int[0], own); + } + + /** + * Convenience method for creating a list of file descriptors. + * + * @hide + */ + private static FileDescriptor[] createFileDescriptorArray(@NonNull int[] fds) { + FileDescriptor[] list = new FileDescriptor[fds.length]; + for (int i = 0; i < fds.length; i++) { + FileDescriptor descriptor = new FileDescriptor(); + descriptor.setInt$(fds[i]); + list[i] = descriptor; + } + return list; + } + + /** + * Convenience method for instantiating a {@link NativeHandle} from JNI. It does + * not take ownership of the int[] params. It does not dupe the FileDescriptors. + * + * @hide + */ + private NativeHandle(@NonNull int[] fds, @NonNull int[] ints, boolean own) { + this(createFileDescriptorArray(fds), ints, own); + } + + /** + * Instantiate an opaque {@link NativeHandle} from fds and integers. + * + * @param own whether the fds are owned by this object and should be closed + */ + public NativeHandle(@NonNull FileDescriptor[] fds, @NonNull int[] ints, boolean own) { + mFds = fds.clone(); + mInts = ints.clone(); + mOwn = own; + } + + /** + * Returns whether this {@link NativeHandle} object contains a single file + * descriptor and nothing else. + * + * @return a boolean value + */ + public boolean hasSingleFileDescriptor() { + return mFds.length == 1 && mInts.length == 0; + } + + /** + * Explicitly duplicate NativeHandle (this dups all file descritptors). + */ + public NativeHandle dup() throws java.io.IOException { + FileDescriptor[] fds = new FileDescriptor[mFds.length]; + try { + for (int i = 0; i < mFds.length; i++) { + fds[i] = Os.dup(mFds[i]); + } + } catch (ErrnoException e) { + e.rethrowAsIOException(); + } + return new NativeHandle(fds, mInts, true /*own*/); + } + + /** + * Closes the file descriptors if they are owned by this object. + * + * This also invalidates the object. + */ + @Override + public void close() throws java.io.IOException { + if (!mOwn) { + return; + } + + try { + for (FileDescriptor fd : mFds) { + Os.close(fd); + } + } catch (ErrnoException e) { + e.rethrowAsIOException(); + } + + mOwn = false; + mFds = null; + mInts = null; + } + + /** + * Returns the underlying lone file descriptor. + * + * @return a {@link FileDescriptor} object + * @throws IllegalStateException if this object contains either zero or + * more than one file descriptor, or a non-empty data stream. + */ + public FileDescriptor getFileDescriptor() { + if (!hasSingleFileDescriptor()) { + throw new IllegalStateException( + "NativeHandle is not single file descriptor. Contents must" + + " be retreived through getFileDescriptors and getInts."); + } + + return mFds[0]; + } + + /** + * Convenience method for fetching this object's file descriptors from JNI. + * @return a mutable copy of the underlying file descriptors (as an int[]) + * + * @hide + */ + private int[] getFdsAsIntArray() { + int numFds = mFds.length; + int[] fds = new int[numFds]; + + for (int i = 0; i < numFds; i++) { + fds[i] = mFds[i].getInt$(); + } + + return fds; + } + + /** + * Fetch file descriptors. + * + * @return the fds. + */ + public FileDescriptor[] getFileDescriptors() { + return mFds; + } + + /** + * Fetch opaque ints. Note: This object retains ownership of the data. + * + * @return the opaque data stream. + */ + public int[] getInts() { + return mInts; + } +} diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 88ffb3f87e5d7..494a957410908 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -93,6 +93,7 @@ cc_library_shared { "android_os_HwBlob.cpp", "android_os_HwParcel.cpp", "android_os_HwRemoteBinder.cpp", + "android_os_NativeHandle.cpp", "android_os_MemoryFile.cpp", "android_os_MessageQueue.cpp", "android_os_Parcel.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index d059253b116bb..f7f10c49fbae3 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -165,6 +165,7 @@ extern int register_android_os_HwBinder(JNIEnv *env); extern int register_android_os_HwBlob(JNIEnv *env); extern int register_android_os_HwParcel(JNIEnv *env); extern int register_android_os_HwRemoteBinder(JNIEnv *env); +extern int register_android_os_NativeHandle(JNIEnv *env); extern int register_android_os_MessageQueue(JNIEnv* env); extern int register_android_os_Parcel(JNIEnv* env); extern int register_android_os_SELinux(JNIEnv* env); @@ -1345,6 +1346,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_os_HwBlob), REG_JNI(register_android_os_HwParcel), REG_JNI(register_android_os_HwRemoteBinder), + REG_JNI(register_android_os_NativeHandle), REG_JNI(register_android_os_VintfObject), REG_JNI(register_android_os_VintfRuntimeInfo), REG_JNI(register_android_nio_utils), diff --git a/core/jni/android_os_HwBlob.cpp b/core/jni/android_os_HwBlob.cpp index bb916d2431c5b..cb55618c685c0 100644 --- a/core/jni/android_os_HwBlob.cpp +++ b/core/jni/android_os_HwBlob.cpp @@ -21,6 +21,7 @@ #include "android_os_HwBlob.h" #include "android_os_HwParcel.h" +#include "android_os_NativeHandle.h" #include #include @@ -31,6 +32,7 @@ #include "core_jni_helpers.h" using android::AndroidRuntime; +using android::hardware::hidl_handle; using android::hardware::hidl_string; #define PACKAGE_PATH "android/os" @@ -82,6 +84,7 @@ sp JHwBlob::GetNativeContext(JNIEnv *env, jobject thiz) { JHwBlob::JHwBlob(JNIEnv *env, jobject thiz, size_t size) : mBuffer(nullptr), mSize(size), + mType(BlobType::GENERIC), mOwnsBuffer(true), mHandle(0) { if (size > 0) { @@ -159,6 +162,15 @@ size_t JHwBlob::size() const { return mSize; } +void JHwBlob::specializeBlobTo(BlobType type) { + CHECK_EQ(static_cast(mType), static_cast(BlobType::GENERIC)); + mType = type; +} + +JHwBlob::BlobType JHwBlob::type() const { + return mType; +} + status_t JHwBlob::putBlob(size_t offset, const sp &blob) { size_t index = mSubBlobs.add(); BlobInfo *info = &mSubBlobs.editItemAt(index); @@ -172,42 +184,52 @@ status_t JHwBlob::putBlob(size_t offset, const sp &blob) { } status_t JHwBlob::writeToParcel(hardware::Parcel *parcel) const { - size_t handle; + CHECK_EQ(static_cast(mType), static_cast(BlobType::GENERIC)); + + size_t handle = 0; status_t err = parcel->writeBuffer(data(), size(), &handle); if (err != OK) { return err; } - for (size_t i = 0; i < mSubBlobs.size(); ++i) { - const BlobInfo &info = mSubBlobs[i]; - - err = info.mBlob->writeEmbeddedToParcel(parcel, handle, info.mOffset); - - if (err != OK) { - return err; - } - } - - return OK; + return writeSubBlobsToParcel(parcel, handle); } status_t JHwBlob::writeEmbeddedToParcel( hardware::Parcel *parcel, size_t parentHandle, size_t parentOffset) const { - size_t handle; - status_t err = parcel->writeEmbeddedBuffer( - data(), size(), &handle, parentHandle, parentOffset); + size_t handle = 0; + status_t err = OK; + + switch (mType) { + case BlobType::GENERIC: { + err = parcel->writeEmbeddedBuffer(data(), size(), &handle, parentHandle, parentOffset); + break; + } + case BlobType::NATIVE_HANDLE: { + err = parcel->writeEmbeddedNativeHandle( + static_cast(data()), parentHandle, parentOffset); + + CHECK(mSubBlobs.empty()); + break; + } + default: { err = INVALID_OPERATION; } + } if (err != OK) { return err; } + return writeSubBlobsToParcel(parcel, handle); +} + +status_t JHwBlob::writeSubBlobsToParcel(hardware::Parcel *parcel, + size_t parentHandle) const { for (size_t i = 0; i < mSubBlobs.size(); ++i) { const BlobInfo &info = mSubBlobs[i]; - - err = info.mBlob->writeEmbeddedToParcel(parcel, handle, info.mOffset); + status_t err = info.mBlob->writeEmbeddedToParcel(parcel, parentHandle, info.mOffset); if (err != OK) { return err; @@ -252,7 +274,7 @@ static void releaseNativeContext(void *nativeContext) { } } -static jlong JHwBlob_native_init(JNIEnv *env) { +static jlong JHwBlob_native_init(JNIEnv *env, jclass /*clazz*/) { JHwBlob::InitClass(env); return reinterpret_cast(&releaseNativeContext); @@ -456,6 +478,31 @@ static void JHwBlob_native_putString( blob->putBlob(offset + hidl_string::kOffsetOfBuffer, subBlob); } +static void JHwBlob_native_putNativeHandle(JNIEnv *env, jobject thiz, + jlong offset, jobject jHandle) { + std::unique_ptr nativeHandle( + JNativeHandle::MakeCppNativeHandle(env, jHandle, nullptr /* storage */), + native_handle_delete); + + size_t size = 0; + if (nativeHandle != nullptr) { + size = sizeof(native_handle_t) + nativeHandle->numFds * sizeof(int) + + nativeHandle->numInts * sizeof(int); + } + + ScopedLocalRef subBlobObj(env, JHwBlob::NewObject(env, size)); + sp subBlob = JHwBlob::GetNativeContext(env, subBlobObj.get()); + subBlob->specializeBlobTo(JHwBlob::BlobType::NATIVE_HANDLE); + subBlob->write(0 /* offset */, nativeHandle.get(), size); + + hidl_handle cppHandle; + cppHandle.setTo(static_cast(subBlob->data()), false /* shouldOwn */); + + sp blob = JHwBlob::GetNativeContext(env, thiz); + blob->write(offset, &cppHandle, sizeof(cppHandle)); + blob->putBlob(offset + hidl_handle::kOffsetOfNativeHandle, subBlob); +} + #define DEFINE_BLOB_ARRAY_PUTTER(Suffix,Type,NewType) \ static void JHwBlob_native_put ## Suffix ## Array( \ JNIEnv *env, jobject thiz, jlong offset, Type ## Array array) { \ @@ -563,6 +610,8 @@ static JNINativeMethod gMethods[] = { { "putFloat", "(JF)V", (void *)JHwBlob_native_putFloat }, { "putDouble", "(JD)V", (void *)JHwBlob_native_putDouble }, { "putString", "(JLjava/lang/String;)V", (void *)JHwBlob_native_putString }, + { "putNativeHandle", "(JL" PACKAGE_PATH "/NativeHandle;)V", + (void*)JHwBlob_native_putNativeHandle }, { "putBoolArray", "(J[Z)V", (void *)JHwBlob_native_putBoolArray }, { "putInt8Array", "(J[B)V", (void *)JHwBlob_native_putInt8Array }, diff --git a/core/jni/android_os_HwBlob.h b/core/jni/android_os_HwBlob.h index 6b1db639ac195..69a1b165af9ac 100644 --- a/core/jni/android_os_HwBlob.h +++ b/core/jni/android_os_HwBlob.h @@ -27,6 +27,11 @@ namespace android { struct JHwBlob : public RefBase { + enum class BlobType { + GENERIC, + NATIVE_HANDLE, + }; + static void InitClass(JNIEnv *env); static sp SetNativeContext( @@ -54,6 +59,9 @@ struct JHwBlob : public RefBase { size_t size() const; + void specializeBlobTo(BlobType type); + BlobType type() const; + status_t putBlob(size_t offset, const sp &blob); status_t writeToParcel(hardware::Parcel *parcel) const; @@ -74,12 +82,15 @@ struct JHwBlob : public RefBase { void *mBuffer; size_t mSize; + BlobType mType; bool mOwnsBuffer; size_t mHandle; Vector mSubBlobs; + status_t writeSubBlobsToParcel(hardware::Parcel *parcel, size_t parentHandle) const; + DISALLOW_COPY_AND_ASSIGN(JHwBlob); }; diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp index 061349aee96fc..7221ca11cc00a 100644 --- a/core/jni/android_os_HwParcel.cpp +++ b/core/jni/android_os_HwParcel.cpp @@ -22,6 +22,7 @@ #include "android_os_HwBinder.h" #include "android_os_HwBlob.h" +#include "android_os_NativeHandle.h" #include "android_os_HwRemoteBinder.h" #include @@ -34,6 +35,7 @@ using android::AndroidRuntime; +using ::android::hardware::hidl_handle; using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; @@ -436,6 +438,18 @@ static void JHwParcel_native_writeString( signalExceptionForError(env, err); } +static void JHwParcel_native_writeNativeHandle(JNIEnv *env, jobject thiz, jobject valObj) { + sp impl = JHwParcel::GetNativeContext(env, thiz); + + EphemeralStorage *storage = impl->getStorage(); + native_handle_t *handle = JNativeHandle::MakeCppNativeHandle(env, valObj, storage); + + hardware::Parcel *parcel = impl->getParcel(); + status_t err = parcel->writeNativeHandleNoDup(handle); + + signalExceptionForError(env, err); +} + #define DEFINE_PARCEL_VECTOR_WRITER(Suffix,Type) \ static void JHwParcel_native_write ## Suffix ## Vector( \ JNIEnv *env, jobject thiz, Type ## Array valObj) { \ @@ -524,12 +538,96 @@ static void JHwParcel_native_writeBoolVector( signalExceptionForError(env, err); } +template +static void WriteHidlVector(JNIEnv *env, jobject thiz, const hidl_vec &vec) { + hardware::Parcel *parcel = JHwParcel::GetNativeContext(env, thiz)->getParcel(); + + size_t parentHandle; + status_t err = parcel->writeBuffer(&vec, sizeof(vec), &parentHandle); + + if (err == OK) { + size_t childHandle; + err = ::android::hardware::writeEmbeddedToParcel( + vec, + parcel, + parentHandle, + 0 /* parentOffset */, + &childHandle); + + for (size_t i = 0; (err == OK) && (i < vec.size()); ++i) { + err = ::android::hardware::writeEmbeddedToParcel( + vec[i], + parcel, + childHandle, + i * sizeof(T)); + } + } + + signalExceptionForError(env, err); +} + +static void JHwParcel_native_writeStringVector( + JNIEnv *env, jobject thiz, jobjectArray arrayObj) { + if (arrayObj == nullptr) { + jniThrowException(env, "java/lang/NullPointerException", nullptr); + return; + } + + sp impl = JHwParcel::GetNativeContext(env, thiz); + EphemeralStorage *storage = impl->getStorage(); + + void *vecPtr = storage->allocTemporaryStorage(sizeof(hidl_vec)); + hidl_vec *vec = new (vecPtr) hidl_vec(); + + jsize len = env->GetArrayLength(arrayObj); + hidl_string *strings = storage->allocStringArray(len); + vec->setToExternal(strings, len, false /* shouldOwn */); + + for (jsize i = 0; i < len; ++i) { + ScopedLocalRef stringObj(env, (jstring) env->GetObjectArrayElement(arrayObj, i)); + + const hidl_string *s = storage->allocTemporaryString(env, stringObj.get()); + strings[i].setToExternal(s->c_str(), s->size()); + } + + WriteHidlVector(env, thiz, *vec); +} + +static void JHwParcel_native_writeNativeHandleVector( + JNIEnv *env, jobject thiz, jobjectArray jHandleArray) { + if (jHandleArray == nullptr) { + jniThrowException(env, "java/lang/NullPointerException", nullptr); + return; + } + + sp impl = JHwParcel::GetNativeContext(env, thiz); + EphemeralStorage *storage = impl->getStorage(); + + void *vecPtr = storage->allocTemporaryStorage(sizeof(hidl_vec)); + hidl_vec *vec = new (vecPtr) hidl_vec(); + + jsize len = env->GetArrayLength(jHandleArray); + hidl_handle *handles = static_cast( + storage->allocTemporaryStorage(len * sizeof(hidl_handle))); + + vec->setToExternal(handles, len, false /* shouldOwn */); + for (jsize i = 0; i < len; i++) { + ScopedLocalRef jHandle(env, env->GetObjectArrayElement(jHandleArray, i)); + + native_handle_t* handle = JNativeHandle::MakeCppNativeHandle(env, jHandle.get(), storage); + + new (&(handles[i])) hidl_handle(); + handles[i].setTo(handle, false /* shouldOwn */); + } + + WriteHidlVector(env, thiz, *vec); +} + static void JHwParcel_native_writeStrongBinder( JNIEnv *env, jobject thiz, jobject binderObj) { sp binder; if (binderObj != NULL) { - ScopedLocalRef hwBinderKlass( - env, FindClassOrDie(env, PACKAGE_PATH "/HwBinder")); + ScopedLocalRef hwBinderKlass(env, FindClassOrDie(env, PACKAGE_PATH "/HwBinder")); ScopedLocalRef hwRemoteBinderKlass( env, FindClassOrDie(env, PACKAGE_PATH "/HwRemoteBinder")); @@ -587,6 +685,37 @@ static jstring JHwParcel_native_readString(JNIEnv *env, jobject thiz) { return MakeStringObjFromHidlString(env, *s); } +static jobject ReadNativeHandle(JNIEnv *env, jobject thiz, jboolean embedded, + jlong parentHandle, jlong offset) { + hardware::Parcel *parcel = + JHwParcel::GetNativeContext(env, thiz)->getParcel(); + + const native_handle_t *handle = nullptr; + status_t err = OK; + + if (embedded) { + err = parcel->readNullableEmbeddedNativeHandle(parentHandle, offset, &handle); + } else { + err = parcel->readNullableNativeHandleNoDup(&handle); + } + + if (err != OK) { + signalExceptionForError(env, err); + return nullptr; + } + + return JNativeHandle::MakeJavaNativeHandleObj(env, handle); +} + +static jobject JHwParcel_native_readNativeHandle(JNIEnv *env, jobject thiz) { + return ReadNativeHandle(env, thiz, false /*embedded*/, 0L /*parentHandle*/, 0L /*offset*/); +} + +static jobject JHwParcel_native_readEmbeddedNativeHandle( + JNIEnv *env, jobject thiz, jlong parentHandle, jlong offset) { + return ReadNativeHandle(env, thiz, true /*embedded*/, parentHandle, offset); +} + #define DEFINE_PARCEL_VECTOR_READER(Suffix,Type,NewType) \ static Type ## Array JHwParcel_native_read ## Suffix ## Vector( \ JNIEnv *env, jobject thiz) { \ @@ -630,10 +759,8 @@ DEFINE_PARCEL_VECTOR_READER(Int64,jlong,Long) DEFINE_PARCEL_VECTOR_READER(Float,jfloat,Float) DEFINE_PARCEL_VECTOR_READER(Double,jdouble,Double) -static jbooleanArray JHwParcel_native_readBoolVector( - JNIEnv *env, jobject thiz) { - hardware::Parcel *parcel = - JHwParcel::GetNativeContext(env, thiz)->getParcel(); +static jbooleanArray JHwParcel_native_readBoolVector(JNIEnv *env, jobject thiz) { + hardware::Parcel *parcel = JHwParcel::GetNativeContext(env, thiz)->getParcel(); size_t parentHandle; @@ -692,101 +819,62 @@ static jobjectArray MakeStringArray( return arrayObj; } -static jobjectArray JHwParcel_native_readStringVector( - JNIEnv *env, jobject thiz) { - typedef hidl_vec string_vec; +template +static const hidl_vec *ReadHidlVector(JNIEnv *env, jobject thiz) { + const hidl_vec *vec; - hardware::Parcel *parcel = - JHwParcel::GetNativeContext(env, thiz)->getParcel(); + hardware::Parcel *parcel = JHwParcel::GetNativeContext(env, thiz)->getParcel(); size_t parentHandle; + status_t err = parcel->readBuffer(sizeof(hidl_vec), + &parentHandle, reinterpret_cast(&vec)); - const string_vec *vec; - status_t err = parcel->readBuffer(sizeof(*vec), &parentHandle, - reinterpret_cast(&vec)); - - if (err != OK) { - signalExceptionForError(env, err); - return NULL; - } - - size_t childHandle; - err = ::android::hardware::readEmbeddedFromParcel( - const_cast(*vec), - *parcel, parentHandle, 0 /* parentOffset */, &childHandle); + if (err == OK) { + size_t childHandle; + err = ::android::hardware::readEmbeddedFromParcel( + const_cast &>(*vec), + *parcel, parentHandle, + 0 /* parentOffset */, + &childHandle); - for (size_t i = 0; (err == OK) && (i < vec->size()); ++i) { - err = android::hardware::readEmbeddedFromParcel( - const_cast((*vec)[i]), + for (size_t i = 0; (err == OK) && (i < vec->size()); i++) { + err = android::hardware::readEmbeddedFromParcel( + const_cast((*vec)[i]), *parcel, childHandle, - i * sizeof(hidl_string) /* parentOffset */); + i * sizeof(T) /* parentOffset */); + } } if (err != OK) { signalExceptionForError(env, err); - return NULL; + return nullptr; } - return MakeStringArray(env, &(*vec)[0], vec->size()); + return vec; } -static void JHwParcel_native_writeStringVector( - JNIEnv *env, jobject thiz, jobjectArray arrayObj) { - typedef hidl_vec string_vec; - - if (arrayObj == NULL) { - jniThrowException(env, "java/lang/NullPointerException", NULL); - return; - } - - jsize len = env->GetArrayLength(arrayObj); - - sp impl = JHwParcel::GetNativeContext(env, thiz); - - void *vecPtr = - impl->getStorage()->allocTemporaryStorage(sizeof(string_vec)); - - string_vec *vec = new (vecPtr) string_vec; - - hidl_string *strings = impl->getStorage()->allocStringArray(len); - vec->setToExternal(strings, len); - - for (jsize i = 0; i < len; ++i) { - ScopedLocalRef stringObj( - env, - (jstring)env->GetObjectArrayElement(arrayObj, i)); - - const hidl_string *s = - impl->getStorage()->allocTemporaryString(env, stringObj.get()); - - strings[i].setToExternal(s->c_str(), s->size()); - } +static jobjectArray JHwParcel_native_readStringVector( + JNIEnv *env, jobject thiz) { + const hidl_vec *vec = ReadHidlVector(env, thiz); + return MakeStringArray(env, &(*vec)[0], vec->size()); +} - hardware::Parcel *parcel = impl->getParcel(); +static jobjectArray JHwParcel_native_readNativeHandleVector( + JNIEnv *env, jobject thiz) { + const hidl_vec *vec = ReadHidlVector(env, thiz); - size_t parentHandle; - status_t err = parcel->writeBuffer(vec, sizeof(*vec), &parentHandle); + jsize length = vec->size(); + jobjectArray objArray = JNativeHandle::AllocJavaNativeHandleObjArray( + env, length); - if (err == OK) { - size_t childHandle; - err = ::android::hardware::writeEmbeddedToParcel( - *vec, - parcel, - parentHandle, - 0 /* parentOffset */, - &childHandle); + for (jsize i = 0; i < length; i++) { + jobject jHandle = JNativeHandle::MakeJavaNativeHandleObj(env, (*vec)[i].getNativeHandle()); - for (size_t i = 0; (err == OK) && (i < vec->size()); ++i) { - err = ::android::hardware::writeEmbeddedToParcel( - (*vec)[i], - parcel, - childHandle, - i * sizeof(hidl_string)); - } + env->SetObjectArrayElement(objArray, i, jHandle); } - signalExceptionForError(env, err); + return objArray; } static jobject JHwParcel_native_readStrongBinder(JNIEnv *env, jobject thiz) { @@ -890,6 +978,9 @@ static JNINativeMethod gMethods[] = { { "writeString", "(Ljava/lang/String;)V", (void *)JHwParcel_native_writeString }, + { "writeNativeHandle", "(L" PACKAGE_PATH "/NativeHandle;)V", + (void *)JHwParcel_native_writeNativeHandle }, + { "writeBoolVector", "([Z)V", (void *)JHwParcel_native_writeBoolVector }, { "writeInt8Vector", "([B)V", (void *)JHwParcel_native_writeInt8Vector }, { "writeInt16Vector", "([S)V", (void *)JHwParcel_native_writeInt16Vector }, @@ -903,6 +994,9 @@ static JNINativeMethod gMethods[] = { { "writeStringVector", "([Ljava/lang/String;)V", (void *)JHwParcel_native_writeStringVector }, + { "writeNativeHandleVector", "([L" PACKAGE_PATH "/NativeHandle;)V", + (void *)JHwParcel_native_writeNativeHandleVector }, + { "writeStrongBinder", "(L" PACKAGE_PATH "/IHwBinder;)V", (void *)JHwParcel_native_writeStrongBinder }, @@ -920,6 +1014,12 @@ static JNINativeMethod gMethods[] = { { "readString", "()Ljava/lang/String;", (void *)JHwParcel_native_readString }, + { "readNativeHandle", "()L" PACKAGE_PATH "/NativeHandle;", + (void *)JHwParcel_native_readNativeHandle }, + + { "readEmbeddedNativeHandle", "(JJ)L" PACKAGE_PATH "/NativeHandle;", + (void *)JHwParcel_native_readEmbeddedNativeHandle }, + { "readBoolVectorAsArray", "()[Z", (void *)JHwParcel_native_readBoolVector }, @@ -944,6 +1044,9 @@ static JNINativeMethod gMethods[] = { { "readStringVectorAsArray", "()[Ljava/lang/String;", (void *)JHwParcel_native_readStringVector }, + { "readNativeHandleAsArray", "()[L" PACKAGE_PATH "/NativeHandle;", + (void *)JHwParcel_native_readNativeHandleVector }, + { "readStrongBinder", "()L" PACKAGE_PATH "/IHwBinder;", (void *)JHwParcel_native_readStrongBinder }, diff --git a/core/jni/android_os_NativeHandle.cpp b/core/jni/android_os_NativeHandle.cpp new file mode 100644 index 0000000000000..770fdb0d5d204 --- /dev/null +++ b/core/jni/android_os_NativeHandle.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2018 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_NativeHandle.h" + +#include +#include + +#include "core_jni_helpers.h" + +#define PACKAGE_PATH "android/os" +#define CLASS_NAME "NativeHandle" +#define CLASS_PATH PACKAGE_PATH "/" CLASS_NAME + +namespace android { + +static struct { + jclass clazz; + jmethodID constructID; // NativeHandle(int[] fds, int[] ints, boolean owns) + + jmethodID getFdsID; // int[] NativeHandle.getFds() + jmethodID getIntsID; // int[] NativeHandle.getInts() +} gNativeHandleFields; + +jobject JNativeHandle::MakeJavaNativeHandleObj( + JNIEnv *env, const native_handle_t *handle) { + if (handle == nullptr) { return nullptr; } + + const int numFds = handle->numFds; + ScopedLocalRef fds(env, env->NewIntArray(numFds)); + env->SetIntArrayRegion(fds.get(), 0, numFds, &(handle->data[0])); + + const int numInts = handle->numInts; + ScopedLocalRef ints(env, env->NewIntArray(numInts)); + env->SetIntArrayRegion(ints.get(), 0, numInts, &(handle->data[numFds])); + + return env->NewObject(gNativeHandleFields.clazz, + gNativeHandleFields.constructID, fds.get(), ints.get(), false /*own*/); +} + +native_handle_t *JNativeHandle::MakeCppNativeHandle( + JNIEnv *env, jobject jHandle, EphemeralStorage *storage) { + if (jHandle == nullptr) { return nullptr; } + + if (!env->IsInstanceOf(jHandle, gNativeHandleFields.clazz)) { + jniThrowException(env, "java/lang/ClassCastException", + "jHandle must be an instance of NativeHandle."); + return nullptr; + } + + ScopedLocalRef fds(env, (jintArray) env->CallObjectMethod( + jHandle, gNativeHandleFields.getFdsID)); + + ScopedLocalRef ints(env, (jintArray) env->CallObjectMethod( + jHandle, gNativeHandleFields.getIntsID)); + + const int numFds = (int) env->GetArrayLength(fds.get()); + const int numInts = (int) env->GetArrayLength(ints.get()); + + native_handle_t *handle = (storage == nullptr) + ? native_handle_create(numFds, numInts) + : storage->allocTemporaryNativeHandle(numFds, numInts); + + if (handle != nullptr) { + env->GetIntArrayRegion(fds.get(), 0, numFds, &(handle->data[0])); + env->GetIntArrayRegion(ints.get(), 0, numInts, &(handle->data[numFds])); + } else { + jniThrowException(env, "java/lang/OutOfMemoryError", + "Failed to allocate memory for native_handle_t."); + } + + return handle; +} + +jobjectArray JNativeHandle::AllocJavaNativeHandleObjArray(JNIEnv *env, jsize length) { + return env->NewObjectArray(length, gNativeHandleFields.clazz, nullptr); +} + +int register_android_os_NativeHandle(JNIEnv *env) { + jclass clazz = FindClassOrDie(env, CLASS_PATH); + gNativeHandleFields.clazz = MakeGlobalRefOrDie(env, clazz); + + gNativeHandleFields.constructID = GetMethodIDOrDie(env, clazz, "", "([I[IZ)V"); + gNativeHandleFields.getFdsID = GetMethodIDOrDie(env, clazz, "getFdsAsIntArray", "()[I"); + gNativeHandleFields.getIntsID = GetMethodIDOrDie(env, clazz, "getInts", "()[I"); + + return 0; +} + +} diff --git a/core/jni/android_os_NativeHandle.h b/core/jni/android_os_NativeHandle.h new file mode 100644 index 0000000000000..bbe3ebc1dbd00 --- /dev/null +++ b/core/jni/android_os_NativeHandle.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2018 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_NATIVE_HANDLE_H +#define ANDROID_OS_NATIVE_HANDLE_H + +#include "hwbinder/EphemeralStorage.h" + +#include +#include + +namespace android { + +struct JNativeHandle { + + /** + * Returns a Java NativeHandle object representing the parameterized + * native_handle_t instance. + */ + static jobject MakeJavaNativeHandleObj(JNIEnv *env, const native_handle_t *handle); + + /** + * Returns a heap-allocated native_handle_t instance representing the + * parameterized Java object. Note that if no valid EphemeralStorage* + * parameter is supplied (storage is nullptr), the return value must + * be explicitly deallocated (using native_handle_delete). + */ + static native_handle_t* MakeCppNativeHandle(JNIEnv *env, jobject jHandle, + EphemeralStorage *storage); + + /** + * Returns an (uninitialized) array of Java NativeHandle objects. + */ + static jobjectArray AllocJavaNativeHandleObjArray(JNIEnv *env, jsize length); +}; + +} + +#endif // ANDROID_OS_NATIVE_HANDLE_H diff --git a/core/jni/hwbinder/EphemeralStorage.cpp b/core/jni/hwbinder/EphemeralStorage.cpp index 3b18f2b36b9ed..95bb42ea57c67 100644 --- a/core/jni/hwbinder/EphemeralStorage.cpp +++ b/core/jni/hwbinder/EphemeralStorage.cpp @@ -71,6 +71,17 @@ const hidl_string *EphemeralStorage::allocTemporaryString( return s; } +native_handle_t *EphemeralStorage::allocTemporaryNativeHandle( + int numFds, int numInts) { + Item item; + item.mType = TYPE_NATIVE_HANDLE; + item.mObj = nullptr; + item.mPtr = native_handle_create(numFds, numInts); + mItems.push_back(item); + + return static_cast(item.mPtr); +} + #define DEFINE_ALLOC_VECTOR_METHODS(Suffix,Type,NewType) \ const hidl_vec *EphemeralStorage::allocTemporary ## Suffix ## Vector( \ JNIEnv *env, Type ## Array arrayObj) { \ @@ -145,6 +156,13 @@ void EphemeralStorage::release(JNIEnv *env) { DEFINE_RELEASE_ARRAY_CASE(Float,jfloat,Float) DEFINE_RELEASE_ARRAY_CASE(Double,jdouble,Double) + case TYPE_NATIVE_HANDLE: + { + int err = native_handle_delete(static_cast(item.mPtr)); + CHECK(err == 0); + break; + } + default: CHECK(!"Should not be here"); } diff --git a/core/jni/hwbinder/EphemeralStorage.h b/core/jni/hwbinder/EphemeralStorage.h index f07c782bfdf78..55ef74169eb12 100644 --- a/core/jni/hwbinder/EphemeralStorage.h +++ b/core/jni/hwbinder/EphemeralStorage.h @@ -43,6 +43,8 @@ struct EphemeralStorage { const ::android::hardware::hidl_string *allocTemporaryString( JNIEnv *env, jstring stringObj); + native_handle_t *allocTemporaryNativeHandle(int numFds, int numInts); + DECLARE_ALLOC_METHODS(Int8,jbyte) DECLARE_ALLOC_METHODS(Int16,jshort) DECLARE_ALLOC_METHODS(Int32,jint) @@ -61,6 +63,7 @@ struct EphemeralStorage { TYPE_Int64_ARRAY, TYPE_Float_ARRAY, TYPE_Double_ARRAY, + TYPE_NATIVE_HANDLE, }; struct Item {