Home | History | Annotate | Download | only in gsm
      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.Rlog;
     25 
     26 import com.android.internal.telephony.CommandsInterface;
     27 import com.android.internal.telephony.cat.ComprehensionTlvTag;
     28 import com.android.internal.telephony.uicc.IccIoResult;
     29 import com.android.internal.telephony.uicc.IccUtils;
     30 
     31 /**
     32  * Handler for SMS-PP data download messages.
     33  * See 3GPP TS 31.111 section 7.1.1
     34  */
     35 public 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             Rlog.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             Rlog.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             Rlog.d(TAG, "USIM data download succeeded: " + response.toString());
    168             success = true;
    169         } else if (sw1 == 0x93 && sw2 == 0x00) {
    170             Rlog.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             Rlog.e(TAG, "USIM data download failed: " + response.toString());
    175             success = false;
    176         } else {
    177             Rlog.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                     Rlog.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                 Rlog.e(TAG, "Ignoring unexpected message, what=" + msg.what);
    265         }
    266     }
    267 }
    268