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