Implementing support for HIDL native handles in Java

This change implements the equivalent of the C++ native_handle_t type in
Java. Similar to the C++ type, the NativeHandle class wraps an arraylist
of FileDescriptor objects, along with a raw data stream (integer array).

Bug: 35098567
Test: Ran m, hidl_test (C++ and Java). Functionality tests are included
in a separate CL.

Change-Id: Ic53f9a49ae17ce5708577a586230126ab0e222c7
diff --git a/core/java/android/os/NativeHandle.java b/core/java/android/os/NativeHandle.java
new file mode 100644
index 0000000..f3dc5c8
--- /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;
+    }
+}