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.os.AsyncResult; 21 import android.os.Handler; 22 import android.os.Message; 23 import android.provider.Telephony.Sms.Intents; 24 import android.telephony.PhoneNumberUtils; 25 import android.telephony.SmsManager; 26 27 import com.android.internal.telephony.CommandsInterface; 28 import com.android.internal.telephony.InboundSmsHandler; 29 import com.android.internal.telephony.PhoneFactory; 30 import com.android.internal.telephony.cat.ComprehensionTlvTag; 31 import com.android.internal.telephony.metrics.TelephonyMetrics; 32 import com.android.internal.telephony.uicc.IccIoResult; 33 import com.android.internal.telephony.uicc.IccUtils; 34 import com.android.internal.telephony.uicc.UsimServiceTable; 35 import com.android.telephony.Rlog; 36 37 /** 38 * Handler for SMS-PP data download messages. 39 * See 3GPP TS 31.111 section 7.1.1 40 */ 41 public class UsimDataDownloadHandler extends Handler { 42 private static final String TAG = "UsimDataDownloadHandler"; 43 44 /** BER-TLV tag for SMS-PP download. TS 31.111 section 9.1. */ 45 private static final int BER_SMS_PP_DOWNLOAD_TAG = 0xd1; 46 47 /** Device identity value for UICC (destination). */ 48 private static final int DEV_ID_UICC = 0x81; 49 50 /** Device identity value for network (source). */ 51 private static final int DEV_ID_NETWORK = 0x83; 52 53 /** Message containing new SMS-PP message to process. */ 54 private static final int EVENT_START_DATA_DOWNLOAD = 1; 55 56 /** Response to SMS-PP download envelope command. */ 57 private static final int EVENT_SEND_ENVELOPE_RESPONSE = 2; 58 59 /** Result of writing SM to UICC (when SMS-PP service is not available). */ 60 private static final int EVENT_WRITE_SMS_COMPLETE = 3; 61 62 private final CommandsInterface mCi; 63 private final int mPhoneId; 64 UsimDataDownloadHandler(CommandsInterface commandsInterface, int phoneId)65 public UsimDataDownloadHandler(CommandsInterface commandsInterface, int phoneId) { 66 mCi = commandsInterface; 67 mPhoneId = phoneId; 68 } 69 70 /** 71 * Handle SMS-PP data download messages. Normally these are automatically handled by the 72 * radio, but we may have to deal with this type of SM arriving via the IMS stack. If the 73 * data download service is not enabled, try to write to the USIM as an SMS, and send the 74 * UICC response as the acknowledgment to the SMSC. 75 * 76 * @param ust the UsimServiceTable, to check if data download is enabled 77 * @param smsMessage the SMS message to process 78 * @param smsSource the source of the SMS message 79 * @return {@code Activity.RESULT_OK} on success; {@code RESULT_SMS_GENERIC_ERROR} on failure 80 */ handleUsimDataDownload(UsimServiceTable ust, SmsMessage smsMessage, @InboundSmsHandler.SmsSource int smsSource)81 int handleUsimDataDownload(UsimServiceTable ust, SmsMessage smsMessage, 82 @InboundSmsHandler.SmsSource int smsSource) { 83 // If we receive an SMS-PP message before the UsimServiceTable has been loaded, 84 // assume that the data download service is not present. This is very unlikely to 85 // happen because the IMS connection will not be established until after the ISIM 86 // records have been loaded, after the USIM service table has been loaded. 87 if (ust != null && ust.isAvailable( 88 UsimServiceTable.UsimService.DATA_DL_VIA_SMS_PP)) { 89 Rlog.d(TAG, "Received SMS-PP data download, sending to UICC."); 90 return startDataDownload(smsMessage, smsSource); 91 } else { 92 Rlog.d(TAG, "DATA_DL_VIA_SMS_PP service not available, storing message to UICC."); 93 String smsc = IccUtils.bytesToHexString( 94 PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength( 95 smsMessage.getServiceCenterAddress())); 96 mCi.writeSmsToSim(SmsManager.STATUS_ON_ICC_UNREAD, smsc, 97 IccUtils.bytesToHexString(smsMessage.getPdu()), 98 obtainMessage(EVENT_WRITE_SMS_COMPLETE)); 99 addUsimDataDownloadToMetrics(false, smsSource); 100 return Activity.RESULT_OK; // acknowledge after response from write to USIM 101 } 102 103 } 104 105 /** 106 * Start an SMS-PP data download for the specified message. Can be called from a different 107 * thread than this Handler is running on. 108 * 109 * @param smsMessage the message to process 110 * @param smsSource the source of the SMS message 111 * @return {@code Activity.RESULT_OK} on success; {@code RESULT_SMS_GENERIC_ERROR} on failure 112 */ startDataDownload(SmsMessage smsMessage, @InboundSmsHandler.SmsSource int smsSource)113 public int startDataDownload(SmsMessage smsMessage, 114 @InboundSmsHandler.SmsSource int smsSource) { 115 if (sendMessage(obtainMessage(EVENT_START_DATA_DOWNLOAD, 116 smsSource, 0 /* unused */, smsMessage))) { 117 return Activity.RESULT_OK; // we will send SMS ACK/ERROR based on UICC response 118 } else { 119 Rlog.e(TAG, "startDataDownload failed to send message to start data download."); 120 return Intents.RESULT_SMS_GENERIC_ERROR; 121 } 122 } 123 handleDataDownload(SmsMessage smsMessage, @InboundSmsHandler.SmsSource int smsSource)124 private void handleDataDownload(SmsMessage smsMessage, 125 @InboundSmsHandler.SmsSource int smsSource) { 126 int dcs = smsMessage.getDataCodingScheme(); 127 int pid = smsMessage.getProtocolIdentifier(); 128 byte[] pdu = smsMessage.getPdu(); // includes SC address 129 130 int scAddressLength = pdu[0] & 0xff; 131 int tpduIndex = scAddressLength + 1; // start of TPDU 132 int tpduLength = pdu.length - tpduIndex; 133 134 int bodyLength = getEnvelopeBodyLength(scAddressLength, tpduLength); 135 136 // Add 1 byte for SMS-PP download tag and 1-2 bytes for BER-TLV length. 137 // See ETSI TS 102 223 Annex C for encoding of length and tags. 138 int totalLength = bodyLength + 1 + (bodyLength > 127 ? 2 : 1); 139 140 byte[] envelope = new byte[totalLength]; 141 int index = 0; 142 143 // SMS-PP download tag and length (assumed to be < 256 bytes). 144 envelope[index++] = (byte) BER_SMS_PP_DOWNLOAD_TAG; 145 if (bodyLength > 127) { 146 envelope[index++] = (byte) 0x81; // length 128-255 encoded as 0x81 + length 147 } 148 envelope[index++] = (byte) bodyLength; 149 150 // Device identities TLV 151 envelope[index++] = (byte) (0x80 | ComprehensionTlvTag.DEVICE_IDENTITIES.value()); 152 envelope[index++] = (byte) 2; 153 envelope[index++] = (byte) DEV_ID_NETWORK; 154 envelope[index++] = (byte) DEV_ID_UICC; 155 156 // Address TLV (if present). Encoded length is assumed to be < 127 bytes. 157 if (scAddressLength != 0) { 158 envelope[index++] = (byte) ComprehensionTlvTag.ADDRESS.value(); 159 envelope[index++] = (byte) scAddressLength; 160 System.arraycopy(pdu, 1, envelope, index, scAddressLength); 161 index += scAddressLength; 162 } 163 164 // SMS TPDU TLV. Length is assumed to be < 256 bytes. 165 envelope[index++] = (byte) (0x80 | ComprehensionTlvTag.SMS_TPDU.value()); 166 if (tpduLength > 127) { 167 envelope[index++] = (byte) 0x81; // length 128-255 encoded as 0x81 + length 168 } 169 envelope[index++] = (byte) tpduLength; 170 System.arraycopy(pdu, tpduIndex, envelope, index, tpduLength); 171 index += tpduLength; 172 173 // Verify that we calculated the payload size correctly. 174 if (index != envelope.length) { 175 Rlog.e(TAG, "startDataDownload() calculated incorrect envelope length, aborting."); 176 acknowledgeSmsWithError(CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR); 177 addUsimDataDownloadToMetrics(false, smsSource); 178 return; 179 } 180 181 String encodedEnvelope = IccUtils.bytesToHexString(envelope); 182 mCi.sendEnvelopeWithStatus(encodedEnvelope, obtainMessage( 183 EVENT_SEND_ENVELOPE_RESPONSE, new int[]{ dcs, pid })); 184 185 addUsimDataDownloadToMetrics(true, smsSource); 186 } 187 188 /** 189 * Return the size in bytes of the envelope to send to the UICC, excluding the 190 * SMS-PP download tag byte and length byte(s). If the size returned is <= 127, 191 * the BER-TLV length will be encoded in 1 byte, otherwise 2 bytes are required. 192 * 193 * @param scAddressLength the length of the SMSC address, or zero if not present 194 * @param tpduLength the length of the TPDU from the SMS-PP message 195 * @return the number of bytes to allocate for the envelope command 196 */ getEnvelopeBodyLength(int scAddressLength, int tpduLength)197 private static int getEnvelopeBodyLength(int scAddressLength, int tpduLength) { 198 // Add 4 bytes for device identities TLV + 1 byte for SMS TPDU tag byte 199 int length = tpduLength + 5; 200 // Add 1 byte for TPDU length, or 2 bytes if length > 127 201 length += (tpduLength > 127 ? 2 : 1); 202 // Add length of address tag, if present (+ 2 bytes for tag and length) 203 if (scAddressLength != 0) { 204 length = length + 2 + scAddressLength; 205 } 206 return length; 207 } 208 209 /** 210 * Handle the response to the ENVELOPE command. 211 * @param response UICC response encoded as hexadecimal digits. First two bytes are the 212 * UICC SW1 and SW2 status bytes. 213 */ sendSmsAckForEnvelopeResponse(IccIoResult response, int dcs, int pid)214 private void sendSmsAckForEnvelopeResponse(IccIoResult response, int dcs, int pid) { 215 int sw1 = response.sw1; 216 int sw2 = response.sw2; 217 218 boolean success; 219 if ((sw1 == 0x90 && sw2 == 0x00) || sw1 == 0x91) { 220 Rlog.d(TAG, "USIM data download succeeded: " + response.toString()); 221 success = true; 222 } else if (sw1 == 0x93 && sw2 == 0x00) { 223 Rlog.e(TAG, "USIM data download failed: Toolkit busy"); 224 acknowledgeSmsWithError(CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_APP_TOOLKIT_BUSY); 225 return; 226 } else if (sw1 == 0x62 || sw1 == 0x63) { 227 Rlog.e(TAG, "USIM data download failed: " + response.toString()); 228 success = false; 229 } else { 230 Rlog.e(TAG, "Unexpected SW1/SW2 response from UICC: " + response.toString()); 231 success = false; 232 } 233 234 byte[] responseBytes = response.payload; 235 if (responseBytes == null || responseBytes.length == 0) { 236 if (success) { 237 mCi.acknowledgeLastIncomingGsmSms(true, 0, null); 238 } else { 239 acknowledgeSmsWithError( 240 CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_DATA_DOWNLOAD_ERROR); 241 } 242 return; 243 } 244 245 byte[] smsAckPdu; 246 int index = 0; 247 if (success) { 248 smsAckPdu = new byte[responseBytes.length + 5]; 249 smsAckPdu[index++] = 0x00; // TP-MTI, TP-UDHI 250 smsAckPdu[index++] = 0x07; // TP-PI: TP-PID, TP-DCS, TP-UDL present 251 } else { 252 smsAckPdu = new byte[responseBytes.length + 6]; 253 smsAckPdu[index++] = 0x00; // TP-MTI, TP-UDHI 254 smsAckPdu[index++] = (byte) 255 CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_DATA_DOWNLOAD_ERROR; // TP-FCS 256 smsAckPdu[index++] = 0x07; // TP-PI: TP-PID, TP-DCS, TP-UDL present 257 } 258 259 smsAckPdu[index++] = (byte) pid; 260 smsAckPdu[index++] = (byte) dcs; 261 262 if (is7bitDcs(dcs)) { 263 int septetCount = responseBytes.length * 8 / 7; 264 smsAckPdu[index++] = (byte) septetCount; 265 } else { 266 smsAckPdu[index++] = (byte) responseBytes.length; 267 } 268 269 System.arraycopy(responseBytes, 0, smsAckPdu, index, responseBytes.length); 270 271 mCi.acknowledgeIncomingGsmSmsWithPdu(success, 272 IccUtils.bytesToHexString(smsAckPdu), null); 273 } 274 acknowledgeSmsWithError(int cause)275 private void acknowledgeSmsWithError(int cause) { 276 mCi.acknowledgeLastIncomingGsmSms(false, cause, null); 277 } 278 279 /** 280 * Returns whether the DCS is 7 bit. If so, set TP-UDL to the septet count of TP-UD; 281 * otherwise, set TP-UDL to the octet count of TP-UD. 282 * @param dcs the TP-Data-Coding-Scheme field from the original download SMS 283 * @return true if the DCS specifies 7 bit encoding; false otherwise 284 */ is7bitDcs(int dcs)285 private static boolean is7bitDcs(int dcs) { 286 // See 3GPP TS 23.038 section 4 287 return ((dcs & 0x8C) == 0x00) || ((dcs & 0xF4) == 0xF0); 288 } 289 290 /** 291 * Add the SMS-PP data to the telephony metrics, indicating if the message was forwarded 292 * to the USIM. The metrics does not cover the case where the SMS-PP might be rejected 293 * by the USIM itself. 294 */ addUsimDataDownloadToMetrics(boolean result, @InboundSmsHandler.SmsSource int smsSource)295 private void addUsimDataDownloadToMetrics(boolean result, 296 @InboundSmsHandler.SmsSource int smsSource) { 297 TelephonyMetrics metrics = TelephonyMetrics.getInstance(); 298 metrics.writeIncomingSMSPP(mPhoneId, android.telephony.SmsMessage.FORMAT_3GPP, result); 299 PhoneFactory.getPhone(mPhoneId).getSmsStats().onIncomingSmsPP(smsSource, result); 300 } 301 302 /** 303 * Handle UICC envelope response and send SMS acknowledgement. 304 * 305 * @param msg the message to handle 306 */ 307 @Override handleMessage(Message msg)308 public void handleMessage(Message msg) { 309 AsyncResult ar; 310 311 switch (msg.what) { 312 case EVENT_START_DATA_DOWNLOAD: 313 handleDataDownload((SmsMessage) msg.obj, msg.arg1 /* smsSource */); 314 break; 315 316 case EVENT_SEND_ENVELOPE_RESPONSE: 317 ar = (AsyncResult) msg.obj; 318 319 if (ar.exception != null) { 320 Rlog.e(TAG, "UICC Send Envelope failure, exception: " + ar.exception); 321 acknowledgeSmsWithError( 322 CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_DATA_DOWNLOAD_ERROR); 323 return; 324 } 325 326 int[] dcsPid = (int[]) ar.userObj; 327 sendSmsAckForEnvelopeResponse((IccIoResult) ar.result, dcsPid[0], dcsPid[1]); 328 break; 329 330 case EVENT_WRITE_SMS_COMPLETE: 331 ar = (AsyncResult) msg.obj; 332 if (ar.exception == null) { 333 Rlog.d(TAG, "Successfully wrote SMS-PP message to UICC"); 334 mCi.acknowledgeLastIncomingGsmSms(true, 0, null); 335 } else { 336 Rlog.d(TAG, "Failed to write SMS-PP message to UICC", ar.exception); 337 mCi.acknowledgeLastIncomingGsmSms(false, 338 CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR, null); 339 } 340 break; 341 342 default: 343 Rlog.e(TAG, "Ignoring unexpected message, what=" + msg.what); 344 } 345 } 346 } 347