summaryrefslogtreecommitdiffstats
path: root/telephony/java
diff options
context:
space:
mode:
authorJake Hamby <jhamby@google.com>2011-09-30 22:17:40 -0700
committerJake Hamby <jhamby@google.com>2011-10-21 16:43:24 -0700
commitac09d2af145b4d820a34f5e7628bc42e2e211bdb (patch)
tree5e6ec122ff1075fee1e8b13af7966f1c54a2d18c /telephony/java
parentab5040cf856b548c0355fdbc5257b68afccedfcc (diff)
downloadframeworks_base-ac09d2af145b4d820a34f5e7628bc42e2e211bdb.zip
frameworks_base-ac09d2af145b4d820a34f5e7628bc42e2e211bdb.tar.gz
frameworks_base-ac09d2af145b4d820a34f5e7628bc42e2e211bdb.tar.bz2
Add support for SMS-PP data download to USIM.
Devices supporting IMS may receive SMS-PP data download messages which are normally handled in the radio baseband. Add support to framework for these messages, passing the data to the UICC and sending the response data as part of the SMS ACK. Change-Id: I1da76982c6f8c402f82a6f535591e614f4e0de18
Diffstat (limited to 'telephony/java')
-rw-r--r--telephony/java/com/android/internal/telephony/CommandsInterface.java2
-rw-r--r--telephony/java/com/android/internal/telephony/IccIoResult.java4
-rw-r--r--telephony/java/com/android/internal/telephony/IccRecords.java5
-rw-r--r--telephony/java/com/android/internal/telephony/Phone.java7
-rw-r--r--telephony/java/com/android/internal/telephony/PhoneBase.java10
-rw-r--r--telephony/java/com/android/internal/telephony/PhoneProxy.java6
-rw-r--r--telephony/java/com/android/internal/telephony/cat/CatService.java55
-rw-r--r--telephony/java/com/android/internal/telephony/cat/ComprehensionTlvTag.java74
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java46
-rwxr-xr-xtelephony/java/com/android/internal/telephony/gsm/SIMRecords.java1
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/SmsMessage.java18
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/UsimDataDownloadHandler.java267
12 files changed, 436 insertions, 59 deletions
diff --git a/telephony/java/com/android/internal/telephony/CommandsInterface.java b/telephony/java/com/android/internal/telephony/CommandsInterface.java
index 9b42dbe..33ead75 100644
--- a/telephony/java/com/android/internal/telephony/CommandsInterface.java
+++ b/telephony/java/com/android/internal/telephony/CommandsInterface.java
@@ -157,6 +157,8 @@ public interface CommandsInterface {
// GSM SMS fail cause for acknowledgeLastIncomingSMS. From TS 23.040, 9.2.3.22.
static final int GSM_SMS_FAIL_CAUSE_MEMORY_CAPACITY_EXCEEDED = 0xD3;
+ static final int GSM_SMS_FAIL_CAUSE_USIM_APP_TOOLKIT_BUSY = 0xD4;
+ static final int GSM_SMS_FAIL_CAUSE_USIM_DATA_DOWNLOAD_ERROR = 0xD5;
static final int GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR = 0xFF;
// CDMA SMS fail cause for acknowledgeLastIncomingCdmaSms. From TS N.S0005, 6.5.2.125.
diff --git a/telephony/java/com/android/internal/telephony/IccIoResult.java b/telephony/java/com/android/internal/telephony/IccIoResult.java
index a6e0ec3..7043da5 100644
--- a/telephony/java/com/android/internal/telephony/IccIoResult.java
+++ b/telephony/java/com/android/internal/telephony/IccIoResult.java
@@ -21,8 +21,8 @@ package com.android.internal.telephony;
*/
public class
IccIoResult {
- int sw1;
- int sw2;
+ public int sw1;
+ public int sw2;
public byte[] payload;
diff --git a/telephony/java/com/android/internal/telephony/IccRecords.java b/telephony/java/com/android/internal/telephony/IccRecords.java
index 51ebd99..fc011c0 100644
--- a/telephony/java/com/android/internal/telephony/IccRecords.java
+++ b/telephony/java/com/android/internal/telephony/IccRecords.java
@@ -22,6 +22,7 @@ import android.os.Message;
import android.os.Registrant;
import android.os.RegistrantList;
+import com.android.internal.telephony.gsm.UsimServiceTable;
import com.android.internal.telephony.ims.IsimRecords;
/**
@@ -362,4 +363,8 @@ public abstract class IccRecords extends Handler implements IccConstants {
public IsimRecords getIsimRecords() {
return null;
}
+
+ public UsimServiceTable getUsimServiceTable() {
+ return null;
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java
index ca04eb2..9d189c1 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -28,6 +28,7 @@ import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import com.android.internal.telephony.DataConnection;
+import com.android.internal.telephony.gsm.UsimServiceTable;
import com.android.internal.telephony.ims.IsimRecords;
import com.android.internal.telephony.test.SimulatedRadioControl;
@@ -1765,4 +1766,10 @@ public interface Phone {
* messages are waiting
*/
void setVoiceMessageWaiting(int line, int countWaiting);
+
+ /**
+ * Gets the USIM service table from the UICC, if present and available.
+ * @return an interface to the UsimServiceTable record, or null if not available
+ */
+ UsimServiceTable getUsimServiceTable();
}
diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java
index 10121dd..94f7a13 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -37,6 +37,7 @@ import android.text.TextUtils;
import android.util.Log;
import com.android.internal.R;
+import com.android.internal.telephony.gsm.UsimServiceTable;
import com.android.internal.telephony.ims.IsimRecords;
import com.android.internal.telephony.test.SimulatedRadioControl;
import com.android.internal.telephony.gsm.SIMRecords;
@@ -1178,4 +1179,13 @@ public abstract class PhoneBase extends Handler implements Phone {
public void setVoiceMessageWaiting(int line, int countWaiting) {
mIccRecords.setVoiceMessageWaiting(line, countWaiting);
}
+
+ /**
+ * Gets the USIM service table from the UICC, if present and available.
+ * @return an interface to the UsimServiceTable record, or null if not available
+ */
+ @Override
+ public UsimServiceTable getUsimServiceTable() {
+ return mIccRecords.getUsimServiceTable();
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/PhoneProxy.java b/telephony/java/com/android/internal/telephony/PhoneProxy.java
index b497ec8..60f364e 100644
--- a/telephony/java/com/android/internal/telephony/PhoneProxy.java
+++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java
@@ -32,6 +32,7 @@ import android.util.Log;
import com.android.internal.telephony.cdma.CDMAPhone;
import com.android.internal.telephony.gsm.GSMPhone;
+import com.android.internal.telephony.gsm.UsimServiceTable;
import com.android.internal.telephony.ims.IsimRecords;
import com.android.internal.telephony.test.SimulatedRadioControl;
@@ -853,4 +854,9 @@ public class PhoneProxy extends Handler implements Phone {
public void setVoiceMessageWaiting(int line, int countWaiting) {
mActivePhone.setVoiceMessageWaiting(line, countWaiting);
}
+
+ @Override
+ public UsimServiceTable getUsimServiceTable() {
+ return mActivePhone.getUsimServiceTable();
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/cat/CatService.java b/telephony/java/com/android/internal/telephony/cat/CatService.java
index fb53686..5420264 100644
--- a/telephony/java/com/android/internal/telephony/cat/CatService.java
+++ b/telephony/java/com/android/internal/telephony/cat/CatService.java
@@ -34,61 +34,6 @@ import com.android.internal.telephony.IccRecords;
import java.io.ByteArrayOutputStream;
import java.util.Locale;
-/**
- * Enumeration for representing the tag value of COMPREHENSION-TLV objects. If
- * you want to get the actual value, call {@link #value() value} method.
- *
- * {@hide}
- */
-enum ComprehensionTlvTag {
- COMMAND_DETAILS(0x01),
- DEVICE_IDENTITIES(0x02),
- RESULT(0x03),
- DURATION(0x04),
- ALPHA_ID(0x05),
- USSD_STRING(0x0a),
- TEXT_STRING(0x0d),
- TONE(0x0e),
- ITEM(0x0f),
- ITEM_ID(0x10),
- RESPONSE_LENGTH(0x11),
- FILE_LIST(0x12),
- HELP_REQUEST(0x15),
- DEFAULT_TEXT(0x17),
- EVENT_LIST(0x19),
- ICON_ID(0x1e),
- ITEM_ICON_ID_LIST(0x1f),
- IMMEDIATE_RESPONSE(0x2b),
- LANGUAGE(0x2d),
- URL(0x31),
- BROWSER_TERMINATION_CAUSE(0x34),
- TEXT_ATTRIBUTE(0x50);
-
- private int mValue;
-
- ComprehensionTlvTag(int value) {
- mValue = value;
- }
-
- /**
- * Returns the actual value of this COMPREHENSION-TLV object.
- *
- * @return Actual tag value of this object
- */
- public int value() {
- return mValue;
- }
-
- public static ComprehensionTlvTag fromInt(int value) {
- for (ComprehensionTlvTag e : ComprehensionTlvTag.values()) {
- if (e.mValue == value) {
- return e;
- }
- }
- return null;
- }
-}
-
class RilMessage {
int mId;
Object mData;
diff --git a/telephony/java/com/android/internal/telephony/cat/ComprehensionTlvTag.java b/telephony/java/com/android/internal/telephony/cat/ComprehensionTlvTag.java
new file mode 100644
index 0000000..973dbc8
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/cat/ComprehensionTlvTag.java
@@ -0,0 +1,74 @@
+/*
+ * 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.cat;
+
+/**
+ * Enumeration for representing the tag value of COMPREHENSION-TLV objects. If
+ * you want to get the actual value, call {@link #value() value} method.
+ *
+ * {@hide}
+ */
+public enum ComprehensionTlvTag {
+ COMMAND_DETAILS(0x01),
+ DEVICE_IDENTITIES(0x02),
+ RESULT(0x03),
+ DURATION(0x04),
+ ALPHA_ID(0x05),
+ ADDRESS(0x06),
+ USSD_STRING(0x0a),
+ SMS_TPDU(0x0b),
+ TEXT_STRING(0x0d),
+ TONE(0x0e),
+ ITEM(0x0f),
+ ITEM_ID(0x10),
+ RESPONSE_LENGTH(0x11),
+ FILE_LIST(0x12),
+ HELP_REQUEST(0x15),
+ DEFAULT_TEXT(0x17),
+ EVENT_LIST(0x19),
+ ICON_ID(0x1e),
+ ITEM_ICON_ID_LIST(0x1f),
+ IMMEDIATE_RESPONSE(0x2b),
+ LANGUAGE(0x2d),
+ URL(0x31),
+ BROWSER_TERMINATION_CAUSE(0x34),
+ TEXT_ATTRIBUTE(0x50);
+
+ private int mValue;
+
+ ComprehensionTlvTag(int value) {
+ mValue = value;
+ }
+
+ /**
+ * Returns the actual value of this COMPREHENSION-TLV object.
+ *
+ * @return Actual tag value of this object
+ */
+ public int value() {
+ return mValue;
+ }
+
+ public static ComprehensionTlvTag fromInt(int value) {
+ for (ComprehensionTlvTag e : ComprehensionTlvTag.values()) {
+ if (e.mValue == value) {
+ return e;
+ }
+ }
+ return null;
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index c1553d8..d29e488 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -25,8 +25,9 @@ import android.os.Message;
import android.os.SystemProperties;
import android.provider.Telephony.Sms;
import android.provider.Telephony.Sms.Intents;
-import android.telephony.ServiceState;
+import android.telephony.PhoneNumberUtils;
import android.telephony.SmsCbMessage;
+import android.telephony.SmsManager;
import android.telephony.gsm.GsmCellLocation;
import android.util.Log;
@@ -41,7 +42,6 @@ import com.android.internal.telephony.SmsStorageMonitor;
import com.android.internal.telephony.SmsUsageMonitor;
import com.android.internal.telephony.TelephonyProperties;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@@ -56,9 +56,16 @@ public final class GsmSMSDispatcher extends SMSDispatcher {
/** New broadcast SMS */
private static final int EVENT_NEW_BROADCAST_SMS = 101;
+ /** Result of writing SM to UICC (when SMS-PP service is not available). */
+ private static final int EVENT_WRITE_SMS_COMPLETE = 102;
+
+ /** Handler for SMS-PP data download messages to UICC. */
+ private final UsimDataDownloadHandler mDataDownloadHandler;
+
public GsmSMSDispatcher(PhoneBase phone, SmsStorageMonitor storageMonitor,
SmsUsageMonitor usageMonitor) {
super(phone, storageMonitor, usageMonitor);
+ mDataDownloadHandler = new UsimDataDownloadHandler(mCm);
mCm.setOnNewGsmSms(this, EVENT_NEW_SMS, null);
mCm.setOnSmsStatus(this, EVENT_NEW_SMS_STATUS_REPORT, null);
mCm.setOnNewGsmBroadcastSms(this, EVENT_NEW_BROADCAST_SMS, null);
@@ -93,6 +100,18 @@ public final class GsmSMSDispatcher extends SMSDispatcher {
handleBroadcastSms((AsyncResult)msg.obj);
break;
+ case EVENT_WRITE_SMS_COMPLETE:
+ AsyncResult ar = (AsyncResult) msg.obj;
+ if (ar.exception == null) {
+ Log.d(TAG, "Successfully wrote SMS-PP message to UICC");
+ mCm.acknowledgeLastIncomingGsmSms(true, 0, null);
+ } else {
+ Log.d(TAG, "Failed to write SMS-PP message to UICC", ar.exception);
+ mCm.acknowledgeLastIncomingGsmSms(false,
+ CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR, null);
+ }
+ break;
+
default:
super.handleMessage(msg);
}
@@ -154,6 +173,29 @@ public final class GsmSMSDispatcher extends SMSDispatcher {
return Intents.RESULT_SMS_HANDLED;
}
+ // Send SMS-PP data download messages to UICC. See 3GPP TS 31.111 section 7.1.1.
+ if (sms.isUsimDataDownload()) {
+ UsimServiceTable ust = mPhone.getUsimServiceTable();
+ // 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)) {
+ Log.d(TAG, "Received SMS-PP data download, sending to UICC.");
+ return mDataDownloadHandler.startDataDownload(sms);
+ } else {
+ Log.d(TAG, "DATA_DL_VIA_SMS_PP service not available, storing message to UICC.");
+ String smsc = IccUtils.bytesToHexString(
+ PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength(
+ sms.getServiceCenterAddress()));
+ mCm.writeSmsToSim(SmsManager.STATUS_ON_ICC_UNREAD, smsc,
+ IccUtils.bytesToHexString(sms.getPdu()),
+ obtainMessage(EVENT_WRITE_SMS_COMPLETE));
+ return Activity.RESULT_OK; // acknowledge after response from write to USIM
+ }
+ }
+
if (mSmsReceiveDisabled) {
// Device doesn't support SMS service,
Log.d(TAG, "Received short message on device which doesn't support "
diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
index 8e965a3..495b5bc 100755
--- a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java
@@ -248,6 +248,7 @@ public class SIMRecords extends IccRecords {
return msisdn;
}
+ @Override
public UsimServiceTable getUsimServiceTable() {
return mUsimServiceTable;
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index 2da9642..677923f 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -769,6 +769,14 @@ public class SmsMessage extends SmsMessageBase {
return protocolIdentifier;
}
+ /**
+ * Returns the TP-Data-Coding-Scheme byte, for acknowledgement of SMS-PP download messages.
+ * @return the TP-DCS field of the SMS header
+ */
+ int getDataCodingScheme() {
+ return dataCodingScheme;
+ }
+
/** {@inheritDoc} */
@Override
public boolean isReplace() {
@@ -1129,4 +1137,14 @@ public class SmsMessage extends SmsMessageBase {
return messageClass;
}
+ /**
+ * Returns true if this is a (U)SIM data download type SM.
+ * See 3GPP TS 31.111 section 9.1 and TS 23.040 section 9.2.3.9.
+ *
+ * @return true if this is a USIM data download message; false otherwise
+ */
+ boolean isUsimDataDownload() {
+ return messageClass == MessageClass.CLASS_2 &&
+ (protocolIdentifier == 0x7f || protocolIdentifier == 0x7c);
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/UsimDataDownloadHandler.java b/telephony/java/com/android/internal/telephony/gsm/UsimDataDownloadHandler.java
new file mode 100644
index 0000000..f47ff1b
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/gsm/UsimDataDownloadHandler.java
@@ -0,0 +1,267 @@
+/*
+ * 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.util.Log;
+
+import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.IccIoResult;
+import com.android.internal.telephony.IccUtils;
+import com.android.internal.telephony.cat.ComprehensionTlvTag;
+
+/**
+ * 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;
+
+ private final CommandsInterface mCI;
+
+ public UsimDataDownloadHandler(CommandsInterface commandsInterface) {
+ mCI = commandsInterface;
+ }
+
+ /**
+ * 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 Activity.RESULT_OK on success; Intents.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 {
+ Log.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) {
+ Log.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) {
+ Log.d(TAG, "USIM data download succeeded: " + response.toString());
+ success = true;
+ } else if (sw1 == 0x93 && sw2 == 0x00) {
+ Log.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) {
+ Log.e(TAG, "USIM data download failed: " + response.toString());
+ success = false;
+ } else {
+ Log.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) {
+ switch (msg.what) {
+ case EVENT_START_DATA_DOWNLOAD:
+ handleDataDownload((SmsMessage) msg.obj);
+ break;
+
+ case EVENT_SEND_ENVELOPE_RESPONSE:
+ AsyncResult ar = (AsyncResult) msg.obj;
+
+ if (ar.exception != null) {
+ Log.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;
+
+ default:
+ Log.e(TAG, "Ignoring unexpected message, what=" + msg.what);
+ }
+ }
+}