• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.system.ErrnoException;
24 import android.system.Os;
25 
26 import java.io.Closeable;
27 import java.io.FileDescriptor;
28 
29 /**
30  * Collection representing a set of open file descriptors and an opaque data stream.
31  *
32  * @hide
33  */
34 @SystemApi
35 public final class NativeHandle implements Closeable {
36     // whether this object owns mFds
37     private boolean mOwn = false;
38     private FileDescriptor[] mFds;
39     private int[] mInts;
40 
41     /**
42      * Constructs a {@link NativeHandle} object containing
43      * zero file descriptors and an empty data stream.
44      */
NativeHandle()45     public NativeHandle() {
46         this(new FileDescriptor[0], new int[0], false);
47     }
48 
49     /**
50      * Constructs a {@link NativeHandle} object containing the given
51      * {@link FileDescriptor} object and an empty data stream.
52      */
NativeHandle(@onNull FileDescriptor descriptor, boolean own)53     public NativeHandle(@NonNull FileDescriptor descriptor, boolean own) {
54         this(new FileDescriptor[] {descriptor}, new int[0], own);
55     }
56 
57     /**
58      * Convenience method for creating a list of file descriptors.
59      *
60      * @hide
61      */
createFileDescriptorArray(@onNull int[] fds)62     private static FileDescriptor[] createFileDescriptorArray(@NonNull int[] fds) {
63         FileDescriptor[] list = new FileDescriptor[fds.length];
64         for (int i = 0; i < fds.length; i++) {
65             FileDescriptor descriptor = new FileDescriptor();
66             descriptor.setInt$(fds[i]);
67             list[i] = descriptor;
68         }
69         return list;
70     }
71 
72     /**
73      * Convenience method for instantiating a {@link NativeHandle} from JNI. It does
74      * not take ownership of the int[] params. It does not dupe the FileDescriptors.
75      *
76      * @hide
77      */
NativeHandle(@onNull int[] fds, @NonNull int[] ints, boolean own)78     private NativeHandle(@NonNull int[] fds, @NonNull int[] ints, boolean own) {
79         this(createFileDescriptorArray(fds), ints, own);
80     }
81 
82     /**
83      * Instantiate an opaque {@link NativeHandle} from fds and integers.
84      *
85      * @param own whether the fds are owned by this object and should be closed
86      */
NativeHandle(@onNull FileDescriptor[] fds, @NonNull int[] ints, boolean own)87     public NativeHandle(@NonNull FileDescriptor[] fds, @NonNull int[] ints, boolean own) {
88         mFds = fds.clone();
89         mInts = ints.clone();
90         mOwn = own;
91     }
92 
93     /**
94      * Returns whether this {@link NativeHandle} object contains a single file
95      * descriptor and nothing else.
96      *
97      * @return a boolean value
98      */
hasSingleFileDescriptor()99     public boolean hasSingleFileDescriptor() {
100         checkOpen();
101 
102         return mFds.length == 1 && mInts.length == 0;
103     }
104 
105     /**
106      * Explicitly duplicate NativeHandle (this dups all file descritptors).
107      *
108      * If this method is called, this must also be explicitly closed with
109      * {@link #close()}.
110      */
dup()111     public @NonNull NativeHandle dup() throws java.io.IOException {
112         FileDescriptor[] fds = new FileDescriptor[mFds.length];
113         try {
114             for (int i = 0; i < mFds.length; i++) {
115                 FileDescriptor newFd = new FileDescriptor();
116                 int fdint = Os.fcntlInt(mFds[i], F_DUPFD_CLOEXEC, 0);
117                 newFd.setInt$(fdint);
118                 fds[i] = newFd;
119             }
120         } catch (ErrnoException e) {
121             e.rethrowAsIOException();
122         }
123         return new NativeHandle(fds, mInts, true /*own*/);
124     }
125 
checkOpen()126     private void checkOpen() {
127         if (mFds == null) {
128             throw new IllegalStateException("NativeHandle is invalidated after close.");
129         }
130     }
131 
132     /**
133      * Closes the file descriptors if they are owned by this object.
134      *
135      * This also invalidates the object.
136      */
137     @Override
close()138     public void close() throws java.io.IOException {
139         checkOpen();
140 
141         if (mOwn) {
142             try {
143                 for (FileDescriptor fd : mFds) {
144                     Os.close(fd);
145                 }
146             } catch (ErrnoException e) {
147                 e.rethrowAsIOException();
148             }
149 
150             mOwn = false;
151         }
152 
153         mFds = null;
154         mInts = null;
155     }
156 
157     /**
158      * Returns the underlying lone file descriptor.
159      *
160      * @return a {@link FileDescriptor} object
161      * @throws IllegalStateException if this object contains either zero or
162      *         more than one file descriptor, or a non-empty data stream.
163      */
getFileDescriptor()164     public @NonNull FileDescriptor getFileDescriptor() {
165         checkOpen();
166 
167         if (!hasSingleFileDescriptor()) {
168             throw new IllegalStateException(
169                     "NativeHandle is not single file descriptor. Contents must"
170                     + " be retreived through getFileDescriptors and getInts.");
171         }
172 
173         return mFds[0];
174     }
175 
176     /**
177      * Convenience method for fetching this object's file descriptors from JNI.
178      * @return a mutable copy of the underlying file descriptors (as an int[])
179      *
180      * @hide
181      */
getFdsAsIntArray()182     private int[] getFdsAsIntArray() {
183         checkOpen();
184 
185         int numFds = mFds.length;
186         int[] fds = new int[numFds];
187 
188         for (int i = 0; i < numFds; i++) {
189             fds[i] = mFds[i].getInt$();
190         }
191 
192         return fds;
193     }
194 
195     /**
196      * Fetch file descriptors
197      *
198      * @return the fds.
199      */
getFileDescriptors()200     public @NonNull FileDescriptor[] getFileDescriptors() {
201         checkOpen();
202 
203         return mFds;
204     }
205 
206     /**
207      * Fetch opaque ints. Note: This object retains ownership of the data.
208      *
209      * @return the opaque data stream.
210      */
getInts()211     public @NonNull int[] getInts() {
212         checkOpen();
213 
214         return mInts;
215     }
216 }
217