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.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 
     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      */
     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      */
    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 
    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      */
    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      */
    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 
    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      */
    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
    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