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