• 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 android.annotation.Nullable;
20 import android.system.ErrnoException;
21 import android.system.Os;
22 import android.system.OsConstants;
23 
24 import libcore.io.IoBridge;
25 
26 import java.io.FileDescriptor;
27 import java.io.InterruptedIOException;
28 import java.io.IOException;
29 
30 
31 /**
32  * A thread that reads from a socket and passes the received packets to a
33  * subclass's handlePacket() method.  The packet receive buffer is recycled
34  * on every read call, so subclasses should make any copies they would like
35  * inside their handlePacket() implementation.
36  *
37  * All public methods may be called from any thread.
38  *
39  * @hide
40  */
41 public abstract class BlockingSocketReader {
42     public static final int DEFAULT_RECV_BUF_SIZE = 2 * 1024;
43 
44     private final byte[] mPacket;
45     private final Thread mThread;
46     private volatile FileDescriptor mSocket;
47     private volatile boolean mRunning;
48     private volatile long mPacketsReceived;
49 
50     // Make it slightly easier for subclasses to properly close a socket
51     // without having to know this incantation.
closeSocket(@ullable FileDescriptor fd)52     public static final void closeSocket(@Nullable FileDescriptor fd) {
53         try {
54             IoBridge.closeAndSignalBlockedThreads(fd);
55         } catch (IOException ignored) {}
56     }
57 
BlockingSocketReader()58     protected BlockingSocketReader() {
59         this(DEFAULT_RECV_BUF_SIZE);
60     }
61 
BlockingSocketReader(int recvbufsize)62     protected BlockingSocketReader(int recvbufsize) {
63         if (recvbufsize < DEFAULT_RECV_BUF_SIZE) {
64             recvbufsize = DEFAULT_RECV_BUF_SIZE;
65         }
66         mPacket = new byte[recvbufsize];
67         mThread = new Thread(() -> { mainLoop(); });
68     }
69 
start()70     public final boolean start() {
71         if (mSocket != null) return false;
72 
73         try {
74             mSocket = createSocket();
75         } catch (Exception e) {
76             logError("Failed to create socket: ", e);
77             return false;
78         }
79 
80         if (mSocket == null) return false;
81 
82         mRunning = true;
83         mThread.start();
84         return true;
85     }
86 
stop()87     public final void stop() {
88         mRunning = false;
89         closeSocket(mSocket);
90         mSocket = null;
91     }
92 
isRunning()93     public final boolean isRunning() { return mRunning; }
94 
numPacketsReceived()95     public final long numPacketsReceived() { return mPacketsReceived; }
96 
97     /**
98      * Subclasses MUST create the listening socket here, including setting
99      * all desired socket options, interface or address/port binding, etc.
100      */
createSocket()101     protected abstract FileDescriptor createSocket();
102 
103     /**
104      * Called by the main loop for every packet.  Any desired copies of
105      * |recvbuf| should be made in here, and the underlying byte array is
106      * reused across all reads.
107      */
handlePacket(byte[] recvbuf, int length)108     protected void handlePacket(byte[] recvbuf, int length) {}
109 
110     /**
111      * Called by the main loop to log errors.  In some cases |e| may be null.
112      */
logError(String msg, Exception e)113     protected void logError(String msg, Exception e) {}
114 
115     /**
116      * Called by the main loop just prior to exiting.
117      */
onExit()118     protected void onExit() {}
119 
mainLoop()120     private final void mainLoop() {
121         while (isRunning()) {
122             final int bytesRead;
123 
124             try {
125                 // Blocking read.
126                 // TODO: See if this can be converted to recvfrom.
127                 bytesRead = Os.read(mSocket, mPacket, 0, mPacket.length);
128                 if (bytesRead < 1) {
129                     if (isRunning()) logError("Socket closed, exiting", null);
130                     break;
131                 }
132                 mPacketsReceived++;
133             } catch (ErrnoException e) {
134                 if (e.errno != OsConstants.EINTR) {
135                     if (isRunning()) logError("read error: ", e);
136                     break;
137                 }
138                 continue;
139             } catch (IOException ioe) {
140                 if (isRunning()) logError("read error: ", ioe);
141                 continue;
142             }
143 
144             try {
145                 handlePacket(mPacket, bytesRead);
146             } catch (Exception e) {
147                 logError("Unexpected exception: ", e);
148                 break;
149             }
150         }
151 
152         stop();
153         onExit();
154     }
155 }
156