Home | History | Annotate | Download | only in sap
      1 package com.android.bluetooth.sap;
      2 
      3 import java.io.IOException;
      4 import java.io.InputStream;
      5 import java.io.OutputStream;
      6 
      7 import org.android.btsap.SapApi.MsgHeader;
      8 
      9 import com.google.protobuf.micro.CodedInputStreamMicro;
     10 import com.google.protobuf.micro.CodedOutputStreamMicro;
     11 
     12 import android.net.LocalSocket;
     13 import android.net.LocalSocketAddress;
     14 import android.os.Handler;
     15 import android.os.Message;
     16 import android.util.Log;
     17 
     18 public class SapRilReceiver implements Runnable {
     19 
     20     private static final String TAG = "SapRilReceiver";
     21     public static final boolean DEBUG = true;
     22     public static final boolean VERBOSE = true;
     23 
     24     private static final String SOCKET_NAME_RIL_BT = "sap_uim_socket1";
     25     // match with constant in ril.cpp - as in RIL.java
     26     private static final int SOCKET_OPEN_RETRY_MILLIS = 4 * 1000;
     27 
     28     LocalSocket mSocket = null;
     29     CodedOutputStreamMicro mRilBtOutStream = null;
     30     InputStream mRilBtInStream = null;
     31     private Handler mSapServerMsgHandler = null;
     32     private Handler mSapServiceHandler = null;
     33 
     34     public static final int RIL_MAX_COMMAND_BYTES = (8 * 1024);
     35     byte[] buffer = new byte[RIL_MAX_COMMAND_BYTES];
     36 
     37     public SapRilReceiver(Handler SapServerMsgHandler, Handler sapServiceHandler) {
     38         mSapServerMsgHandler = SapServerMsgHandler;
     39         mSapServiceHandler = sapServiceHandler;
     40     }
     41 
     42     /**
     43      * Open the RIL-BT socket in rild. Will continuously try to open the BT socket until
     44      * success. (Based on the approach used to open the rild socket in telephony)
     45      * @return The socket handle
     46      */
     47     public static LocalSocket openRilBtSocket() {
     48         int retryCount = 0;
     49         LocalSocket rilSocket = null;
     50 
     51         for (;;) {
     52             LocalSocketAddress address;
     53 
     54             try {
     55                 rilSocket = new LocalSocket();
     56                 address = new LocalSocketAddress(SOCKET_NAME_RIL_BT,
     57                         LocalSocketAddress.Namespace.RESERVED);
     58                 rilSocket.connect(address);
     59                 break; // Socket opened
     60             } catch (IOException ex){
     61                 try {
     62                     if (rilSocket != null) {
     63                         rilSocket.close();
     64                     }
     65                 } catch (IOException ex2) {
     66                     //ignore failure to close after failure to connect
     67                 }
     68 
     69                 // don't print an error message after the the first time
     70                 // or after the 8th time
     71                 if (retryCount == 8) {
     72                     Log.e (TAG,
     73                         "Couldn't find '" + SOCKET_NAME_RIL_BT
     74                         + "' socket after " + retryCount
     75                         + " times, continuing to retry silently");
     76                 } else if (retryCount > 0 && retryCount < 8) {
     77                     Log.i (TAG,
     78                         "Couldn't find '" + SOCKET_NAME_RIL_BT
     79                         + "' socket; retrying after timeout");
     80                     if (VERBOSE) Log.w(TAG, ex);
     81                 }
     82 
     83                 try {
     84                     Thread.sleep(SOCKET_OPEN_RETRY_MILLIS);
     85                 } catch (InterruptedException er) {
     86                 }
     87 
     88                 retryCount++;
     89                 continue;
     90             }
     91         }
     92         return rilSocket;
     93     }
     94 
     95 
     96     public CodedOutputStreamMicro getRilBtOutStream() {
     97         return mRilBtOutStream;
     98     }
     99 
    100     /**
    101      * Notify SapServer that this class is ready for shutdown.
    102      */
    103     private void notifyShutdown() {
    104         if (DEBUG) Log.i(TAG, "notifyShutdown()");
    105         // If we are already shutdown, don't bother sending a notification.
    106         synchronized (this) {
    107             if (mSocket != null) sendShutdownMessage();
    108         }
    109     }
    110 
    111     /**
    112      * This will terminate the SapRilReceiver thread, by closing the RIL-BT in-/output
    113      * streams.
    114      */
    115     public void shutdown() {
    116         if (DEBUG) Log.i(TAG, "shutdown()");
    117 
    118         /* On Android you need to close the IOstreams using Socket.shutdown*
    119          * The IOstream close must not be used, as it some how decouples the
    120          * stream from the socket, and when the socket is closed, the pending
    121          * reads never return nor throw and exception.
    122          * Hence here we use the shutdown method: */
    123         synchronized (this) {
    124             if (mSocket != null) {
    125                 try {
    126                     mSocket.shutdownOutput();
    127                 } catch (IOException e) {}
    128                 try {
    129                     mSocket.shutdownInput();
    130                 } catch (IOException e) {}
    131                 try {
    132                     mSocket.close();
    133                 } catch (IOException ex) {
    134                     if (VERBOSE) Log.e(TAG,"Uncaught exception", ex);
    135                 } finally {
    136                     mSocket = null;
    137                 }
    138             }
    139         }
    140     }
    141 
    142     /**
    143      * Read the message into buffer
    144      * @param is
    145      * @param buffer
    146      * @return the length of the message
    147      * @throws IOException
    148      */
    149     private static int readMessage(InputStream is, byte[] buffer) throws IOException {
    150         int countRead;
    151         int offset;
    152         int remaining;
    153         int messageLength;
    154 
    155         // Read in the length of the message
    156         offset = 0;
    157         remaining = 4;
    158         do {
    159             countRead = is.read(buffer, offset, remaining);
    160 
    161             if (countRead < 0 ) {
    162                 Log.e(TAG, "Hit EOS reading message length");
    163                 return -1;
    164             }
    165 
    166             offset += countRead;
    167             remaining -= countRead;
    168         } while (remaining > 0);
    169 
    170         messageLength = ((buffer[0] & 0xff) << 24)
    171                 | ((buffer[1] & 0xff) << 16)
    172                 | ((buffer[2] & 0xff) << 8)
    173                 | (buffer[3] & 0xff);
    174         if (VERBOSE) Log.e(TAG,"Message length found to be: "+messageLength);
    175         // Read the message
    176         offset = 0;
    177         remaining = messageLength;
    178         do {
    179             countRead = is.read(buffer, offset, remaining);
    180 
    181             if (countRead < 0 ) {
    182                 Log.e(TAG, "Hit EOS reading message.  messageLength=" + messageLength
    183                         + " remaining=" + remaining);
    184                 return -1;
    185             }
    186 
    187             offset += countRead;
    188             remaining -= countRead;
    189         } while (remaining > 0);
    190 
    191         return messageLength;
    192     }
    193 
    194     /**
    195      * The RIL reader thread. Will handle open of the RIL-BT socket, and notify
    196      * SapServer when done.
    197      */
    198     @Override
    199     public void run() {
    200 
    201         try {
    202             if (VERBOSE) Log.i(TAG, "Starting RilBtReceiverThread...");
    203 
    204             mSocket = openRilBtSocket();
    205             mRilBtInStream = mSocket.getInputStream();
    206             mRilBtOutStream = CodedOutputStreamMicro.newInstance(mSocket.getOutputStream());
    207 
    208             // Notify the SapServer that we have connected to the RilBtSocket
    209             sendRilConnectMessage();
    210 
    211             // The main loop - read messages and forward to SAP server
    212             for (;;) {
    213                 SapMessage sapMsg = null;
    214                 MsgHeader rilMsg;
    215 
    216                 if (VERBOSE) Log.i(TAG, "Waiting for incoming message...");
    217                 int length = readMessage(mRilBtInStream, buffer);
    218 
    219                 SapService.notifyUpdateWakeLock(mSapServiceHandler);
    220 
    221                 if (length == -1) {
    222                     if (DEBUG) Log.i(TAG, "EOF reached - closing down.");
    223                     break;
    224                 }
    225 
    226                 CodedInputStreamMicro msgStream =
    227                         CodedInputStreamMicro.newInstance(buffer, 0, length);
    228 
    229                 rilMsg = MsgHeader.parseFrom(msgStream);
    230 
    231                 if (VERBOSE) Log.i(TAG, "Message received.");
    232 
    233                 sapMsg = SapMessage.newInstance(rilMsg);
    234 
    235                 if (sapMsg != null && sapMsg.getMsgType() != SapMessage.INVALID_VALUE)
    236                 {
    237                     if (sapMsg.getMsgType() < SapMessage.ID_RIL_BASE) {
    238                         sendClientMessage(sapMsg);
    239                     } else {
    240                         sendRilIndMessage(sapMsg);
    241                     }
    242                 } // else simply ignore it
    243             }
    244 
    245         } catch (IOException e) {
    246             notifyShutdown(); /* Only needed in case of a connection error */
    247             Log.i(TAG, "'" + SOCKET_NAME_RIL_BT + "' socket inputStream closed", e);
    248 
    249         } finally {
    250             Log.i(TAG, "Disconnected from '" + SOCKET_NAME_RIL_BT + "' socket");
    251         }
    252     }
    253 
    254     /**
    255      * Notify SapServer that the RIL socket is connected
    256      */
    257     private void sendRilConnectMessage() {
    258         if (mSapServerMsgHandler != null) {
    259             mSapServerMsgHandler.sendEmptyMessage(SapServer.SAP_MSG_RIL_CONNECT);
    260         }
    261     }
    262 
    263     /**
    264      * Send reply (solicited) message from the RIL to the Sap Server Handler Thread
    265      * @param sapMsg The message to send
    266      */
    267     private void sendClientMessage(SapMessage sapMsg) {
    268         Message newMsg = mSapServerMsgHandler.obtainMessage(SapServer.SAP_MSG_RFC_REPLY, sapMsg);
    269         mSapServerMsgHandler.sendMessage(newMsg);
    270     }
    271 
    272     /**
    273      * Send a shutdown signal to SapServer to indicate the
    274      */
    275     private void sendShutdownMessage() {
    276         if (mSapServerMsgHandler != null) {
    277             mSapServerMsgHandler.sendEmptyMessage(SapServer.SAP_RIL_SOCK_CLOSED);
    278         }
    279     }
    280 
    281     /**
    282      * Send indication (unsolicited) message from RIL to the Sap Server Handler Thread
    283      * @param sapMsg The message to send
    284      */
    285     private void sendRilIndMessage(SapMessage sapMsg) {
    286         Message newMsg = mSapServerMsgHandler.obtainMessage(SapServer.SAP_MSG_RIL_IND, sapMsg);
    287         mSapServerMsgHandler.sendMessage(newMsg);
    288     }
    289 
    290 }
    291