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