1package com.android.bluetooth.sap;
2
3import java.io.IOException;
4import java.io.InputStream;
5import java.util.ArrayList;
6import java.util.List;
7import java.util.concurrent.atomic.AtomicLong;
8
9import org.android.btsap.SapApi.MsgHeader;
10
11import com.google.protobuf.micro.CodedInputStreamMicro;
12import com.google.protobuf.micro.CodedOutputStreamMicro;
13
14import android.hardware.radio.V1_0.ISap;
15import android.hardware.radio.V1_0.ISapCallback;
16
17import android.net.LocalSocket;
18import android.net.LocalSocketAddress;
19import android.os.Handler;
20import android.os.HwBinder;
21import android.os.Message;
22import android.os.RemoteException;
23import android.util.Log;
24
25public class SapRilReceiver {
26    private static final String TAG = "SapRilReceiver";
27    public static final boolean DEBUG = true;
28    public static final boolean VERBOSE = true;
29
30    private static final String SERVICE_NAME_RIL_BT = "slot1";
31    // match with constant in ril.cpp - as in RIL.java
32    private static final int SOCKET_OPEN_RETRY_MILLIS = 4 * 1000;
33
34    SapCallback mSapCallback;
35    volatile ISap mSapProxy = null;
36    Object mSapProxyLock = new Object();
37    final AtomicLong mSapProxyCookie = new AtomicLong(0);
38    final SapProxyDeathRecipient mSapProxyDeathRecipient;
39
40    private Handler mSapServerMsgHandler = null;
41    private Handler mSapServiceHandler = null;
42
43    public static final int RIL_MAX_COMMAND_BYTES = (8 * 1024);
44    byte[] buffer = new byte[RIL_MAX_COMMAND_BYTES];
45
46    final class SapProxyDeathRecipient implements HwBinder.DeathRecipient {
47        @Override
48        public void serviceDied(long cookie) {
49            // Deal with service going away
50            Log.d(TAG, "serviceDied");
51            // todo: temp hack to send delayed message so that rild is back up by then
52            // mSapHandler.sendMessage(mSapHandler.obtainMessage(EVENT_SAP_PROXY_DEAD, cookie));
53            mSapServerMsgHandler.sendMessageDelayed(
54                    mSapServerMsgHandler.obtainMessage(SapServer.SAP_PROXY_DEAD, cookie),
55                    SapServer.ISAP_GET_SERVICE_DELAY_MILLIS);
56        }
57    }
58
59    private void sendSapMessage(SapMessage sapMessage) {
60        if (sapMessage.getMsgType() < SapMessage.ID_RIL_BASE) {
61            sendClientMessage(sapMessage);
62        } else {
63            sendRilIndMessage(sapMessage);
64        }
65    }
66
67    private void removeOngoingReqAndSendMessage(int token, SapMessage sapMessage) {
68        Integer reqType = SapMessage.sOngoingRequests.remove(token);
69        if (VERBOSE) {
70            Log.d(TAG, "removeOngoingReqAndSendMessage: token " + token + " reqType "
71                            + (reqType == null ? "null" : SapMessage.getMsgTypeName(reqType)));
72        }
73        sendSapMessage(sapMessage);
74    }
75
76    class SapCallback extends ISapCallback.Stub {
77        public void connectResponse(int token, int sapConnectRsp, int maxMsgSize) {
78            Log.d(TAG, "connectResponse: token " + token + " sapConnectRsp " + sapConnectRsp
79                            + " maxMsgSize " + maxMsgSize);
80            SapService.notifyUpdateWakeLock(mSapServiceHandler);
81            SapMessage sapMessage = new SapMessage(SapMessage.ID_CONNECT_RESP);
82            sapMessage.setConnectionStatus(sapConnectRsp);
83            if (sapConnectRsp == SapMessage.CON_STATUS_ERROR_MAX_MSG_SIZE_UNSUPPORTED) {
84                sapMessage.setMaxMsgSize(maxMsgSize);
85            }
86            sapMessage.setResultCode(SapMessage.INVALID_VALUE);
87            removeOngoingReqAndSendMessage(token, sapMessage);
88        }
89
90        public void disconnectResponse(int token) {
91            Log.d(TAG, "disconnectResponse: token " + token);
92            SapService.notifyUpdateWakeLock(mSapServiceHandler);
93            SapMessage sapMessage = new SapMessage(SapMessage.ID_DISCONNECT_RESP);
94            sapMessage.setResultCode(SapMessage.INVALID_VALUE);
95            removeOngoingReqAndSendMessage(token, sapMessage);
96        }
97
98        public void disconnectIndication(int token, int disconnectType) {
99            Log.d(TAG,
100                    "disconnectIndication: token " + token + " disconnectType " + disconnectType);
101            SapService.notifyUpdateWakeLock(mSapServiceHandler);
102            SapMessage sapMessage = new SapMessage(SapMessage.ID_RIL_UNSOL_DISCONNECT_IND);
103            sapMessage.setDisconnectionType(disconnectType);
104            sendSapMessage(sapMessage);
105        }
106
107        public void apduResponse(int token, int resultCode, ArrayList<Byte> apduRsp) {
108            Log.d(TAG, "apduResponse: token " + token);
109            SapService.notifyUpdateWakeLock(mSapServiceHandler);
110            SapMessage sapMessage = new SapMessage(SapMessage.ID_TRANSFER_APDU_RESP);
111            sapMessage.setResultCode(resultCode);
112            if (resultCode == SapMessage.RESULT_OK) {
113                sapMessage.setApduResp(arrayListToPrimitiveArray(apduRsp));
114            }
115            removeOngoingReqAndSendMessage(token, sapMessage);
116        }
117
118        public void transferAtrResponse(int token, int resultCode, ArrayList<Byte> atr) {
119            Log.d(TAG, "transferAtrResponse: token " + token + " resultCode " + resultCode);
120            SapService.notifyUpdateWakeLock(mSapServiceHandler);
121            SapMessage sapMessage = new SapMessage(SapMessage.ID_TRANSFER_ATR_RESP);
122            sapMessage.setResultCode(resultCode);
123            if (resultCode == SapMessage.RESULT_OK) {
124                sapMessage.setAtr(arrayListToPrimitiveArray(atr));
125            }
126            removeOngoingReqAndSendMessage(token, sapMessage);
127        }
128
129        public void powerResponse(int token, int resultCode) {
130            Log.d(TAG, "powerResponse: token " + token + " resultCode " + resultCode);
131            SapService.notifyUpdateWakeLock(mSapServiceHandler);
132            Integer reqType = SapMessage.sOngoingRequests.remove(token);
133            if (VERBOSE) {
134                Log.d(TAG, "powerResponse: reqType "
135                                + (reqType == null ? "null" : SapMessage.getMsgTypeName(reqType)));
136            }
137            SapMessage sapMessage;
138            if (reqType == SapMessage.ID_POWER_SIM_OFF_REQ) {
139                sapMessage = new SapMessage(SapMessage.ID_POWER_SIM_OFF_RESP);
140            } else if (reqType == SapMessage.ID_POWER_SIM_ON_REQ) {
141                sapMessage = new SapMessage(SapMessage.ID_POWER_SIM_ON_RESP);
142            } else {
143                return;
144            }
145            sapMessage.setResultCode(resultCode);
146            sendSapMessage(sapMessage);
147        }
148
149        public void resetSimResponse(int token, int resultCode) {
150            Log.d(TAG, "resetSimResponse: token " + token + " resultCode " + resultCode);
151            SapService.notifyUpdateWakeLock(mSapServiceHandler);
152            SapMessage sapMessage = new SapMessage(SapMessage.ID_RESET_SIM_RESP);
153            sapMessage.setResultCode(resultCode);
154            removeOngoingReqAndSendMessage(token, sapMessage);
155        }
156
157        public void statusIndication(int token, int status) {
158            Log.d(TAG, "statusIndication: token " + token + " status " + status);
159            SapService.notifyUpdateWakeLock(mSapServiceHandler);
160            SapMessage sapMessage = new SapMessage(SapMessage.ID_STATUS_IND);
161            sapMessage.setStatusChange(status);
162            sendSapMessage(sapMessage);
163        }
164
165        public void transferCardReaderStatusResponse(
166                int token, int resultCode, int cardReaderStatus) {
167            Log.d(TAG, "transferCardReaderStatusResponse: token " + token + " resultCode "
168                            + resultCode + " cardReaderStatus " + cardReaderStatus);
169            SapService.notifyUpdateWakeLock(mSapServiceHandler);
170            SapMessage sapMessage = new SapMessage(SapMessage.ID_TRANSFER_CARD_READER_STATUS_RESP);
171            sapMessage.setResultCode(resultCode);
172            if (resultCode == SapMessage.RESULT_OK) {
173                sapMessage.setCardReaderStatus(cardReaderStatus);
174            }
175            removeOngoingReqAndSendMessage(token, sapMessage);
176        }
177
178        public void errorResponse(int token) {
179            Log.d(TAG, "errorResponse: token " + token);
180            SapService.notifyUpdateWakeLock(mSapServiceHandler);
181            // Since ERROR_RESP isn't supported by createUnsolicited(), keeping behavior same here
182            // SapMessage sapMessage = new SapMessage(SapMessage.ID_ERROR_RESP);
183            SapMessage sapMessage = new SapMessage(SapMessage.ID_RIL_UNKNOWN);
184            sendSapMessage(sapMessage);
185        }
186
187        public void transferProtocolResponse(int token, int resultCode) {
188            Log.d(TAG, "transferProtocolResponse: token " + token + " resultCode " + resultCode);
189            SapService.notifyUpdateWakeLock(mSapServiceHandler);
190            SapMessage sapMessage = new SapMessage(SapMessage.ID_SET_TRANSPORT_PROTOCOL_RESP);
191            sapMessage.setResultCode(resultCode);
192            removeOngoingReqAndSendMessage(token, sapMessage);
193        }
194    }
195
196    public static byte[] arrayListToPrimitiveArray(List<Byte> bytes) {
197        byte[] ret = new byte[bytes.size()];
198        for (int i = 0; i < ret.length; i++) {
199            ret[i] = bytes.get(i);
200        }
201        return ret;
202    }
203
204    public Object getSapProxyLock() {
205        return mSapProxyLock;
206    }
207
208    public ISap getSapProxy() {
209        synchronized (mSapProxyLock) {
210            if (mSapProxy != null) {
211                return mSapProxy;
212            }
213
214            try {
215                mSapProxy = ISap.getService(SERVICE_NAME_RIL_BT);
216                if (mSapProxy != null) {
217                    mSapProxy.linkToDeath(
218                            mSapProxyDeathRecipient, mSapProxyCookie.incrementAndGet());
219                    mSapProxy.setCallback(mSapCallback);
220                } else {
221                    Log.e(TAG, "getSapProxy: mSapProxy == null");
222                }
223            } catch (RemoteException | RuntimeException e) {
224                mSapProxy = null;
225                Log.e(TAG, "getSapProxy: exception: " + e);
226            }
227
228            if (mSapProxy == null) {
229                // if service is not up, treat it like death notification to try to get service
230                // again
231                mSapServerMsgHandler.sendMessageDelayed(
232                        mSapServerMsgHandler.obtainMessage(
233                                SapServer.SAP_PROXY_DEAD, mSapProxyCookie.get()),
234                        SapServer.ISAP_GET_SERVICE_DELAY_MILLIS);
235            }
236            return mSapProxy;
237        }
238    }
239
240    public void resetSapProxy() {
241        synchronized (mSapProxyLock) {
242            mSapProxy = null;
243        }
244    }
245
246    public SapRilReceiver(Handler SapServerMsgHandler, Handler sapServiceHandler) {
247        mSapServerMsgHandler = SapServerMsgHandler;
248        mSapServiceHandler = sapServiceHandler;
249        mSapCallback = new SapCallback();
250        mSapProxyDeathRecipient = new SapProxyDeathRecipient();
251        synchronized (mSapProxyLock) {
252            mSapProxy = getSapProxy();
253        }
254    }
255
256    /**
257     * Notify SapServer that this class is ready for shutdown.
258     */
259    void notifyShutdown() {
260        if (DEBUG) Log.i(TAG, "notifyShutdown()");
261        // If we are already shutdown, don't bother sending a notification.
262        synchronized (mSapProxyLock) {
263            if (mSapProxy != null) sendShutdownMessage();
264        }
265    }
266
267    /**
268     * Read the message into buffer
269     * @param is
270     * @param buffer
271     * @return the length of the message
272     * @throws IOException
273     */
274    private static int readMessage(InputStream is, byte[] buffer) throws IOException {
275        int countRead;
276        int offset;
277        int remaining;
278        int messageLength;
279
280        // Read in the length of the message
281        offset = 0;
282        remaining = 4;
283        do {
284            countRead = is.read(buffer, offset, remaining);
285
286            if (countRead < 0 ) {
287                Log.e(TAG, "Hit EOS reading message length");
288                return -1;
289            }
290
291            offset += countRead;
292            remaining -= countRead;
293        } while (remaining > 0);
294
295        messageLength = ((buffer[0] & 0xff) << 24)
296                | ((buffer[1] & 0xff) << 16)
297                | ((buffer[2] & 0xff) << 8)
298                | (buffer[3] & 0xff);
299        if (VERBOSE) Log.e(TAG,"Message length found to be: "+messageLength);
300        // Read the message
301        offset = 0;
302        remaining = messageLength;
303        do {
304            countRead = is.read(buffer, offset, remaining);
305
306            if (countRead < 0 ) {
307                Log.e(TAG, "Hit EOS reading message.  messageLength=" + messageLength
308                        + " remaining=" + remaining);
309                return -1;
310            }
311
312            offset += countRead;
313            remaining -= countRead;
314        } while (remaining > 0);
315
316        return messageLength;
317    }
318
319    /**
320     * Notify SapServer that the RIL socket is connected
321     */
322    void sendRilConnectMessage() {
323        if (mSapServerMsgHandler != null) {
324            mSapServerMsgHandler.sendEmptyMessage(SapServer.SAP_MSG_RIL_CONNECT);
325        }
326    }
327
328    /**
329     * Send reply (solicited) message from the RIL to the Sap Server Handler Thread
330     * @param sapMsg The message to send
331     */
332    private void sendClientMessage(SapMessage sapMsg) {
333        Message newMsg = mSapServerMsgHandler.obtainMessage(SapServer.SAP_MSG_RFC_REPLY, sapMsg);
334        mSapServerMsgHandler.sendMessage(newMsg);
335    }
336
337    /**
338     * Send a shutdown signal to SapServer to indicate the
339     */
340    private void sendShutdownMessage() {
341        if (mSapServerMsgHandler != null) {
342            mSapServerMsgHandler.sendEmptyMessage(SapServer.SAP_RIL_SOCK_CLOSED);
343        }
344    }
345
346    /**
347     * Send indication (unsolicited) message from RIL to the Sap Server Handler Thread
348     * @param sapMsg The message to send
349     */
350    private void sendRilIndMessage(SapMessage sapMsg) {
351        Message newMsg = mSapServerMsgHandler.obtainMessage(SapServer.SAP_MSG_RIL_IND, sapMsg);
352        mSapServerMsgHandler.sendMessage(newMsg);
353    }
354
355}
356