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