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 17package com.android.internal.telephony.gsm; 18 19import android.app.Activity; 20import android.os.AsyncResult; 21import android.os.Handler; 22import android.os.Message; 23import android.provider.Telephony.Sms.Intents; 24import android.util.Log; 25 26import com.android.internal.telephony.CommandsInterface; 27import com.android.internal.telephony.IccIoResult; 28import com.android.internal.telephony.IccUtils; 29import com.android.internal.telephony.cat.ComprehensionTlvTag; 30 31/** 32 * Handler for SMS-PP data download messages. 33 * See 3GPP TS 31.111 section 7.1.1 34 */ 35public class UsimDataDownloadHandler extends Handler { 36 private static final String TAG = "UsimDataDownloadHandler"; 37 38 /** BER-TLV tag for SMS-PP download. TS 31.111 section 9.1. */ 39 private static final int BER_SMS_PP_DOWNLOAD_TAG = 0xd1; 40 41 /** Device identity value for UICC (destination). */ 42 private static final int DEV_ID_UICC = 0x81; 43 44 /** Device identity value for network (source). */ 45 private static final int DEV_ID_NETWORK = 0x83; 46 47 /** Message containing new SMS-PP message to process. */ 48 private static final int EVENT_START_DATA_DOWNLOAD = 1; 49 50 /** Response to SMS-PP download envelope command. */ 51 private static final int EVENT_SEND_ENVELOPE_RESPONSE = 2; 52 53 private final CommandsInterface mCI; 54 55 public UsimDataDownloadHandler(CommandsInterface commandsInterface) { 56 mCI = commandsInterface; 57 } 58 59 /** 60 * Start an SMS-PP data download for the specified message. Can be called from a different 61 * thread than this Handler is running on. 62 * 63 * @param smsMessage the message to process 64 * @return Activity.RESULT_OK on success; Intents.RESULT_SMS_GENERIC_ERROR on failure 65 */ 66 public int startDataDownload(SmsMessage smsMessage) { 67 if (sendMessage(obtainMessage(EVENT_START_DATA_DOWNLOAD, smsMessage))) { 68 return Activity.RESULT_OK; // we will send SMS ACK/ERROR based on UICC response 69 } else { 70 Log.e(TAG, "startDataDownload failed to send message to start data download."); 71 return Intents.RESULT_SMS_GENERIC_ERROR; 72 } 73 } 74 75 private void handleDataDownload(SmsMessage smsMessage) { 76 int dcs = smsMessage.getDataCodingScheme(); 77 int pid = smsMessage.getProtocolIdentifier(); 78 byte[] pdu = smsMessage.getPdu(); // includes SC address 79 80 int scAddressLength = pdu[0] & 0xff; 81 int tpduIndex = scAddressLength + 1; // start of TPDU 82 int tpduLength = pdu.length - tpduIndex; 83 84 int bodyLength = getEnvelopeBodyLength(scAddressLength, tpduLength); 85 86 // Add 1 byte for SMS-PP download tag and 1-2 bytes for BER-TLV length. 87 // See ETSI TS 102 223 Annex C for encoding of length and tags. 88 int totalLength = bodyLength + 1 + (bodyLength > 127 ? 2 : 1); 89 90 byte[] envelope = new byte[totalLength]; 91 int index = 0; 92 93 // SMS-PP download tag and length (assumed to be < 256 bytes). 94 envelope[index++] = (byte) BER_SMS_PP_DOWNLOAD_TAG; 95 if (bodyLength > 127) { 96 envelope[index++] = (byte) 0x81; // length 128-255 encoded as 0x81 + length 97 } 98 envelope[index++] = (byte) bodyLength; 99 100 // Device identities TLV 101 envelope[index++] = (byte) (0x80 | ComprehensionTlvTag.DEVICE_IDENTITIES.value()); 102 envelope[index++] = (byte) 2; 103 envelope[index++] = (byte) DEV_ID_NETWORK; 104 envelope[index++] = (byte) DEV_ID_UICC; 105 106 // Address TLV (if present). Encoded length is assumed to be < 127 bytes. 107 if (scAddressLength != 0) { 108 envelope[index++] = (byte) ComprehensionTlvTag.ADDRESS.value(); 109 envelope[index++] = (byte) scAddressLength; 110 System.arraycopy(pdu, 1, envelope, index, scAddressLength); 111 index += scAddressLength; 112 } 113 114 // SMS TPDU TLV. Length is assumed to be < 256 bytes. 115 envelope[index++] = (byte) (0x80 | ComprehensionTlvTag.SMS_TPDU.value()); 116 if (tpduLength > 127) { 117 envelope[index++] = (byte) 0x81; // length 128-255 encoded as 0x81 + length 118 } 119 envelope[index++] = (byte) tpduLength; 120 System.arraycopy(pdu, tpduIndex, envelope, index, tpduLength); 121 index += tpduLength; 122 123 // Verify that we calculated the payload size correctly. 124 if (index != envelope.length) { 125 Log.e(TAG, "startDataDownload() calculated incorrect envelope length, aborting."); 126 acknowledgeSmsWithError(CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR); 127 return; 128 } 129 130 String encodedEnvelope = IccUtils.bytesToHexString(envelope); 131 mCI.sendEnvelopeWithStatus(encodedEnvelope, obtainMessage( 132 EVENT_SEND_ENVELOPE_RESPONSE, new int[]{ dcs, pid })); 133 } 134 135 /** 136 * Return the size in bytes of the envelope to send to the UICC, excluding the 137 * SMS-PP download tag byte and length byte(s). If the size returned is <= 127, 138 * the BER-TLV length will be encoded in 1 byte, otherwise 2 bytes are required. 139 * 140 * @param scAddressLength the length of the SMSC address, or zero if not present 141 * @param tpduLength the length of the TPDU from the SMS-PP message 142 * @return the number of bytes to allocate for the envelope command 143 */ 144 private static int getEnvelopeBodyLength(int scAddressLength, int tpduLength) { 145 // Add 4 bytes for device identities TLV + 1 byte for SMS TPDU tag byte 146 int length = tpduLength + 5; 147 // Add 1 byte for TPDU length, or 2 bytes if length > 127 148 length += (tpduLength > 127 ? 2 : 1); 149 // Add length of address tag, if present (+ 2 bytes for tag and length) 150 if (scAddressLength != 0) { 151 length = length + 2 + scAddressLength; 152 } 153 return length; 154 } 155 156 /** 157 * Handle the response to the ENVELOPE command. 158 * @param response UICC response encoded as hexadecimal digits. First two bytes are the 159 * UICC SW1 and SW2 status bytes. 160 */ 161 private void sendSmsAckForEnvelopeResponse(IccIoResult response, int dcs, int pid) { 162 int sw1 = response.sw1; 163 int sw2 = response.sw2; 164 165 boolean success; 166 if ((sw1 == 0x90 && sw2 == 0x00) || sw1 == 0x91) { 167 Log.d(TAG, "USIM data download succeeded: " + response.toString()); 168 success = true; 169 } else if (sw1 == 0x93 && sw2 == 0x00) { 170 Log.e(TAG, "USIM data download failed: Toolkit busy"); 171 acknowledgeSmsWithError(CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_APP_TOOLKIT_BUSY); 172 return; 173 } else if (sw1 == 0x62 || sw1 == 0x63) { 174 Log.e(TAG, "USIM data download failed: " + response.toString()); 175 success = false; 176 } else { 177 Log.e(TAG, "Unexpected SW1/SW2 response from UICC: " + response.toString()); 178 success = false; 179 } 180 181 byte[] responseBytes = response.payload; 182 if (responseBytes == null || responseBytes.length == 0) { 183 if (success) { 184 mCI.acknowledgeLastIncomingGsmSms(true, 0, null); 185 } else { 186 acknowledgeSmsWithError( 187 CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_DATA_DOWNLOAD_ERROR); 188 } 189 return; 190 } 191 192 byte[] smsAckPdu; 193 int index = 0; 194 if (success) { 195 smsAckPdu = new byte[responseBytes.length + 5]; 196 smsAckPdu[index++] = 0x00; // TP-MTI, TP-UDHI 197 smsAckPdu[index++] = 0x07; // TP-PI: TP-PID, TP-DCS, TP-UDL present 198 } else { 199 smsAckPdu = new byte[responseBytes.length + 6]; 200 smsAckPdu[index++] = 0x00; // TP-MTI, TP-UDHI 201 smsAckPdu[index++] = (byte) 202 CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_DATA_DOWNLOAD_ERROR; // TP-FCS 203 smsAckPdu[index++] = 0x07; // TP-PI: TP-PID, TP-DCS, TP-UDL present 204 } 205 206 smsAckPdu[index++] = (byte) pid; 207 smsAckPdu[index++] = (byte) dcs; 208 209 if (is7bitDcs(dcs)) { 210 int septetCount = responseBytes.length * 8 / 7; 211 smsAckPdu[index++] = (byte) septetCount; 212 } else { 213 smsAckPdu[index++] = (byte) responseBytes.length; 214 } 215 216 System.arraycopy(responseBytes, 0, smsAckPdu, index, responseBytes.length); 217 218 mCI.acknowledgeIncomingGsmSmsWithPdu(success, 219 IccUtils.bytesToHexString(smsAckPdu), null); 220 } 221 222 private void acknowledgeSmsWithError(int cause) { 223 mCI.acknowledgeLastIncomingGsmSms(false, cause, null); 224 } 225 226 /** 227 * Returns whether the DCS is 7 bit. If so, set TP-UDL to the septet count of TP-UD; 228 * otherwise, set TP-UDL to the octet count of TP-UD. 229 * @param dcs the TP-Data-Coding-Scheme field from the original download SMS 230 * @return true if the DCS specifies 7 bit encoding; false otherwise 231 */ 232 private static boolean is7bitDcs(int dcs) { 233 // See 3GPP TS 23.038 section 4 234 return ((dcs & 0x8C) == 0x00) || ((dcs & 0xF4) == 0xF0); 235 } 236 237 /** 238 * Handle UICC envelope response and send SMS acknowledgement. 239 * 240 * @param msg the message to handle 241 */ 242 @Override 243 public void handleMessage(Message msg) { 244 switch (msg.what) { 245 case EVENT_START_DATA_DOWNLOAD: 246 handleDataDownload((SmsMessage) msg.obj); 247 break; 248 249 case EVENT_SEND_ENVELOPE_RESPONSE: 250 AsyncResult ar = (AsyncResult) msg.obj; 251 252 if (ar.exception != null) { 253 Log.e(TAG, "UICC Send Envelope failure, exception: " + ar.exception); 254 acknowledgeSmsWithError( 255 CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_DATA_DOWNLOAD_ERROR); 256 return; 257 } 258 259 int[] dcsPid = (int[]) ar.userObj; 260 sendSmsAckForEnvelopeResponse((IccIoResult) ar.result, dcsPid[0], dcsPid[1]); 261 break; 262 263 default: 264 Log.e(TAG, "Ignoring unexpected message, what=" + msg.what); 265 } 266 } 267} 268