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