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