summaryrefslogtreecommitdiffstats
path: root/src/com/android/bluetooth/sap/SapRilReceiver.java
blob: 54251934431e2094d554aadb7c2ce0ae83d70c9e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
package com.android.bluetooth.sap;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.android.btsap.SapApi.MsgHeader;

import com.google.protobuf.micro.CodedInputStreamMicro;
import com.google.protobuf.micro.CodedOutputStreamMicro;

import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.Handler;
import android.os.Message;
import android.util.Log;

public class SapRilReceiver implements Runnable {

    private static final String TAG = "SapRilReceiver";
    public static final boolean DEBUG = true;
    public static final boolean VERBOSE = Log.isLoggable(SapService.LOG_TAG, Log.VERBOSE);

    private static final String SOCKET_NAME_RIL_BT = "sap_uim_socket1";
    // match with constant in ril.cpp - as in RIL.java
    private static final int SOCKET_OPEN_RETRY_MILLIS = 4 * 1000;

    LocalSocket mSocket = null;
    CodedOutputStreamMicro mRilBtOutStream = null;
    InputStream mRilBtInStream = null;
    private Handler mSapServerMsgHandler = null;
    private Handler mSapServiceHandler = null;

    public static final int RIL_MAX_COMMAND_BYTES = (8 * 1024);
    byte[] buffer = new byte[RIL_MAX_COMMAND_BYTES];
    boolean mShutdown = false;

    public SapRilReceiver(Handler SapServerMsgHandler, Handler sapServiceHandler) {
        mSapServerMsgHandler = SapServerMsgHandler;
        mSapServiceHandler = sapServiceHandler;
    }

    /**
     * Open the RIL-BT socket in rild. Will continuously try to open the BT socket until
     * success. (Based on the approach used to open the rild socket in telephony)
     * @return The socket handle
     */
    private LocalSocket openRilBtSocket() {
        int retryCount = 0;
        LocalSocket rilSocket = null;

        for (;mShutdown == false;) {
            LocalSocketAddress address;

            try {
                rilSocket = new LocalSocket();
                address = new LocalSocketAddress(SOCKET_NAME_RIL_BT,
                        LocalSocketAddress.Namespace.RESERVED);
                rilSocket.connect(address);
                break; // Socket opened
            } catch (IOException ex){
                try {
                    if (rilSocket != null) {
                        rilSocket.close();
                    }
                } catch (IOException ex2) {
                    //ignore failure to close after failure to connect
                }

                // don't print an error message after the the first time
                // or after the 8th time
                if (retryCount == 8) {
                    Log.e (TAG,
                        "Couldn't find '" + SOCKET_NAME_RIL_BT
                        + "' socket after " + retryCount
                        + " times, continuing to retry silently");
                } else if (retryCount > 0 && retryCount < 8) {
                    Log.i (TAG,
                        "Couldn't find '" + SOCKET_NAME_RIL_BT
                        + "' socket; retrying after timeout");
                    Log.w(TAG, ex);
                }

                try {
                    Thread.sleep(SOCKET_OPEN_RETRY_MILLIS);
                } catch (InterruptedException er) {
                }

                retryCount++;
                continue;
            }
        }
        if(mShutdown) {
            if(DEBUG) Log.d(TAG, "Shutdown received before RIL socket was opened.");
        }
        return rilSocket;
    }


    public CodedOutputStreamMicro getRilBtOutStream() {
        return mRilBtOutStream;
    }

    /**
     * Notify SapServer that this class is ready for shutdown.
     */
    private void notifyShutdown() {
        if (DEBUG) Log.d(TAG, "notifyShutdown()");
        // If we are already shutdown, don't bother sending a notification.
        synchronized (this) {
            if (mSocket != null) sendShutdownMessage();
        }
    }

    /**
     * This will terminate the SapRilReceiver thread, by closing the RIL-BT in-/output
     * streams.
     */
    public void shutdown() {
        if (DEBUG) Log.d(TAG, "shutdown()");

        /* On Android you need to close the IOstreams using Socket.shutdown*
         * The IOstream close must not be used, as it some how decouples the
         * stream from the socket, and when the socket is closed, the pending
         * reads never return nor throw and exception.
         * Hence here we use the shutdown method: */
        synchronized (this) {
            if (mSocket != null) {
                try {
                    mSocket.shutdownOutput();
                } catch (IOException e) {}
                try {
                    mSocket.shutdownInput();
                } catch (IOException e) {}
                try {
                    mSocket.close();
                } catch (IOException ex) {
                    Log.e(TAG,"Uncaught exception", ex);
                } finally {
                    mSocket = null;
                }
            }
        }
        mShutdown = true;
    }

    /**
     * Read the message into buffer
     * @param is
     * @param buffer
     * @return the length of the message
     * @throws IOException
     */
    private static int readMessage(InputStream is, byte[] buffer) throws IOException {
        int countRead;
        int offset;
        int remaining;
        int messageLength;

        // Read in the length of the message
        offset = 0;
        remaining = 4;
        do {
            countRead = is.read(buffer, offset, remaining);

            if (countRead < 0 ) {
                Log.e(TAG, "Hit EOS reading message length");
                return -1;
            }

            offset += countRead;
            remaining -= countRead;
        } while (remaining > 0);

        messageLength = ((buffer[0] & 0xff) << 24)
                | ((buffer[1] & 0xff) << 16)
                | ((buffer[2] & 0xff) << 8)
                | (buffer[3] & 0xff);
        Log.e(TAG,"Message length found to be: "+messageLength);
        // Read the message
        offset = 0;
        remaining = messageLength;
        do {
            countRead = is.read(buffer, offset, remaining);

            if (countRead < 0 ) {
                Log.e(TAG, "Hit EOS reading message.  messageLength=" + messageLength
                        + " remaining=" + remaining);
                return -1;
            }

            offset += countRead;
            remaining -= countRead;
        } while (remaining > 0);

        return messageLength;
    }

    /**
     * The RIL reader thread. Will handle open of the RIL-BT socket, and notify
     * SapServer when done.
     */
    @Override
    public void run() {

        try {
            int length = 0;
            mShutdown = false; // Currently only used to break openRilBtSocket()
            if(VERBOSE) Log.i(TAG, "Starting RilBtReceiverThread...");

            mSocket = openRilBtSocket();
            mRilBtInStream = mSocket.getInputStream();
            mRilBtOutStream = CodedOutputStreamMicro.newInstance(mSocket.getOutputStream());

            // Notify the SapServer that we have connected to the RilBtSocket
            sendRilConnectMessage();

            // The main loop - read messages and forward to SAP server
            for (;;) {
                SapMessage sapMsg = null;
                MsgHeader rilMsg;

                if (VERBOSE) Log.v(TAG, "Waiting for incoming message...");
                length = readMessage(mRilBtInStream, buffer);

                SapService.notifyUpdateWakeLock(mSapServiceHandler);

                if (length == -1) {
                    if (DEBUG) Log.d(TAG, "EOF reached - closing down.");
                    break;
                }

                CodedInputStreamMicro msgStream =
                        CodedInputStreamMicro.newInstance(buffer, 0, length);

                rilMsg = MsgHeader.parseFrom(msgStream);

                if (VERBOSE) Log.v(TAG, "Message received.");

                sapMsg = SapMessage.newInstance(rilMsg);

                if (sapMsg != null && sapMsg.getMsgType() != SapMessage.INVALID_VALUE)
                {
                    if (sapMsg.getMsgType() < SapMessage.ID_RIL_BASE) {
                        sendClientMessage(sapMsg);
                    } else {
                        sendRilIndMessage(sapMsg);
                    }
                } // else simply ignore it
            }

        } catch (IOException e) {
            notifyShutdown(); /* Only needed in case of a connection error */
            Log.i(TAG, "'" + SOCKET_NAME_RIL_BT + "' socket inputStream closed", e);

        } finally {
            Log.i(TAG, "Disconnected from '" + SOCKET_NAME_RIL_BT + "' socket");
        }
    }

    /**
     * Notify SapServer that the RIL socket is connected
     */
    private void sendRilConnectMessage() {
        if (mSapServerMsgHandler != null) {
            mSapServerMsgHandler.sendEmptyMessage(SapServer.SAP_MSG_RIL_CONNECT);
        }
    }

    /**
     * Send reply (solicited) message from the RIL to the Sap Server Handler Thread
     * @param sapMsg The message to send
     */
    private void sendClientMessage(SapMessage sapMsg) {
        Message newMsg = mSapServerMsgHandler.obtainMessage(SapServer.SAP_MSG_RFC_REPLY, sapMsg);
        mSapServerMsgHandler.sendMessage(newMsg);
    }

    /**
     * Send a shutdown signal to SapServer to indicate the
     */
    private void sendShutdownMessage() {
        if (mSapServerMsgHandler != null) {
            mSapServerMsgHandler.sendEmptyMessage(SapServer.SAP_RIL_SOCK_CLOSED);
        }
    }

    /**
     * Send indication (unsolicited) message from RIL to the Sap Server Handler Thread
     * @param sapMsg The message to send
     */
    private void sendRilIndMessage(SapMessage sapMsg) {
        Message newMsg = mSapServerMsgHandler.obtainMessage(SapServer.SAP_MSG_RIL_IND, sapMsg);
        mSapServerMsgHandler.sendMessage(newMsg);
    }

}