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 
SapRilReceiver(Handler SapServerMsgHandler, Handler sapServiceHandler)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      */
openRilBtSocket()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 
getRilBtOutStream()96     public CodedOutputStreamMicro getRilBtOutStream() {
97         return mRilBtOutStream;
98     }
99 
100     /**
101      * Notify SapServer that this class is ready for shutdown.
102      */
notifyShutdown()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      */
shutdown()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      */
readMessage(InputStream is, byte[] buffer)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
run()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      */
sendRilConnectMessage()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      */
sendClientMessage(SapMessage sapMsg)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      */
sendShutdownMessage()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      */
sendRilIndMessage(SapMessage sapMsg)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