1 /* 2 * Copyright (C) 2016 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.net.util; 18 19 import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR; 20 import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT; 21 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.os.Handler; 25 import android.os.Looper; 26 import android.os.MessageQueue; 27 import android.system.ErrnoException; 28 import android.system.OsConstants; 29 30 import java.io.FileDescriptor; 31 import java.io.IOException; 32 33 34 /** 35 * This class encapsulates the mechanics of registering a file descriptor 36 * with a thread's Looper and handling read events (and errors). 37 * 38 * Subclasses MUST implement createFd() and SHOULD override handlePacket(). They MAY override 39 * onStop() and onStart(). 40 * 41 * Subclasses can expect a call life-cycle like the following: 42 * 43 * [1] when a client calls start(), createFd() is called, followed by the onStart() hook if all 44 * goes well. Implementations may override onStart() for additional initialization. 45 * 46 * [2] yield, waiting for read event or error notification: 47 * 48 * [a] readPacket() && handlePacket() 49 * 50 * [b] if (no error): 51 * goto 2 52 * else: 53 * goto 3 54 * 55 * [3] when a client calls stop(), the onStop() hook is called (unless already stopped or never 56 * started). Implementations may override onStop() for additional cleanup. 57 * 58 * The packet receive buffer is recycled on every read call, so subclasses 59 * should make any copies they would like inside their handlePacket() 60 * implementation. 61 * 62 * All public methods MUST only be called from the same thread with which 63 * the Handler constructor argument is associated. 64 * 65 * @param <BufferType> the type of the buffer used to read data. 66 * @hide 67 */ 68 public abstract class FdEventsReader<BufferType> { 69 private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR; 70 private static final int UNREGISTER_THIS_FD = 0; 71 72 @NonNull 73 private final Handler mHandler; 74 @NonNull 75 private final MessageQueue mQueue; 76 @NonNull 77 private final BufferType mBuffer; 78 @Nullable 79 private FileDescriptor mFd; 80 private long mPacketsReceived; 81 closeFd(FileDescriptor fd)82 protected static void closeFd(FileDescriptor fd) { 83 try { 84 SocketUtils.closeSocket(fd); 85 } catch (IOException ignored) { 86 } 87 } 88 FdEventsReader(@onNull Handler h, @NonNull BufferType buffer)89 protected FdEventsReader(@NonNull Handler h, @NonNull BufferType buffer) { 90 mHandler = h; 91 mQueue = mHandler.getLooper().getQueue(); 92 mBuffer = buffer; 93 } 94 95 /** Start this FdEventsReader. */ start()96 public void start() { 97 if (onCorrectThread()) { 98 createAndRegisterFd(); 99 } else { 100 mHandler.post(() -> { 101 logError("start() called from off-thread", null); 102 createAndRegisterFd(); 103 }); 104 } 105 } 106 107 /** Stop this FdEventsReader and destroy the file descriptor. */ stop()108 public void stop() { 109 if (onCorrectThread()) { 110 unregisterAndDestroyFd(); 111 } else { 112 mHandler.post(() -> { 113 logError("stop() called from off-thread", null); 114 unregisterAndDestroyFd(); 115 }); 116 } 117 } 118 119 @NonNull getHandler()120 public Handler getHandler() { 121 return mHandler; 122 } 123 recvBufSize(@onNull BufferType buffer)124 protected abstract int recvBufSize(@NonNull BufferType buffer); 125 126 /** Returns the size of the receive buffer. */ recvBufSize()127 public int recvBufSize() { 128 return recvBufSize(mBuffer); 129 } 130 131 /** 132 * Get the number of successful calls to {@link #readPacket(FileDescriptor, Object)}. 133 * 134 * <p>A call was successful if {@link #readPacket(FileDescriptor, Object)} returned a value > 0. 135 */ numPacketsReceived()136 public final long numPacketsReceived() { 137 return mPacketsReceived; 138 } 139 140 /** 141 * Subclasses MUST create the listening socket here, including setting all desired socket 142 * options, interface or address/port binding, etc. The socket MUST be created nonblocking. 143 */ 144 @Nullable createFd()145 protected abstract FileDescriptor createFd(); 146 147 /** 148 * Implementations MUST return the bytes read or throw an Exception. 149 * 150 * <p>The caller may throw a {@link ErrnoException} with {@link OsConstants#EAGAIN} or 151 * {@link OsConstants#EINTR}, in which case {@link FdEventsReader} will ignore the buffer 152 * contents and respectively wait for further input or retry the read immediately. For all other 153 * exceptions, the {@link FdEventsReader} will be stopped with no more interactions with this 154 * method. 155 */ readPacket(@onNull FileDescriptor fd, @NonNull BufferType buffer)156 protected abstract int readPacket(@NonNull FileDescriptor fd, @NonNull BufferType buffer) 157 throws Exception; 158 159 /** 160 * Called by the main loop for every packet. Any desired copies of 161 * |recvbuf| should be made in here, as the underlying byte array is 162 * reused across all reads. 163 */ handlePacket(@onNull BufferType recvbuf, int length)164 protected void handlePacket(@NonNull BufferType recvbuf, int length) {} 165 166 /** 167 * Called by the main loop to log errors. In some cases |e| may be null. 168 */ logError(@onNull String msg, @Nullable Exception e)169 protected void logError(@NonNull String msg, @Nullable Exception e) {} 170 171 /** 172 * Called by start(), if successful, just prior to returning. 173 */ onStart()174 protected void onStart() {} 175 176 /** 177 * Called by stop() just prior to returning. 178 */ onStop()179 protected void onStop() {} 180 createAndRegisterFd()181 private void createAndRegisterFd() { 182 if (mFd != null) return; 183 184 try { 185 mFd = createFd(); 186 } catch (Exception e) { 187 logError("Failed to create socket: ", e); 188 closeFd(mFd); 189 mFd = null; 190 } 191 192 if (mFd == null) return; 193 194 mQueue.addOnFileDescriptorEventListener( 195 mFd, 196 FD_EVENTS, 197 (fd, events) -> { 198 // Always call handleInput() so read/recvfrom are given 199 // a proper chance to encounter a meaningful errno and 200 // perhaps log a useful error message. 201 if (!isRunning() || !handleInput()) { 202 unregisterAndDestroyFd(); 203 return UNREGISTER_THIS_FD; 204 } 205 return FD_EVENTS; 206 }); 207 onStart(); 208 } 209 isRunning()210 private boolean isRunning() { 211 return (mFd != null) && mFd.valid(); 212 } 213 214 // Keep trying to read until we get EAGAIN/EWOULDBLOCK or some fatal error. handleInput()215 private boolean handleInput() { 216 while (isRunning()) { 217 final int bytesRead; 218 219 try { 220 bytesRead = readPacket(mFd, mBuffer); 221 if (bytesRead < 1) { 222 if (isRunning()) logError("Socket closed, exiting", null); 223 break; 224 } 225 mPacketsReceived++; 226 } catch (ErrnoException e) { 227 if (e.errno == OsConstants.EAGAIN) { 228 // We've read everything there is to read this time around. 229 return true; 230 } else if (e.errno == OsConstants.EINTR) { 231 continue; 232 } else { 233 if (isRunning()) logError("readPacket error: ", e); 234 break; 235 } 236 } catch (Exception e) { 237 if (isRunning()) logError("readPacket error: ", e); 238 break; 239 } 240 241 try { 242 handlePacket(mBuffer, bytesRead); 243 } catch (Exception e) { 244 logError("handlePacket error: ", e); 245 break; 246 } 247 } 248 249 return false; 250 } 251 unregisterAndDestroyFd()252 private void unregisterAndDestroyFd() { 253 if (mFd == null) return; 254 255 mQueue.removeOnFileDescriptorEventListener(mFd); 256 closeFd(mFd); 257 mFd = null; 258 onStop(); 259 } 260 onCorrectThread()261 private boolean onCorrectThread() { 262 return (mHandler.getLooper() == Looper.myLooper()); 263 } 264 } 265