1 /* 2 * Copyright (C) 2019 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.internal.net.ipsec.ike.utils; 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 31 import java.io.FileDescriptor; 32 import java.io.IOException; 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 * <p> This code is an exact copy of {@link FdEventsReader} in 66 * frameworks/base/packages/NetworkStack/src/android/net/util/FdEventsReader.java, except the class 67 * name is changed to avoid confusion. 68 * 69 * FIXME: b/130058477 Find a way to share the code between network stack and code outside. 70 * 71 * @param <BufferType> the type of the buffer used to read data. 72 * @hide 73 */ 74 public abstract class FdEventsReader<BufferType> { 75 private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR; 76 private static final int UNREGISTER_THIS_FD = 0; 77 78 @NonNull 79 private final Handler mHandler; 80 @NonNull 81 private final MessageQueue mQueue; 82 @NonNull 83 private final BufferType mBuffer; 84 @Nullable 85 private FileDescriptor mFd; 86 private long mPacketsReceived; 87 closeFd(FileDescriptor fd)88 protected static void closeFd(FileDescriptor fd) { 89 try { 90 SocketUtils.closeSocket(fd); 91 } catch (IOException ignored) { 92 } 93 } 94 FdEventsReader(@onNull Handler h, @NonNull BufferType buffer)95 protected FdEventsReader(@NonNull Handler h, @NonNull BufferType buffer) { 96 mHandler = h; 97 mQueue = mHandler.getLooper().getQueue(); 98 mBuffer = buffer; 99 } 100 101 /** Start this FdEventsReader. */ start()102 public void start() { 103 if (onCorrectThread()) { 104 createAndRegisterFd(); 105 } else { 106 mHandler.post(() -> { 107 logError("start() called from off-thread", null); 108 createAndRegisterFd(); 109 }); 110 } 111 } 112 113 /** Stop this FdEventsReader and destroy the file descriptor. */ stop()114 public void stop() { 115 if (onCorrectThread()) { 116 unregisterAndDestroyFd(); 117 } else { 118 mHandler.post(() -> { 119 logError("stop() called from off-thread", null); 120 unregisterAndDestroyFd(); 121 }); 122 } 123 } 124 125 @NonNull getHandler()126 public Handler getHandler() { 127 return mHandler; 128 } 129 recvBufSize(@onNull BufferType buffer)130 protected abstract int recvBufSize(@NonNull BufferType buffer); 131 132 /** Returns the size of the receive buffer. */ recvBufSize()133 public int recvBufSize() { 134 return recvBufSize(mBuffer); 135 } 136 137 /** 138 * Get the number of successful calls to {@link #readPacket(FileDescriptor, Object)}. 139 * 140 * <p>A call was successful if {@link #readPacket(FileDescriptor, Object)} returned a value > 0. 141 */ numPacketsReceived()142 public final long numPacketsReceived() { 143 return mPacketsReceived; 144 } 145 146 /** 147 * Subclasses MUST create the listening socket here, including setting all desired socket 148 * options, interface or address/port binding, etc. The socket MUST be created nonblocking. 149 */ 150 @Nullable createFd()151 protected abstract FileDescriptor createFd(); 152 153 /** 154 * Implementations MUST return the bytes read or throw an Exception. 155 * 156 * <p>The caller may throw a {@link ErrnoException} with {@link OsConstants#EAGAIN} or 157 * {@link OsConstants#EINTR}, in which case {@link FdEventsReader} will ignore the buffer 158 * contents and respectively wait for further input or retry the read immediately. For all other 159 * exceptions, the {@link FdEventsReader} will be stopped with no more interactions with this 160 * method. 161 */ readPacket(@onNull FileDescriptor fd, @NonNull BufferType buffer)162 protected abstract int readPacket(@NonNull FileDescriptor fd, @NonNull BufferType buffer) 163 throws Exception; 164 165 /** 166 * Called by the main loop for every packet. Any desired copies of 167 * |recvbuf| should be made in here, as the underlying byte array is 168 * reused across all reads. 169 */ handlePacket(@onNull BufferType recvbuf, int length)170 protected void handlePacket(@NonNull BufferType recvbuf, int length) {} 171 172 /** 173 * Called by the main loop to log errors. In some cases |e| may be null. 174 */ logError(@onNull String msg, @Nullable Exception e)175 protected void logError(@NonNull String msg, @Nullable Exception e) {} 176 177 /** 178 * Called by start(), if successful, just prior to returning. 179 */ onStart()180 protected void onStart() {} 181 182 /** 183 * Called by stop() just prior to returning. 184 */ onStop()185 protected void onStop() {} 186 createAndRegisterFd()187 private void createAndRegisterFd() { 188 if (mFd != null) return; 189 190 try { 191 mFd = createFd(); 192 } catch (Exception e) { 193 logError("Failed to create socket: ", e); 194 closeFd(mFd); 195 mFd = null; 196 } 197 198 if (mFd == null) return; 199 200 mQueue.addOnFileDescriptorEventListener( 201 mFd, 202 FD_EVENTS, 203 (fd, events) -> { 204 // Always call handleInput() so read/recvfrom are given 205 // a proper chance to encounter a meaningful errno and 206 // perhaps log a useful error message. 207 if (!isRunning() || !handleInput()) { 208 unregisterAndDestroyFd(); 209 return UNREGISTER_THIS_FD; 210 } 211 return FD_EVENTS; 212 }); 213 onStart(); 214 } 215 isRunning()216 private boolean isRunning() { 217 return (mFd != null) && mFd.valid(); 218 } 219 220 // Keep trying to read until we get EAGAIN/EWOULDBLOCK or some fatal error. handleInput()221 private boolean handleInput() { 222 while (isRunning()) { 223 final int bytesRead; 224 225 try { 226 bytesRead = readPacket(mFd, mBuffer); 227 if (bytesRead < 1) { 228 if (isRunning()) logError("Socket closed, exiting", null); 229 break; 230 } 231 mPacketsReceived++; 232 } catch (ErrnoException e) { 233 if (e.errno == OsConstants.EAGAIN) { 234 // We've read everything there is to read this time around. 235 return true; 236 } else if (e.errno == OsConstants.EINTR) { 237 continue; 238 } else { 239 if (isRunning()) logError("readPacket error: ", e); 240 break; 241 } 242 } catch (Exception e) { 243 if (isRunning()) logError("readPacket error: ", e); 244 break; 245 } 246 247 try { 248 handlePacket(mBuffer, bytesRead); 249 } catch (Exception e) { 250 logError("handlePacket error: ", e); 251 break; 252 } 253 } 254 255 return false; 256 } 257 unregisterAndDestroyFd()258 private void unregisterAndDestroyFd() { 259 if (mFd == null) return; 260 261 mQueue.removeOnFileDescriptorEventListener(mFd); 262 closeFd(mFd); 263 mFd = null; 264 onStop(); 265 } 266 onCorrectThread()267 private boolean onCorrectThread() { 268 return (mHandler.getLooper() == Looper.myLooper()); 269 } 270 } 271