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