1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.telephony.gsm; 18 19 import android.app.Activity; 20 import android.content.res.Resources; 21 import android.content.res.Resources.NotFoundException; 22 import android.os.AsyncResult; 23 import android.os.Handler; 24 import android.os.Message; 25 import android.provider.Telephony.Sms.Intents; 26 import android.telephony.PhoneNumberUtils; 27 import android.telephony.SmsManager; 28 import android.telephony.ims.stub.ImsSmsImplBase; 29 30 import com.android.ims.ImsException; 31 import com.android.ims.ImsManager; 32 import com.android.internal.annotations.VisibleForTesting; 33 import com.android.internal.telephony.CommandsInterface; 34 import com.android.internal.telephony.InboundSmsHandler; 35 import com.android.internal.telephony.PhoneFactory; 36 import com.android.internal.telephony.cat.ComprehensionTlvTag; 37 import com.android.internal.telephony.metrics.TelephonyMetrics; 38 import com.android.internal.telephony.uicc.IccIoResult; 39 import com.android.internal.telephony.uicc.IccUtils; 40 import com.android.internal.telephony.uicc.UsimServiceTable; 41 import com.android.telephony.Rlog; 42 43 /** 44 * Handler for SMS-PP data download messages. 45 * See 3GPP TS 31.111 section 7.1.1 46 */ 47 public class UsimDataDownloadHandler extends Handler { 48 private static final String TAG = "UsimDataDownloadHandler"; 49 50 /** BER-TLV tag for SMS-PP download. TS 31.111 section 9.1. */ 51 private static final int BER_SMS_PP_DOWNLOAD_TAG = 0xd1; 52 53 /** Device identity value for UICC (destination). */ 54 private static final int DEV_ID_UICC = 0x81; 55 56 /** Device identity value for network (source). */ 57 private static final int DEV_ID_NETWORK = 0x83; 58 59 /** Message containing new SMS-PP message to process. */ 60 private static final int EVENT_START_DATA_DOWNLOAD = 1; 61 62 /** Response to SMS-PP download envelope command. */ 63 private static final int EVENT_SEND_ENVELOPE_RESPONSE = 2; 64 65 /** Result of writing SM to UICC (when SMS-PP service is not available). */ 66 private static final int EVENT_WRITE_SMS_COMPLETE = 3; 67 68 private final CommandsInterface mCi; 69 private final int mPhoneId; 70 private ImsManager mImsManager; 71 Resources mResource; 72 UsimDataDownloadHandler(CommandsInterface commandsInterface, int phoneId)73 public UsimDataDownloadHandler(CommandsInterface commandsInterface, int phoneId) { 74 mCi = commandsInterface; 75 mPhoneId = phoneId; 76 mImsManager = null; // will get initialized when ImsManager connection is ready 77 mResource = Resources.getSystem(); 78 } 79 80 /** 81 * Handle SMS-PP data download messages. Normally these are automatically handled by the 82 * radio, but we may have to deal with this type of SM arriving via the IMS stack. If the 83 * data download service is not enabled, try to write to the USIM as an SMS, and send the 84 * UICC response as the acknowledgment to the SMSC. 85 * 86 * @param ust the UsimServiceTable, to check if data download is enabled 87 * @param smsMessage the SMS message to process 88 * @param smsSource the source of the SMS message 89 * @return {@code Activity.RESULT_OK} on success; {@code RESULT_SMS_GENERIC_ERROR} on failure 90 */ handleUsimDataDownload(UsimServiceTable ust, SmsMessage smsMessage, @InboundSmsHandler.SmsSource int smsSource, int token)91 int handleUsimDataDownload(UsimServiceTable ust, SmsMessage smsMessage, 92 @InboundSmsHandler.SmsSource int smsSource, int token) { 93 // If we receive an SMS-PP message before the UsimServiceTable has been loaded, 94 // assume that the data download service is not present. This is very unlikely to 95 // happen because the IMS connection will not be established until after the ISIM 96 // records have been loaded, after the USIM service table has been loaded. 97 if (ust != null && ust.isAvailable( 98 UsimServiceTable.UsimService.DATA_DL_VIA_SMS_PP)) { 99 Rlog.d(TAG, "Received SMS-PP data download, sending to UICC."); 100 return startDataDownload(smsMessage, smsSource, token); 101 } else { 102 Rlog.d(TAG, "DATA_DL_VIA_SMS_PP service not available, storing message to UICC."); 103 String smsc = IccUtils.bytesToHexString( 104 PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength( 105 smsMessage.getServiceCenterAddress())); 106 mCi.writeSmsToSim(SmsManager.STATUS_ON_ICC_UNREAD, smsc, 107 IccUtils.bytesToHexString(smsMessage.getPdu()), 108 obtainMessage(EVENT_WRITE_SMS_COMPLETE, 109 new int[]{ smsSource, smsMessage.mMessageRef, token })); 110 addUsimDataDownloadToMetrics(false, smsSource); 111 return Activity.RESULT_OK; // acknowledge after response from write to USIM 112 } 113 114 } 115 116 /** 117 * Start an SMS-PP data download for the specified message. Can be called from a different 118 * thread than this Handler is running on. 119 * 120 * @param smsMessage the message to process 121 * @param smsSource the source of the SMS message 122 * @return {@code Activity.RESULT_OK} on success; {@code RESULT_SMS_GENERIC_ERROR} on failure 123 */ startDataDownload(SmsMessage smsMessage, @InboundSmsHandler.SmsSource int smsSource, int token)124 public int startDataDownload(SmsMessage smsMessage, 125 @InboundSmsHandler.SmsSource int smsSource, int token) { 126 if (sendMessage(obtainMessage(EVENT_START_DATA_DOWNLOAD, 127 smsSource, token, smsMessage))) { 128 return Activity.RESULT_OK; // we will send SMS ACK/ERROR based on UICC response 129 } else { 130 Rlog.e(TAG, "startDataDownload failed to send message to start data download."); 131 return Intents.RESULT_SMS_GENERIC_ERROR; 132 } 133 } 134 handleDataDownload(SmsMessage smsMessage, @InboundSmsHandler.SmsSource int smsSource, int token)135 private void handleDataDownload(SmsMessage smsMessage, 136 @InboundSmsHandler.SmsSource int smsSource, int token) { 137 int dcs = smsMessage.getDataCodingScheme(); 138 int pid = smsMessage.getProtocolIdentifier(); 139 byte[] pdu = smsMessage.getPdu(); // includes SC address 140 141 int scAddressLength = pdu[0] & 0xff; 142 int tpduIndex = scAddressLength + 1; // start of TPDU 143 int tpduLength = pdu.length - tpduIndex; 144 145 int bodyLength = getEnvelopeBodyLength(scAddressLength, tpduLength); 146 147 // Add 1 byte for SMS-PP download tag and 1-2 bytes for BER-TLV length. 148 // See ETSI TS 102 223 Annex C for encoding of length and tags. 149 int totalLength = bodyLength + 1 + (bodyLength > 127 ? 2 : 1); 150 151 byte[] envelope = new byte[totalLength]; 152 int index = 0; 153 Rlog.d(TAG, "smsSource: " + smsSource + "Token: " + token); 154 155 // SMS-PP download tag and length (assumed to be < 256 bytes). 156 envelope[index++] = (byte) BER_SMS_PP_DOWNLOAD_TAG; 157 if (bodyLength > 127) { 158 envelope[index++] = (byte) 0x81; // length 128-255 encoded as 0x81 + length 159 } 160 envelope[index++] = (byte) bodyLength; 161 162 // Device identities TLV 163 envelope[index++] = (byte) (0x80 | ComprehensionTlvTag.DEVICE_IDENTITIES.value()); 164 envelope[index++] = (byte) 2; 165 envelope[index++] = (byte) DEV_ID_NETWORK; 166 envelope[index++] = (byte) DEV_ID_UICC; 167 168 // Address TLV (if present). Encoded length is assumed to be < 127 bytes. 169 if (scAddressLength != 0) { 170 envelope[index++] = (byte) ComprehensionTlvTag.ADDRESS.value(); 171 envelope[index++] = (byte) scAddressLength; 172 System.arraycopy(pdu, 1, envelope, index, scAddressLength); 173 index += scAddressLength; 174 } 175 176 // SMS TPDU TLV. Length is assumed to be < 256 bytes. 177 envelope[index++] = (byte) (0x80 | ComprehensionTlvTag.SMS_TPDU.value()); 178 if (tpduLength > 127) { 179 envelope[index++] = (byte) 0x81; // length 128-255 encoded as 0x81 + length 180 } 181 envelope[index++] = (byte) tpduLength; 182 System.arraycopy(pdu, tpduIndex, envelope, index, tpduLength); 183 index += tpduLength; 184 185 // Verify that we calculated the payload size correctly. 186 if (index != envelope.length) { 187 Rlog.e(TAG, "startDataDownload() calculated incorrect envelope length, aborting."); 188 acknowledgeSmsWithError(CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR, 189 smsSource, token, smsMessage.mMessageRef); 190 addUsimDataDownloadToMetrics(false, smsSource); 191 return; 192 } 193 194 String encodedEnvelope = IccUtils.bytesToHexString(envelope); 195 mCi.sendEnvelopeWithStatus(encodedEnvelope, obtainMessage( 196 EVENT_SEND_ENVELOPE_RESPONSE, new int[]{ dcs, pid, smsSource, 197 smsMessage.mMessageRef, token })); 198 199 addUsimDataDownloadToMetrics(true, smsSource); 200 } 201 202 /** 203 * Return the size in bytes of the envelope to send to the UICC, excluding the 204 * SMS-PP download tag byte and length byte(s). If the size returned is <= 127, 205 * the BER-TLV length will be encoded in 1 byte, otherwise 2 bytes are required. 206 * 207 * @param scAddressLength the length of the SMSC address, or zero if not present 208 * @param tpduLength the length of the TPDU from the SMS-PP message 209 * @return the number of bytes to allocate for the envelope command 210 */ getEnvelopeBodyLength(int scAddressLength, int tpduLength)211 private static int getEnvelopeBodyLength(int scAddressLength, int tpduLength) { 212 // Add 4 bytes for device identities TLV + 1 byte for SMS TPDU tag byte 213 int length = tpduLength + 5; 214 // Add 1 byte for TPDU length, or 2 bytes if length > 127 215 length += (tpduLength > 127 ? 2 : 1); 216 // Add length of address tag, if present (+ 2 bytes for tag and length) 217 if (scAddressLength != 0) { 218 length = length + 2 + scAddressLength; 219 } 220 return length; 221 } 222 223 /** 224 * Handle the response to the ENVELOPE command. 225 * @param response UICC response encoded as hexadecimal digits. First two bytes are the 226 * UICC SW1 and SW2 status bytes. 227 */ sendSmsAckForEnvelopeResponse(IccIoResult response, int dcs, int pid, int smsSource, int token, int messageRef)228 private void sendSmsAckForEnvelopeResponse(IccIoResult response, int dcs, int pid, 229 int smsSource, int token, int messageRef) { 230 int sw1 = response.sw1; 231 int sw2 = response.sw2; 232 233 boolean success; 234 if ((sw1 == 0x90 && sw2 == 0x00) || sw1 == 0x91) { 235 Rlog.d(TAG, "USIM data download succeeded: " + response.toString()); 236 success = true; 237 } else if (sw1 == 0x93 && sw2 == 0x00) { 238 Rlog.e(TAG, "USIM data download failed: Toolkit busy"); 239 acknowledgeSmsWithError(CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_APP_TOOLKIT_BUSY, 240 smsSource, token, messageRef); 241 return; 242 } else if (sw1 == 0x62 || sw1 == 0x63) { 243 Rlog.e(TAG, "USIM data download failed: " + response.toString()); 244 success = false; 245 } else { 246 Rlog.e(TAG, "Unexpected SW1/SW2 response from UICC: " + response.toString()); 247 success = false; 248 } 249 250 byte[] responseBytes = response.payload; 251 if (responseBytes == null || responseBytes.length == 0) { 252 if (success) { 253 acknowledgeSmsWithSuccess(0, smsSource, token, messageRef); 254 } else { 255 acknowledgeSmsWithError( 256 CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_DATA_DOWNLOAD_ERROR, smsSource, 257 token, messageRef); 258 } 259 return; 260 } 261 262 byte[] smsAckPdu; 263 int index = 0; 264 if (success) { 265 smsAckPdu = new byte[responseBytes.length + 5]; 266 smsAckPdu[index++] = 0x00; // TP-MTI, TP-UDHI 267 smsAckPdu[index++] = 0x07; // TP-PI: TP-PID, TP-DCS, TP-UDL present 268 } else { 269 smsAckPdu = new byte[responseBytes.length + 6]; 270 smsAckPdu[index++] = 0x00; // TP-MTI, TP-UDHI 271 smsAckPdu[index++] = (byte) 272 CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_DATA_DOWNLOAD_ERROR; // TP-FCS 273 smsAckPdu[index++] = 0x07; // TP-PI: TP-PID, TP-DCS, TP-UDL present 274 } 275 276 smsAckPdu[index++] = (byte) pid; 277 smsAckPdu[index++] = (byte) dcs; 278 279 if (is7bitDcs(dcs)) { 280 int septetCount = responseBytes.length * 8 / 7; 281 smsAckPdu[index++] = (byte) septetCount; 282 } else { 283 smsAckPdu[index++] = (byte) responseBytes.length; 284 } 285 286 System.arraycopy(responseBytes, 0, smsAckPdu, index, responseBytes.length); 287 288 if (smsSource == InboundSmsHandler.SOURCE_INJECTED_FROM_IMS && ackViaIms()) { 289 acknowledgeImsSms(token, messageRef, true, smsAckPdu); 290 } else { 291 mCi.acknowledgeIncomingGsmSmsWithPdu(success, 292 IccUtils.bytesToHexString(smsAckPdu), null); 293 } 294 } 295 acknowledgeSmsWithSuccess(int cause, int smsSource, int token, int messageRef)296 private void acknowledgeSmsWithSuccess(int cause, int smsSource, int token, int messageRef) { 297 Rlog.d(TAG, "acknowledgeSmsWithSuccess- cause: " + cause + " smsSource: " + smsSource 298 + " token: " + token + " messageRef: " + messageRef); 299 if (smsSource == InboundSmsHandler.SOURCE_INJECTED_FROM_IMS && ackViaIms()) { 300 acknowledgeImsSms(token, messageRef, true, null); 301 } else { 302 mCi.acknowledgeLastIncomingGsmSms(true, cause, null); 303 } 304 } 305 acknowledgeSmsWithError(int cause, int smsSource, int token, int messageRef)306 private void acknowledgeSmsWithError(int cause, int smsSource, int token, int messageRef) { 307 Rlog.d(TAG, "acknowledgeSmsWithError- cause: " + cause + " smsSource: " + smsSource 308 + " token: " + token + " messageRef: " + messageRef); 309 if (smsSource == InboundSmsHandler.SOURCE_INJECTED_FROM_IMS && ackViaIms()) { 310 acknowledgeImsSms(token, messageRef, false, null); 311 } else { 312 mCi.acknowledgeLastIncomingGsmSms(false, cause, null); 313 } 314 } 315 316 /** 317 * Returns whether the DCS is 7 bit. If so, set TP-UDL to the septet count of TP-UD; 318 * otherwise, set TP-UDL to the octet count of TP-UD. 319 * @param dcs the TP-Data-Coding-Scheme field from the original download SMS 320 * @return true if the DCS specifies 7 bit encoding; false otherwise 321 */ is7bitDcs(int dcs)322 private static boolean is7bitDcs(int dcs) { 323 // See 3GPP TS 23.038 section 4 324 return ((dcs & 0x8C) == 0x00) || ((dcs & 0xF4) == 0xF0); 325 } 326 327 /** 328 * Add the SMS-PP data to the telephony metrics, indicating if the message was forwarded 329 * to the USIM. The metrics does not cover the case where the SMS-PP might be rejected 330 * by the USIM itself. 331 */ addUsimDataDownloadToMetrics(boolean result, @InboundSmsHandler.SmsSource int smsSource)332 private void addUsimDataDownloadToMetrics(boolean result, 333 @InboundSmsHandler.SmsSource int smsSource) { 334 TelephonyMetrics metrics = TelephonyMetrics.getInstance(); 335 metrics.writeIncomingSMSPP(mPhoneId, android.telephony.SmsMessage.FORMAT_3GPP, result); 336 PhoneFactory.getPhone(mPhoneId).getSmsStats().onIncomingSmsPP(smsSource, result); 337 } 338 339 /** 340 * Route resposes via ImsManager based on config 341 */ ackViaIms()342 private boolean ackViaIms() { 343 boolean isViaIms; 344 345 try { 346 isViaIms = mResource.getBoolean( 347 com.android.internal.R.bool.config_smppsim_response_via_ims); 348 } catch (NotFoundException e) { 349 isViaIms = false; 350 } 351 352 Rlog.d(TAG, "ackViaIms : " + isViaIms); 353 return isViaIms; 354 } 355 356 /** 357 * Acknowledges IMS SMS and delivers the result based on the envelope or SIM saving respose 358 * received from SIM for SMS-PP Data. 359 */ acknowledgeImsSms(int token, int messageRef, boolean success, byte[] pdu)360 private void acknowledgeImsSms(int token, int messageRef, boolean success, byte[] pdu) { 361 int result = success ? ImsSmsImplBase.DELIVER_STATUS_OK : 362 ImsSmsImplBase.DELIVER_STATUS_ERROR_GENERIC; 363 Rlog.d(TAG, "sending result via acknowledgeImsSms: " + result + " token: " + token); 364 365 try { 366 if (mImsManager != null) { 367 if (pdu != null && pdu.length > 0) { 368 mImsManager.acknowledgeSms(token, messageRef, result, pdu); 369 } else { 370 mImsManager.acknowledgeSms(token, messageRef, result); 371 } 372 } 373 } catch (ImsException e) { 374 Rlog.e(TAG, "Failed to acknowledgeSms(). Error: " + e.getMessage()); 375 } 376 } 377 378 /** 379 * Handle UICC envelope response and send SMS acknowledgement. 380 * 381 * @param msg the message to handle 382 */ 383 @Override handleMessage(Message msg)384 public void handleMessage(Message msg) { 385 AsyncResult ar; 386 int smsSource = InboundSmsHandler.SOURCE_INJECTED_FROM_UNKNOWN; 387 int token = 0; 388 int messageRef = 0; 389 int[] responseInfo; 390 391 switch (msg.what) { 392 case EVENT_START_DATA_DOWNLOAD: 393 Rlog.d(TAG, "EVENT_START_DATA_DOWNLOAD"); 394 handleDataDownload((SmsMessage) msg.obj, msg.arg1 /* smsSource */, 395 msg.arg2 /* token */); 396 break; 397 398 case EVENT_SEND_ENVELOPE_RESPONSE: 399 ar = (AsyncResult) msg.obj; 400 401 responseInfo = (int[]) ar.userObj; 402 smsSource = responseInfo[2]; 403 messageRef = responseInfo[3]; 404 token = responseInfo[4]; 405 406 Rlog.d(TAG, "Received EVENT_SEND_ENVELOPE_RESPONSE from source : " + smsSource); 407 408 if (ar.exception != null) { 409 Rlog.e(TAG, "UICC Send Envelope failure, exception: " + ar.exception); 410 411 acknowledgeSmsWithError( 412 CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_DATA_DOWNLOAD_ERROR, 413 smsSource, token, messageRef); 414 return; 415 } 416 417 Rlog.d(TAG, "Successful in sending envelope response"); 418 sendSmsAckForEnvelopeResponse((IccIoResult) ar.result, responseInfo[0], 419 responseInfo[1], smsSource, token, messageRef); 420 break; 421 422 case EVENT_WRITE_SMS_COMPLETE: 423 ar = (AsyncResult) msg.obj; 424 425 responseInfo = (int[]) ar.userObj; 426 smsSource = responseInfo[0]; 427 messageRef = responseInfo[1]; 428 token = responseInfo[2]; 429 430 Rlog.d(TAG, "Received EVENT_WRITE_SMS_COMPLETE from source : " + smsSource); 431 432 if (ar.exception == null) { 433 Rlog.d(TAG, "Successfully wrote SMS-PP message to UICC"); 434 acknowledgeSmsWithSuccess(0, smsSource, token, messageRef); 435 } else { 436 Rlog.d(TAG, "Failed to write SMS-PP message to UICC", ar.exception); 437 acknowledgeSmsWithError( 438 CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR, 439 smsSource, token, messageRef); 440 } 441 break; 442 443 default: 444 Rlog.e(TAG, "Ignoring unexpected message, what=" + msg.what); 445 } 446 } 447 448 /** 449 * Called when ImsManager connection is ready. ImsManager object will be used to send ACK to IMS 450 * which doesn't use RIL interface. 451 * @param imsManager object 452 */ setImsManager(ImsManager imsManager)453 public void setImsManager(ImsManager imsManager) { 454 mImsManager = imsManager; 455 } 456 457 /** 458 * Called to set mocked object of type Resources during unit testing of this file. 459 * @param resource object 460 */ 461 @VisibleForTesting setResourcesForTest(Resources resource)462 public void setResourcesForTest(Resources resource) { 463 mResource = resource; 464 Rlog.d(TAG, "setResourcesForTest"); 465 } 466 } 467