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