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