PacketReader.java revision 84714bffa1a58fe1f6a114ae015f8e38be46f32d
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 17package android.net.util; 18 19import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT; 20import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR; 21 22import android.annotation.Nullable; 23import android.os.Handler; 24import android.os.Looper; 25import android.os.MessageQueue; 26import android.os.MessageQueue.OnFileDescriptorEventListener; 27import android.system.ErrnoException; 28import android.system.Os; 29import android.system.OsConstants; 30 31import libcore.io.IoUtils; 32 33import java.io.FileDescriptor; 34import java.io.IOException; 35 36 37/** 38 * This class encapsulates the mechanics of registering a file descriptor 39 * with a thread's Looper and handling read events (and errors). 40 * 41 * Subclasses MUST implement createFd() and SHOULD override handlePacket(). 42 43 * Subclasses can expect a call life-cycle like the following: 44 * 45 * [1] start() calls createFd() and (if all goes well) onStart() 46 * 47 * [2] yield, waiting for read event or error notification: 48 * 49 * [a] readPacket() && handlePacket() 50 * 51 * [b] if (no error): 52 * goto 2 53 * else: 54 * goto 3 55 * 56 * [3] stop() calls onStop() if not previously stopped 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 * TODO: rename this class to something more correctly descriptive (something 66 * like [or less horrible than] FdReadEventsHandler?). 67 * 68 * @hide 69 */ 70public abstract class PacketReader { 71 private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR; 72 private static final int UNREGISTER_THIS_FD = 0; 73 74 public static final int DEFAULT_RECV_BUF_SIZE = 2 * 1024; 75 76 private final Handler mHandler; 77 private final MessageQueue mQueue; 78 private final byte[] mPacket; 79 private FileDescriptor mFd; 80 private long mPacketsReceived; 81 82 protected static void closeFd(FileDescriptor fd) { 83 IoUtils.closeQuietly(fd); 84 } 85 86 protected PacketReader(Handler h) { 87 this(h, DEFAULT_RECV_BUF_SIZE); 88 } 89 90 protected PacketReader(Handler h, int recvbufsize) { 91 mHandler = h; 92 mQueue = mHandler.getLooper().getQueue(); 93 mPacket = new byte[Math.max(recvbufsize, DEFAULT_RECV_BUF_SIZE)]; 94 } 95 96 public final 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 public final void stop() { 108 if (onCorrectThread()) { 109 unregisterAndDestroyFd(); 110 } else { 111 mHandler.post(() -> { 112 logError("stop() called from off-thread", null); 113 unregisterAndDestroyFd(); 114 }); 115 } 116 } 117 118 public Handler getHandler() { return mHandler; } 119 120 public final int recvBufSize() { return mPacket.length; } 121 122 public final long numPacketsReceived() { return mPacketsReceived; } 123 124 /** 125 * Subclasses MUST create the listening socket here, including setting 126 * all desired socket options, interface or address/port binding, etc. 127 */ 128 protected abstract FileDescriptor createFd(); 129 130 /** 131 * Subclasses MAY override this to change the default read() implementation 132 * in favour of, say, recvfrom(). 133 * 134 * Implementations MUST return the bytes read or throw an Exception. 135 */ 136 protected int readPacket(FileDescriptor fd, byte[] packetBuffer) throws Exception { 137 return Os.read(fd, packetBuffer, 0, packetBuffer.length); 138 } 139 140 /** 141 * Called by the main loop for every packet. Any desired copies of 142 * |recvbuf| should be made in here, as the underlying byte array is 143 * reused across all reads. 144 */ 145 protected void handlePacket(byte[] recvbuf, int length) {} 146 147 /** 148 * Called by the main loop to log errors. In some cases |e| may be null. 149 */ 150 protected void logError(String msg, Exception e) {} 151 152 /** 153 * Called by start(), if successful, just prior to returning. 154 */ 155 protected void onStart() {} 156 157 /** 158 * Called by stop() just prior to returning. 159 */ 160 protected void onStop() {} 161 162 private void createAndRegisterFd() { 163 if (mFd != null) return; 164 165 try { 166 mFd = createFd(); 167 if (mFd != null) { 168 // Force the socket to be non-blocking. 169 IoUtils.setBlocking(mFd, false); 170 } 171 } catch (Exception e) { 172 logError("Failed to create socket: ", e); 173 closeFd(mFd); 174 mFd = null; 175 return; 176 } 177 178 if (mFd == null) return; 179 180 mQueue.addOnFileDescriptorEventListener( 181 mFd, 182 FD_EVENTS, 183 new OnFileDescriptorEventListener() { 184 @Override 185 public int onFileDescriptorEvents(FileDescriptor fd, int events) { 186 // Always call handleInput() so read/recvfrom are given 187 // a proper chance to encounter a meaningful errno and 188 // perhaps log a useful error message. 189 if (!isRunning() || !handleInput()) { 190 unregisterAndDestroyFd(); 191 return UNREGISTER_THIS_FD; 192 } 193 return FD_EVENTS; 194 } 195 }); 196 onStart(); 197 } 198 199 private boolean isRunning() { return (mFd != null) && mFd.valid(); } 200 201 // Keep trying to read until we get EAGAIN/EWOULDBLOCK or some fatal error. 202 private boolean handleInput() { 203 while (isRunning()) { 204 final int bytesRead; 205 206 try { 207 bytesRead = readPacket(mFd, mPacket); 208 if (bytesRead < 1) { 209 if (isRunning()) logError("Socket closed, exiting", null); 210 break; 211 } 212 mPacketsReceived++; 213 } catch (ErrnoException e) { 214 if (e.errno == OsConstants.EAGAIN) { 215 // We've read everything there is to read this time around. 216 return true; 217 } else if (e.errno == OsConstants.EINTR) { 218 continue; 219 } else { 220 if (isRunning()) logError("readPacket error: ", e); 221 break; 222 } 223 } catch (Exception e) { 224 if (isRunning()) logError("readPacket error: ", e); 225 break; 226 } 227 228 try { 229 handlePacket(mPacket, bytesRead); 230 } catch (Exception e) { 231 logError("handlePacket error: ", e); 232 break; 233 } 234 } 235 236 return false; 237 } 238 239 private void unregisterAndDestroyFd() { 240 if (mFd == null) return; 241 242 mQueue.removeOnFileDescriptorEventListener(mFd); 243 closeFd(mFd); 244 mFd = null; 245 onStop(); 246 } 247 248 private boolean onCorrectThread() { 249 return (mHandler.getLooper() == Looper.myLooper()); 250 } 251} 252