Home | History | Annotate | Download | only in os
      1 /*
      2  * Copyright (C) 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.os;
     18 
     19 import static android.system.OsConstants.F_DUPFD_CLOEXEC;
     20 
     21 import android.annotation.NonNull;
     22 import android.annotation.SystemApi;
     23 import android.annotation.TestApi;
     24 import android.system.ErrnoException;
     25 import android.system.Os;
     26 
     27 import java.io.Closeable;
     28 import java.io.FileDescriptor;
     29 
     30 /**
     31  * Collection representing a set of open file descriptors and an opaque data stream.
     32  *
     33  * @hide
     34  */
     35 @SystemApi
     36 @TestApi
     37 public final class NativeHandle implements Closeable {
     38     // whether this object owns mFds
     39     private boolean mOwn = false;
     40     private FileDescriptor[] mFds;
     41     private int[] mInts;
     42 
     43     /**
     44      * Constructs a {@link NativeHandle} object containing
     45      * zero file descriptors and an empty data stream.
     46      */
     47     public NativeHandle() {
     48         this(new FileDescriptor[0], new int[0], false);
     49     }
     50 
     51     /**
     52      * Constructs a {@link NativeHandle} object containing the given
     53      * {@link FileDescriptor} object and an empty data stream.
     54      */
     55     public NativeHandle(@NonNull FileDescriptor descriptor, boolean own) {
     56         this(new FileDescriptor[] {descriptor}, new int[0], own);
     57     }
     58 
     59     /**
     60      * Convenience method for creating a list of file descriptors.
     61      *
     62      * @hide
     63      */
     64     private static FileDescriptor[] createFileDescriptorArray(@NonNull int[] fds) {
     65         FileDescriptor[] list = new FileDescriptor[fds.length];
     66         for (int i = 0; i < fds.length; i++) {
     67             FileDescriptor descriptor = new FileDescriptor();
     68             descriptor.setInt$(fds[i]);
     69             list[i] = descriptor;
     70         }
     71         return list;
     72     }
     73 
     74     /**
     75      * Convenience method for instantiating a {@link NativeHandle} from JNI. It does
     76      * not take ownership of the int[] params. It does not dupe the FileDescriptors.
     77      *
     78      * @hide
     79      */
     80     private NativeHandle(@NonNull int[] fds, @NonNull int[] ints, boolean own) {
     81         this(createFileDescriptorArray(fds), ints, own);
     82     }
     83 
     84     /**
     85      * Instantiate an opaque {@link NativeHandle} from fds and integers.
     86      *
     87      * @param own whether the fds are owned by this object and should be closed
     88      */
     89     public NativeHandle(@NonNull FileDescriptor[] fds, @NonNull int[] ints, boolean own) {
     90         mFds = fds.clone();
     91         mInts = ints.clone();
     92         mOwn = own;
     93     }
     94 
     95     /**
     96      * Returns whether this {@link NativeHandle} object contains a single file
     97      * descriptor and nothing else.
     98      *
     99      * @return a boolean value
    100      */
    101     public boolean hasSingleFileDescriptor() {
    102         checkOpen();
    103 
    104         return mFds.length == 1 && mInts.length == 0;
    105     }
    106 
    107     /**
    108      * Explicitly duplicate NativeHandle (this dups all file descritptors).
    109      *
    110      * If this method is called, this must also be explicitly closed with
    111      * {@link #close()}.
    112      */
    113     public @NonNull NativeHandle dup() throws java.io.IOException {
    114         FileDescriptor[] fds = new FileDescriptor[mFds.length];
    115         try {
    116             for (int i = 0; i < mFds.length; i++) {
    117                 FileDescriptor newFd = new FileDescriptor();
    118                 int fdint = Os.fcntlInt(mFds[i], F_DUPFD_CLOEXEC, 0);
    119                 newFd.setInt$(fdint);
    120                 fds[i] = newFd;
    121             }
    122         } catch (ErrnoException e) {
    123             e.rethrowAsIOException();
    124         }
    125         return new NativeHandle(fds, mInts, true /*own*/);
    126     }
    127 
    128     private void checkOpen() {
    129         if (mFds == null) {
    130             throw new IllegalStateException("NativeHandle is invalidated after close.");
    131         }
    132     }
    133 
    134     /**
    135      * Closes the file descriptors if they are owned by this object.
    136      *
    137      * This also invalidates the object.
    138      */
    139     @Override
    140     public void close() throws java.io.IOException {
    141         checkOpen();
    142 
    143         if (mOwn) {
    144             try {
    145                 for (FileDescriptor fd : mFds) {
    146                     Os.close(fd);
    147                 }
    148             } catch (ErrnoException e) {
    149                 e.rethrowAsIOException();
    150             }
    151 
    152             mOwn = false;
    153         }
    154 
    155         mFds = null;
    156         mInts = null;
    157     }
    158 
    159     /**
    160      * Returns the underlying lone file descriptor.
    161      *
    162      * @return a {@link FileDescriptor} object
    163      * @throws IllegalStateException if this object contains either zero or
    164      *         more than one file descriptor, or a non-empty data stream.
    165      */
    166     public @NonNull FileDescriptor getFileDescriptor() {
    167         checkOpen();
    168 
    169         if (!hasSingleFileDescriptor()) {
    170             throw new IllegalStateException(
    171                     "NativeHandle is not single file descriptor. Contents must"
    172                     + " be retreived through getFileDescriptors and getInts.");
    173         }
    174 
    175         return mFds[0];
    176     }
    177 
    178     /**
    179      * Convenience method for fetching this object's file descriptors from JNI.
    180      * @return a mutable copy of the underlying file descriptors (as an int[])
    181      *
    182      * @hide
    183      */
    184     private int[] getFdsAsIntArray() {
    185         checkOpen();
    186 
    187         int numFds = mFds.length;
    188         int[] fds = new int[numFds];
    189 
    190         for (int i = 0; i < numFds; i++) {
    191             fds[i] = mFds[i].getInt$();
    192         }
    193 
    194         return fds;
    195     }
    196 
    197     /**
    198      * Fetch file descriptors
    199      *
    200      * @return the fds.
    201      */
    202     public @NonNull FileDescriptor[] getFileDescriptors() {
    203         checkOpen();
    204 
    205         return mFds;
    206     }
    207 
    208     /**
    209      * Fetch opaque ints. Note: This object retains ownership of the data.
    210      *
    211      * @return the opaque data stream.
    212      */
    213     public @NonNull int[] getInts() {
    214         checkOpen();
    215 
    216         return mInts;
    217     }
    218 }
    219