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