| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.internal.telephony.gsm; |
| |
| import android.app.Activity; |
| import android.os.AsyncResult; |
| import android.os.Handler; |
| import android.os.Message; |
| import android.provider.Telephony.Sms.Intents; |
| import android.telephony.PhoneNumberUtils; |
| import android.telephony.Rlog; |
| import android.telephony.SmsManager; |
| |
| import com.android.internal.telephony.CommandsInterface; |
| import com.android.internal.telephony.cat.ComprehensionTlvTag; |
| import com.android.internal.telephony.uicc.IccIoResult; |
| import com.android.internal.telephony.uicc.IccUtils; |
| import com.android.internal.telephony.uicc.UsimServiceTable; |
| |
| /** |
| * Handler for SMS-PP data download messages. |
| * See 3GPP TS 31.111 section 7.1.1 |
| */ |
| public class UsimDataDownloadHandler extends Handler { |
| private static final String TAG = "UsimDataDownloadHandler"; |
| |
| /** BER-TLV tag for SMS-PP download. TS 31.111 section 9.1. */ |
| private static final int BER_SMS_PP_DOWNLOAD_TAG = 0xd1; |
| |
| /** Device identity value for UICC (destination). */ |
| private static final int DEV_ID_UICC = 0x81; |
| |
| /** Device identity value for network (source). */ |
| private static final int DEV_ID_NETWORK = 0x83; |
| |
| /** Message containing new SMS-PP message to process. */ |
| private static final int EVENT_START_DATA_DOWNLOAD = 1; |
| |
| /** Response to SMS-PP download envelope command. */ |
| private static final int EVENT_SEND_ENVELOPE_RESPONSE = 2; |
| |
| /** Result of writing SM to UICC (when SMS-PP service is not available). */ |
| private static final int EVENT_WRITE_SMS_COMPLETE = 3; |
| |
| private final CommandsInterface mCi; |
| |
| public UsimDataDownloadHandler(CommandsInterface commandsInterface) { |
| mCi = commandsInterface; |
| } |
| |
| /** |
| * Handle SMS-PP data download messages. Normally these are automatically handled by the |
| * radio, but we may have to deal with this type of SM arriving via the IMS stack. If the |
| * data download service is not enabled, try to write to the USIM as an SMS, and send the |
| * UICC response as the acknowledgment to the SMSC. |
| * |
| * @param ust the UsimServiceTable, to check if data download is enabled |
| * @param smsMessage the SMS message to process |
| * @return {@code Activity.RESULT_OK} on success; {@code RESULT_SMS_GENERIC_ERROR} on failure |
| */ |
| int handleUsimDataDownload(UsimServiceTable ust, SmsMessage smsMessage) { |
| // If we receive an SMS-PP message before the UsimServiceTable has been loaded, |
| // assume that the data download service is not present. This is very unlikely to |
| // happen because the IMS connection will not be established until after the ISIM |
| // records have been loaded, after the USIM service table has been loaded. |
| if (ust != null && ust.isAvailable( |
| UsimServiceTable.UsimService.DATA_DL_VIA_SMS_PP)) { |
| Rlog.d(TAG, "Received SMS-PP data download, sending to UICC."); |
| return startDataDownload(smsMessage); |
| } else { |
| Rlog.d(TAG, "DATA_DL_VIA_SMS_PP service not available, storing message to UICC."); |
| String smsc = IccUtils.bytesToHexString( |
| PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength( |
| smsMessage.getServiceCenterAddress())); |
| mCi.writeSmsToSim(SmsManager.STATUS_ON_ICC_UNREAD, smsc, |
| IccUtils.bytesToHexString(smsMessage.getPdu()), |
| obtainMessage(EVENT_WRITE_SMS_COMPLETE)); |
| return Activity.RESULT_OK; // acknowledge after response from write to USIM |
| } |
| |
| } |
| |
| /** |
| * Start an SMS-PP data download for the specified message. Can be called from a different |
| * thread than this Handler is running on. |
| * |
| * @param smsMessage the message to process |
| * @return {@code Activity.RESULT_OK} on success; {@code RESULT_SMS_GENERIC_ERROR} on failure |
| */ |
| public int startDataDownload(SmsMessage smsMessage) { |
| if (sendMessage(obtainMessage(EVENT_START_DATA_DOWNLOAD, smsMessage))) { |
| return Activity.RESULT_OK; // we will send SMS ACK/ERROR based on UICC response |
| } else { |
| Rlog.e(TAG, "startDataDownload failed to send message to start data download."); |
| return Intents.RESULT_SMS_GENERIC_ERROR; |
| } |
| } |
| |
| private void handleDataDownload(SmsMessage smsMessage) { |
| int dcs = smsMessage.getDataCodingScheme(); |
| int pid = smsMessage.getProtocolIdentifier(); |
| byte[] pdu = smsMessage.getPdu(); // includes SC address |
| |
| int scAddressLength = pdu[0] & 0xff; |
| int tpduIndex = scAddressLength + 1; // start of TPDU |
| int tpduLength = pdu.length - tpduIndex; |
| |
| int bodyLength = getEnvelopeBodyLength(scAddressLength, tpduLength); |
| |
| // Add 1 byte for SMS-PP download tag and 1-2 bytes for BER-TLV length. |
| // See ETSI TS 102 223 Annex C for encoding of length and tags. |
| int totalLength = bodyLength + 1 + (bodyLength > 127 ? 2 : 1); |
| |
| byte[] envelope = new byte[totalLength]; |
| int index = 0; |
| |
| // SMS-PP download tag and length (assumed to be < 256 bytes). |
| envelope[index++] = (byte) BER_SMS_PP_DOWNLOAD_TAG; |
| if (bodyLength > 127) { |
| envelope[index++] = (byte) 0x81; // length 128-255 encoded as 0x81 + length |
| } |
| envelope[index++] = (byte) bodyLength; |
| |
| // Device identities TLV |
| envelope[index++] = (byte) (0x80 | ComprehensionTlvTag.DEVICE_IDENTITIES.value()); |
| envelope[index++] = (byte) 2; |
| envelope[index++] = (byte) DEV_ID_NETWORK; |
| envelope[index++] = (byte) DEV_ID_UICC; |
| |
| // Address TLV (if present). Encoded length is assumed to be < 127 bytes. |
| if (scAddressLength != 0) { |
| envelope[index++] = (byte) ComprehensionTlvTag.ADDRESS.value(); |
| envelope[index++] = (byte) scAddressLength; |
| System.arraycopy(pdu, 1, envelope, index, scAddressLength); |
| index += scAddressLength; |
| } |
| |
| // SMS TPDU TLV. Length is assumed to be < 256 bytes. |
| envelope[index++] = (byte) (0x80 | ComprehensionTlvTag.SMS_TPDU.value()); |
| if (tpduLength > 127) { |
| envelope[index++] = (byte) 0x81; // length 128-255 encoded as 0x81 + length |
| } |
| envelope[index++] = (byte) tpduLength; |
| System.arraycopy(pdu, tpduIndex, envelope, index, tpduLength); |
| index += tpduLength; |
| |
| // Verify that we calculated the payload size correctly. |
| if (index != envelope.length) { |
| Rlog.e(TAG, "startDataDownload() calculated incorrect envelope length, aborting."); |
| acknowledgeSmsWithError(CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR); |
| return; |
| } |
| |
| String encodedEnvelope = IccUtils.bytesToHexString(envelope); |
| mCi.sendEnvelopeWithStatus(encodedEnvelope, obtainMessage( |
| EVENT_SEND_ENVELOPE_RESPONSE, new int[]{ dcs, pid })); |
| } |
| |
| /** |
| * Return the size in bytes of the envelope to send to the UICC, excluding the |
| * SMS-PP download tag byte and length byte(s). If the size returned is <= 127, |
| * the BER-TLV length will be encoded in 1 byte, otherwise 2 bytes are required. |
| * |
| * @param scAddressLength the length of the SMSC address, or zero if not present |
| * @param tpduLength the length of the TPDU from the SMS-PP message |
| * @return the number of bytes to allocate for the envelope command |
| */ |
| private static int getEnvelopeBodyLength(int scAddressLength, int tpduLength) { |
| // Add 4 bytes for device identities TLV + 1 byte for SMS TPDU tag byte |
| int length = tpduLength + 5; |
| // Add 1 byte for TPDU length, or 2 bytes if length > 127 |
| length += (tpduLength > 127 ? 2 : 1); |
| // Add length of address tag, if present (+ 2 bytes for tag and length) |
| if (scAddressLength != 0) { |
| length = length + 2 + scAddressLength; |
| } |
| return length; |
| } |
| |
| /** |
| * Handle the response to the ENVELOPE command. |
| * @param response UICC response encoded as hexadecimal digits. First two bytes are the |
| * UICC SW1 and SW2 status bytes. |
| */ |
| private void sendSmsAckForEnvelopeResponse(IccIoResult response, int dcs, int pid) { |
| int sw1 = response.sw1; |
| int sw2 = response.sw2; |
| |
| boolean success; |
| if ((sw1 == 0x90 && sw2 == 0x00) || sw1 == 0x91) { |
| Rlog.d(TAG, "USIM data download succeeded: " + response.toString()); |
| success = true; |
| } else if (sw1 == 0x93 && sw2 == 0x00) { |
| Rlog.e(TAG, "USIM data download failed: Toolkit busy"); |
| acknowledgeSmsWithError(CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_APP_TOOLKIT_BUSY); |
| return; |
| } else if (sw1 == 0x62 || sw1 == 0x63) { |
| Rlog.e(TAG, "USIM data download failed: " + response.toString()); |
| success = false; |
| } else { |
| Rlog.e(TAG, "Unexpected SW1/SW2 response from UICC: " + response.toString()); |
| success = false; |
| } |
| |
| byte[] responseBytes = response.payload; |
| if (responseBytes == null || responseBytes.length == 0) { |
| if (success) { |
| mCi.acknowledgeLastIncomingGsmSms(true, 0, null); |
| } else { |
| acknowledgeSmsWithError( |
| CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_DATA_DOWNLOAD_ERROR); |
| } |
| return; |
| } |
| |
| byte[] smsAckPdu; |
| int index = 0; |
| if (success) { |
| smsAckPdu = new byte[responseBytes.length + 5]; |
| smsAckPdu[index++] = 0x00; // TP-MTI, TP-UDHI |
| smsAckPdu[index++] = 0x07; // TP-PI: TP-PID, TP-DCS, TP-UDL present |
| } else { |
| smsAckPdu = new byte[responseBytes.length + 6]; |
| smsAckPdu[index++] = 0x00; // TP-MTI, TP-UDHI |
| smsAckPdu[index++] = (byte) |
| CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_DATA_DOWNLOAD_ERROR; // TP-FCS |
| smsAckPdu[index++] = 0x07; // TP-PI: TP-PID, TP-DCS, TP-UDL present |
| } |
| |
| smsAckPdu[index++] = (byte) pid; |
| smsAckPdu[index++] = (byte) dcs; |
| |
| if (is7bitDcs(dcs)) { |
| int septetCount = responseBytes.length * 8 / 7; |
| smsAckPdu[index++] = (byte) septetCount; |
| } else { |
| smsAckPdu[index++] = (byte) responseBytes.length; |
| } |
| |
| System.arraycopy(responseBytes, 0, smsAckPdu, index, responseBytes.length); |
| |
| mCi.acknowledgeIncomingGsmSmsWithPdu(success, |
| IccUtils.bytesToHexString(smsAckPdu), null); |
| } |
| |
| private void acknowledgeSmsWithError(int cause) { |
| mCi.acknowledgeLastIncomingGsmSms(false, cause, null); |
| } |
| |
| /** |
| * Returns whether the DCS is 7 bit. If so, set TP-UDL to the septet count of TP-UD; |
| * otherwise, set TP-UDL to the octet count of TP-UD. |
| * @param dcs the TP-Data-Coding-Scheme field from the original download SMS |
| * @return true if the DCS specifies 7 bit encoding; false otherwise |
| */ |
| private static boolean is7bitDcs(int dcs) { |
| // See 3GPP TS 23.038 section 4 |
| return ((dcs & 0x8C) == 0x00) || ((dcs & 0xF4) == 0xF0); |
| } |
| |
| /** |
| * Handle UICC envelope response and send SMS acknowledgement. |
| * |
| * @param msg the message to handle |
| */ |
| @Override |
| public void handleMessage(Message msg) { |
| AsyncResult ar; |
| |
| switch (msg.what) { |
| case EVENT_START_DATA_DOWNLOAD: |
| handleDataDownload((SmsMessage) msg.obj); |
| break; |
| |
| case EVENT_SEND_ENVELOPE_RESPONSE: |
| ar = (AsyncResult) msg.obj; |
| |
| if (ar.exception != null) { |
| Rlog.e(TAG, "UICC Send Envelope failure, exception: " + ar.exception); |
| acknowledgeSmsWithError( |
| CommandsInterface.GSM_SMS_FAIL_CAUSE_USIM_DATA_DOWNLOAD_ERROR); |
| return; |
| } |
| |
| int[] dcsPid = (int[]) ar.userObj; |
| sendSmsAckForEnvelopeResponse((IccIoResult) ar.result, dcsPid[0], dcsPid[1]); |
| break; |
| |
| case EVENT_WRITE_SMS_COMPLETE: |
| ar = (AsyncResult) msg.obj; |
| if (ar.exception == null) { |
| Rlog.d(TAG, "Successfully wrote SMS-PP message to UICC"); |
| mCi.acknowledgeLastIncomingGsmSms(true, 0, null); |
| } else { |
| Rlog.d(TAG, "Failed to write SMS-PP message to UICC", ar.exception); |
| mCi.acknowledgeLastIncomingGsmSms(false, |
| CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR, null); |
| } |
| break; |
| |
| default: |
| Rlog.e(TAG, "Ignoring unexpected message, what=" + msg.what); |
| } |
| } |
| } |