1 package com.android.bluetooth.sap;
2 
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.util.ArrayList;
6 import java.util.List;
7 import java.util.concurrent.atomic.AtomicLong;
8 
9 import org.android.btsap.SapApi.MsgHeader;
10 
11 import com.google.protobuf.micro.CodedInputStreamMicro;
12 import com.google.protobuf.micro.CodedOutputStreamMicro;
13 
14 import android.hardware.radio.V1_0.ISap;
15 import android.hardware.radio.V1_0.ISapCallback;
16 
17 import android.net.LocalSocket;
18 import android.net.LocalSocketAddress;
19 import android.os.Handler;
20 import android.os.HwBinder;
21 import android.os.Message;
22 import android.os.RemoteException;
23 import android.util.Log;
24 
25 public 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
serviceDied(long cookie)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 
sendSapMessage(SapMessage sapMessage)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 
removeOngoingReqAndSendMessage(int token, SapMessage sapMessage)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 {
connectResponse(int token, int sapConnectRsp, int maxMsgSize)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 
disconnectResponse(int token)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 
disconnectIndication(int token, int disconnectType)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 
apduResponse(int token, int resultCode, ArrayList<Byte> apduRsp)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 
transferAtrResponse(int token, int resultCode, ArrayList<Byte> atr)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 
powerResponse(int token, int resultCode)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 
resetSimResponse(int token, int resultCode)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 
statusIndication(int token, int status)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 
transferCardReaderStatusResponse( int token, int resultCode, int cardReaderStatus)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 
errorResponse(int token)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 
transferProtocolResponse(int token, int resultCode)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 
arrayListToPrimitiveArray(List<Byte> bytes)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 
getSapProxyLock()204     public Object getSapProxyLock() {
205         return mSapProxyLock;
206     }
207 
getSapProxy()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 
resetSapProxy()240     public void resetSapProxy() {
241         synchronized (mSapProxyLock) {
242             mSapProxy = null;
243         }
244     }
245 
SapRilReceiver(Handler SapServerMsgHandler, Handler sapServiceHandler)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      */
notifyShutdown()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      */
readMessage(InputStream is, byte[] buffer)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      */
sendRilConnectMessage()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      */
sendClientMessage(SapMessage sapMsg)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      */
sendShutdownMessage()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      */
sendRilIndMessage(SapMessage sapMsg)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