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 */ NativeHandle()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 */ NativeHandle(@onNull FileDescriptor descriptor, boolean own)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 */ createFileDescriptorArray(@onNull int[] fds)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 */ NativeHandle(@onNull int[] fds, @NonNull int[] ints, boolean own)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 */ NativeHandle(@onNull FileDescriptor[] fds, @NonNull int[] ints, boolean own)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 */ hasSingleFileDescriptor()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 */ dup()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 checkOpen()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 close()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 */ getFileDescriptor()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 */ getFdsAsIntArray()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 */ getFileDescriptors()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 */ getInts()213 public @NonNull int[] getInts() { 214 checkOpen(); 215 216 return mInts; 217 } 218 } 219