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