Chalard Jean | 48c6c7d | 2020-06-25 23:39:15 +0900 | [diff] [blame^] | 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 | // TODO : move this and PacketReader to com.android.net.module.util. |
| 18 | package android.net.util; |
| 19 | |
| 20 | import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR; |
| 21 | import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT; |
| 22 | |
| 23 | import android.os.Handler; |
| 24 | import android.os.Looper; |
| 25 | import android.os.MessageQueue; |
| 26 | import android.system.ErrnoException; |
| 27 | import android.system.OsConstants; |
| 28 | import android.util.Log; |
| 29 | |
| 30 | import android.annotation.NonNull; |
| 31 | import android.annotation.Nullable; |
| 32 | |
| 33 | import com.android.internal.annotations.VisibleForTesting; |
| 34 | |
| 35 | import java.io.FileDescriptor; |
| 36 | import java.io.IOException; |
| 37 | |
| 38 | |
| 39 | /** |
| 40 | * This class encapsulates the mechanics of registering a file descriptor |
| 41 | * with a thread's Looper and handling read events (and errors). |
| 42 | * |
| 43 | * Subclasses MUST implement createFd() and SHOULD override handlePacket(). They MAY override |
| 44 | * onStop() and onStart(). |
| 45 | * |
| 46 | * Subclasses can expect a call life-cycle like the following: |
| 47 | * |
| 48 | * [1] when a client calls start(), createFd() is called, followed by the onStart() hook if all |
| 49 | * goes well. Implementations may override onStart() for additional initialization. |
| 50 | * |
| 51 | * [2] yield, waiting for read event or error notification: |
| 52 | * |
| 53 | * [a] readPacket() && handlePacket() |
| 54 | * |
| 55 | * [b] if (no error): |
| 56 | * goto 2 |
| 57 | * else: |
| 58 | * goto 3 |
| 59 | * |
| 60 | * [3] when a client calls stop(), the onStop() hook is called (unless already stopped or never |
| 61 | * started). Implementations may override onStop() for additional cleanup. |
| 62 | * |
| 63 | * The packet receive buffer is recycled on every read call, so subclasses |
| 64 | * should make any copies they would like inside their handlePacket() |
| 65 | * implementation. |
| 66 | * |
| 67 | * All public methods MUST only be called from the same thread with which |
| 68 | * the Handler constructor argument is associated. |
| 69 | * |
| 70 | * @param <BufferType> the type of the buffer used to read data. |
| 71 | */ |
| 72 | public abstract class FdEventsReader<BufferType> { |
| 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 | |
| 86 | protected static void closeFd(FileDescriptor fd) { |
| 87 | try { |
| 88 | SocketUtils.closeSocket(fd); |
| 89 | } catch (IOException ignored) { |
| 90 | } |
| 91 | } |
| 92 | |
| 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 |
| 101 | protected MessageQueue getMessageQueue() { |
| 102 | return mQueue; |
| 103 | } |
| 104 | |
| 105 | /** Start this FdEventsReader. */ |
| 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. */ |
| 115 | public void stop() { |
| 116 | if (!onCorrectThread()) { |
| 117 | throw new IllegalStateException("stop() called from off-thread"); |
| 118 | } |
| 119 | |
| 120 | unregisterAndDestroyFd(); |
| 121 | } |
| 122 | |
| 123 | @NonNull |
| 124 | public Handler getHandler() { |
| 125 | return mHandler; |
| 126 | } |
| 127 | |
| 128 | protected abstract int recvBufSize(@NonNull BufferType buffer); |
| 129 | |
| 130 | /** Returns the size of the receive buffer. */ |
| 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 | */ |
| 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 |
| 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 | */ |
| 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 | */ |
| 168 | protected void handlePacket(@NonNull BufferType recvbuf, int length) {} |
| 169 | |
| 170 | /** |
| 171 | * Called by the main loop to log errors. In some cases |e| may be null. |
| 172 | */ |
| 173 | protected void logError(@NonNull String msg, @Nullable Exception e) {} |
| 174 | |
| 175 | /** |
| 176 | * Called by start(), if successful, just prior to returning. |
| 177 | */ |
| 178 | protected void onStart() {} |
| 179 | |
| 180 | /** |
| 181 | * Called by stop() just prior to returning. |
| 182 | */ |
| 183 | protected void onStop() {} |
| 184 | |
| 185 | private boolean createAndRegisterFd() { |
| 186 | if (mFd != null) return true; |
| 187 | |
| 188 | try { |
| 189 | mFd = createFd(); |
| 190 | } catch (Exception e) { |
| 191 | logError("Failed to create socket: ", e); |
| 192 | closeFd(mFd); |
| 193 | mFd = null; |
| 194 | } |
| 195 | |
| 196 | if (mFd == null) return false; |
| 197 | |
| 198 | getMessageQueue().addOnFileDescriptorEventListener( |
| 199 | mFd, |
| 200 | FD_EVENTS, |
| 201 | (fd, events) -> { |
| 202 | // Always call handleInput() so read/recvfrom are given |
| 203 | // a proper chance to encounter a meaningful errno and |
| 204 | // perhaps log a useful error message. |
| 205 | if (!isRunning() || !handleInput()) { |
| 206 | unregisterAndDestroyFd(); |
| 207 | return UNREGISTER_THIS_FD; |
| 208 | } |
| 209 | return FD_EVENTS; |
| 210 | }); |
| 211 | onStart(); |
| 212 | return true; |
| 213 | } |
| 214 | |
| 215 | private boolean isRunning() { |
| 216 | return (mFd != null) && mFd.valid(); |
| 217 | } |
| 218 | |
| 219 | // Keep trying to read until we get EAGAIN/EWOULDBLOCK or some fatal error. |
| 220 | private boolean handleInput() { |
| 221 | while (isRunning()) { |
| 222 | final int bytesRead; |
| 223 | |
| 224 | try { |
| 225 | bytesRead = readPacket(mFd, mBuffer); |
| 226 | if (bytesRead < 1) { |
| 227 | if (isRunning()) logError("Socket closed, exiting", null); |
| 228 | break; |
| 229 | } |
| 230 | mPacketsReceived++; |
| 231 | } catch (ErrnoException e) { |
| 232 | if (e.errno == OsConstants.EAGAIN) { |
| 233 | // We've read everything there is to read this time around. |
| 234 | return true; |
| 235 | } else if (e.errno == OsConstants.EINTR) { |
| 236 | continue; |
| 237 | } else { |
| 238 | if (isRunning()) logError("readPacket error: ", e); |
| 239 | break; |
| 240 | } |
| 241 | } catch (Exception e) { |
| 242 | if (isRunning()) logError("readPacket error: ", e); |
| 243 | break; |
| 244 | } |
| 245 | |
| 246 | try { |
| 247 | handlePacket(mBuffer, bytesRead); |
| 248 | } catch (Exception e) { |
| 249 | logError("handlePacket error: ", e); |
| 250 | Log.wtf(FdEventsReader.class.getSimpleName(), "Error handling packet", e); |
| 251 | } |
| 252 | } |
| 253 | |
| 254 | return false; |
| 255 | } |
| 256 | |
| 257 | private void unregisterAndDestroyFd() { |
| 258 | if (mFd == null) return; |
| 259 | |
| 260 | getMessageQueue().removeOnFileDescriptorEventListener(mFd); |
| 261 | closeFd(mFd); |
| 262 | mFd = null; |
| 263 | onStop(); |
| 264 | } |
| 265 | |
| 266 | private boolean onCorrectThread() { |
| 267 | return (mHandler.getLooper() == Looper.myLooper()); |
| 268 | } |
| 269 | } |