Home | History | Annotate | Download | only in util
      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.
     52     public static final void closeSocket(@Nullable FileDescriptor fd) {
     53         try {
     54             IoBridge.closeAndSignalBlockedThreads(fd);
     55         } catch (IOException ignored) {}
     56     }
     57 
     58     protected BlockingSocketReader() {
     59         this(DEFAULT_RECV_BUF_SIZE);
     60     }
     61 
     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 
     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 
     87     public final void stop() {
     88         mRunning = false;
     89         closeSocket(mSocket);
     90         mSocket = null;
     91     }
     92 
     93     public final boolean isRunning() { return mRunning; }
     94 
     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      */
    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      */
    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      */
    113     protected void logError(String msg, Exception e) {}
    114 
    115     /**
    116      * Called by the main loop just prior to exiting.
    117      */
    118     protected void onExit() {}
    119 
    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