• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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