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
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 @@
// 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 @@
*/
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.Registrant;
import android.os.RegistrantList;
+import com.android.internal.telephony.gsm.UsimServiceTable;
import com.android.internal.telephony.ims.IsimRecords;
/**
@@ -362,4 +363,8 @@
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.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 @@
* 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.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 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 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 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 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.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.SmsUsageMonitor;
import com.android.internal.telephony.TelephonyProperties;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@@ -56,9 +56,16 @@
/** 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 @@
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 @@
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 @@
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 @@
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 @@
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);
+ }
+ }
+}
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java
new file mode 100644
index 0000000..7e0d3c4
--- /dev/null
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java
@@ -0,0 +1,611 @@
+/*
+ * 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.content.Context;
+import android.os.AsyncResult;
+import android.os.Message;
+import android.os.SystemClock;
+import android.util.Log;
+
+import com.android.internal.telephony.BaseCommands;
+import com.android.internal.telephony.IccIoResult;
+import com.android.internal.telephony.UUSInfo;
+
+import junit.framework.Assert;
+
+/**
+ * Dummy BaseCommands for UsimDataDownloadTest. Only implements UICC envelope and
+ * SMS acknowledgement commands.
+ */
+class UsimDataDownloadCommands extends BaseCommands {
+ private static final String TAG = "UsimDataDownloadCommands";
+
+ private boolean mExpectingAcknowledgeGsmSms; // true if expecting ack GSM SMS
+ private boolean mExpectingAcknowledgeGsmSmsSuccess; // true if expecting ack SMS success
+ private int mExpectingAcknowledgeGsmSmsFailureCause; // expecting ack SMS failure cause
+ private String mExpectingAcknowledgeGsmSmsPdu; // expecting ack SMS PDU
+
+ private boolean mExpectingSendEnvelope; // true to expect a send envelope command
+ private String mExpectingSendEnvelopeContents; // expected string for send envelope
+ private int mExpectingSendEnvelopeResponseSw1; // SW1/SW2 response status
+ private int mExpectingSendEnvelopeResponseSw2; // SW1/SW2 response status
+ private String mExpectingSendEnvelopeResponse; // Response string for Send Envelope
+
+ UsimDataDownloadCommands(Context context) {
+ super(context);
+ }
+
+ /**
+ * Expect a call to acknowledgeLastIncomingGsmSms with success flag and failure cause.
+ * @param success true if expecting success; false if expecting failure
+ * @param cause the failure cause, if success is false
+ */
+ synchronized void expectAcknowledgeGsmSms(boolean success, int cause) {
+ Assert.assertFalse("expectAcknowledgeGsmSms called twice", mExpectingAcknowledgeGsmSms);
+ mExpectingAcknowledgeGsmSms = true;
+ mExpectingAcknowledgeGsmSmsSuccess = success;
+ mExpectingAcknowledgeGsmSmsFailureCause = cause;
+ }
+
+ /**
+ * Expect a call to acknowledgeLastIncomingGsmSmsWithPdu with success flag and PDU.
+ * @param success true if expecting success; false if expecting failure
+ * @param ackPdu the acknowledgement PDU to expect
+ */
+ synchronized void expectAcknowledgeGsmSmsWithPdu(boolean success, String ackPdu) {
+ Assert.assertFalse("expectAcknowledgeGsmSms called twice", mExpectingAcknowledgeGsmSms);
+ mExpectingAcknowledgeGsmSms = true;
+ mExpectingAcknowledgeGsmSmsSuccess = success;
+ mExpectingAcknowledgeGsmSmsPdu = ackPdu;
+ }
+
+ /**
+ * Expect a call to sendEnvelopeWithStatus().
+ * @param contents expected envelope contents to send
+ * @param sw1 simulated SW1 status to return
+ * @param sw2 simulated SW2 status to return
+ * @param response simulated envelope response to return
+ */
+ synchronized void expectSendEnvelope(String contents, int sw1, int sw2, String response) {
+ Assert.assertFalse("expectSendEnvelope called twice", mExpectingSendEnvelope);
+ mExpectingSendEnvelope = true;
+ mExpectingSendEnvelopeContents = contents;
+ mExpectingSendEnvelopeResponseSw1 = sw1;
+ mExpectingSendEnvelopeResponseSw2 = sw2;
+ mExpectingSendEnvelopeResponse = response;
+ }
+
+ synchronized void assertExpectedMethodsCalled() {
+ long stopTime = SystemClock.elapsedRealtime() + 5000;
+ while ((mExpectingAcknowledgeGsmSms || mExpectingSendEnvelope)
+ && SystemClock.elapsedRealtime() < stopTime) {
+ try {
+ wait();
+ } catch (InterruptedException ignored) {}
+ }
+ Assert.assertFalse("expecting SMS acknowledge call", mExpectingAcknowledgeGsmSms);
+ Assert.assertFalse("expecting send envelope call", mExpectingSendEnvelope);
+ }
+
+ @Override
+ public synchronized void acknowledgeLastIncomingGsmSms(boolean success, int cause,
+ Message response) {
+ Log.d(TAG, "acknowledgeLastIncomingGsmSms: success=" + success + ", cause=" + cause);
+ Assert.assertTrue("unexpected call to acknowledge SMS", mExpectingAcknowledgeGsmSms);
+ Assert.assertEquals(mExpectingAcknowledgeGsmSmsSuccess, success);
+ Assert.assertEquals(mExpectingAcknowledgeGsmSmsFailureCause, cause);
+ mExpectingAcknowledgeGsmSms = false;
+ if (response != null) {
+ AsyncResult.forMessage(response);
+ response.sendToTarget();
+ }
+ notifyAll(); // wake up assertExpectedMethodsCalled()
+ }
+
+ @Override
+ public synchronized void acknowledgeIncomingGsmSmsWithPdu(boolean success, String ackPdu,
+ Message response) {
+ Log.d(TAG, "acknowledgeLastIncomingGsmSmsWithPdu: success=" + success
+ + ", ackPDU= " + ackPdu);
+ Assert.assertTrue("unexpected call to acknowledge SMS", mExpectingAcknowledgeGsmSms);
+ Assert.assertEquals(mExpectingAcknowledgeGsmSmsSuccess, success);
+ Assert.assertEquals(mExpectingAcknowledgeGsmSmsPdu, ackPdu);
+ mExpectingAcknowledgeGsmSms = false;
+ if (response != null) {
+ AsyncResult.forMessage(response);
+ response.sendToTarget();
+ }
+ notifyAll(); // wake up assertExpectedMethodsCalled()
+ }
+
+ @Override
+ public synchronized void sendEnvelopeWithStatus(String contents, Message response) {
+ // Add spaces between hex bytes for readability
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < contents.length(); i += 2) {
+ builder.append(contents.charAt(i)).append(contents.charAt(i+1)).append(' ');
+ }
+ Log.d(TAG, "sendEnvelopeWithStatus: " + builder.toString());
+
+ Assert.assertTrue("unexpected call to send envelope", mExpectingSendEnvelope);
+ Assert.assertEquals(mExpectingSendEnvelopeContents, contents);
+ mExpectingSendEnvelope = false;
+
+ IccIoResult result = new IccIoResult(mExpectingSendEnvelopeResponseSw1,
+ mExpectingSendEnvelopeResponseSw2, mExpectingSendEnvelopeResponse);
+
+ if (response != null) {
+ AsyncResult.forMessage(response, result, null);
+ response.sendToTarget();
+ }
+ notifyAll(); // wake up assertExpectedMethodsCalled()
+ }
+
+ @Override
+ public void setSuppServiceNotifications(boolean enable, Message result) {
+ }
+
+ @Override
+ public void supplyIccPin(String pin, Message result) {
+ }
+
+ @Override
+ public void supplyIccPinForApp(String pin, String aid, Message result) {
+ }
+
+ @Override
+ public void supplyIccPuk(String puk, String newPin, Message result) {
+ }
+
+ @Override
+ public void supplyIccPukForApp(String puk, String newPin, String aid, Message result) {
+ }
+
+ @Override
+ public void supplyIccPin2(String pin2, Message result) {
+ }
+
+ @Override
+ public void supplyIccPin2ForApp(String pin2, String aid, Message result) {
+ }
+
+ @Override
+ public void supplyIccPuk2(String puk2, String newPin2, Message result) {
+ }
+
+ @Override
+ public void supplyIccPuk2ForApp(String puk2, String newPin2, String aid, Message result) {
+ }
+
+ @Override
+ public void changeIccPin(String oldPin, String newPin, Message result) {
+ }
+
+ @Override
+ public void changeIccPinForApp(String oldPin, String newPin, String aidPtr, Message result) {
+ }
+
+ @Override
+ public void changeIccPin2(String oldPin2, String newPin2, Message result) {
+ }
+
+ @Override
+ public void changeIccPin2ForApp(String oldPin2, String newPin2, String aidPtr, Message result) {
+ }
+
+ @Override
+ public void changeBarringPassword(String facility, String oldPwd, String newPwd,
+ Message result) {
+ }
+
+ @Override
+ public void supplyNetworkDepersonalization(String netpin, Message result) {
+ }
+
+ @Override
+ public void getCurrentCalls(Message result) {
+ }
+
+ @Override
+ public void getPDPContextList(Message result) {
+ }
+
+ @Override
+ public void getDataCallList(Message result) {
+ }
+
+ @Override
+ public void dial(String address, int clirMode, Message result) {
+ }
+
+ @Override
+ public void dial(String address, int clirMode, UUSInfo uusInfo, Message result) {
+ }
+
+ @Override
+ public void getIMSI(Message result) {
+ }
+
+ @Override
+ public void getIMEI(Message result) {
+ }
+
+ @Override
+ public void getIMEISV(Message result) {
+ }
+
+ @Override
+ public void hangupConnection(int gsmIndex, Message result) {
+ }
+
+ @Override
+ public void hangupWaitingOrBackground(Message result) {
+ }
+
+ @Override
+ public void hangupForegroundResumeBackground(Message result) {
+ }
+
+ @Override
+ public void switchWaitingOrHoldingAndActive(Message result) {
+ }
+
+ @Override
+ public void conference(Message result) {
+ }
+
+ @Override
+ public void setPreferredVoicePrivacy(boolean enable, Message result) {
+ }
+
+ @Override
+ public void getPreferredVoicePrivacy(Message result) {
+ }
+
+ @Override
+ public void separateConnection(int gsmIndex, Message result) {
+ }
+
+ @Override
+ public void acceptCall(Message result) {
+ }
+
+ @Override
+ public void rejectCall(Message result) {
+ }
+
+ @Override
+ public void explicitCallTransfer(Message result) {
+ }
+
+ @Override
+ public void getLastCallFailCause(Message result) {
+ }
+
+ @Override
+ public void getLastPdpFailCause(Message result) {
+ }
+
+ @Override
+ public void getLastDataCallFailCause(Message result) {
+ }
+
+ @Override
+ public void setMute(boolean enableMute, Message response) {
+ }
+
+ @Override
+ public void getMute(Message response) {
+ }
+
+ @Override
+ public void getSignalStrength(Message response) {
+ }
+
+ @Override
+ public void getVoiceRegistrationState(Message response) {
+ }
+
+ @Override
+ public void getDataRegistrationState(Message response) {
+ }
+
+ @Override
+ public void getOperator(Message response) {
+ }
+
+ @Override
+ public void sendDtmf(char c, Message result) {
+ }
+
+ @Override
+ public void startDtmf(char c, Message result) {
+ }
+
+ @Override
+ public void stopDtmf(Message result) {
+ }
+
+ @Override
+ public void sendBurstDtmf(String dtmfString, int on, int off, Message result) {
+ }
+
+ @Override
+ public void sendSMS(String smscPDU, String pdu, Message response) {
+ }
+
+ @Override
+ public void sendCdmaSms(byte[] pdu, Message response) {
+ }
+
+ @Override
+ public void deleteSmsOnSim(int index, Message response) {
+ }
+
+ @Override
+ public void deleteSmsOnRuim(int index, Message response) {
+ }
+
+ @Override
+ public void writeSmsToSim(int status, String smsc, String pdu, Message response) {
+ }
+
+ @Override
+ public void writeSmsToRuim(int status, String pdu, Message response) {
+ }
+
+ @Override
+ public void setRadioPower(boolean on, Message response) {
+ }
+
+ @Override
+ public void acknowledgeLastIncomingCdmaSms(boolean success, int cause, Message response) {
+ }
+
+ @Override
+ public void iccIO(int command, int fileid, String path, int p1, int p2, int p3, String data,
+ String pin2, Message response) {
+ }
+
+ @Override
+ public void queryCLIP(Message response) {
+ }
+
+ @Override
+ public void getCLIR(Message response) {
+ }
+
+ @Override
+ public void setCLIR(int clirMode, Message response) {
+ }
+
+ @Override
+ public void queryCallWaiting(int serviceClass, Message response) {
+ }
+
+ @Override
+ public void setCallWaiting(boolean enable, int serviceClass, Message response) {
+ }
+
+ @Override
+ public void setCallForward(int action, int cfReason, int serviceClass, String number,
+ int timeSeconds, Message response) {
+ }
+
+ @Override
+ public void queryCallForwardStatus(int cfReason, int serviceClass, String number,
+ Message response) {
+ }
+
+ @Override
+ public void setNetworkSelectionModeAutomatic(Message response) {
+ }
+
+ @Override
+ public void setNetworkSelectionModeManual(String operatorNumeric, Message response) {
+ }
+
+ @Override
+ public void getNetworkSelectionMode(Message response) {
+ }
+
+ @Override
+ public void getAvailableNetworks(Message response) {
+ }
+
+ @Override
+ public void getBasebandVersion(Message response) {
+ }
+
+ @Override
+ public void queryFacilityLock(String facility, String password, int serviceClass,
+ Message response) {
+ }
+
+ @Override
+ public void queryFacilityLockForApp(String facility, String password, int serviceClass,
+ String appId, Message response) {
+ }
+
+ @Override
+ public void setFacilityLock(String facility, boolean lockState, String password,
+ int serviceClass, Message response) {
+ }
+
+ @Override
+ public void setFacilityLockForApp(String facility, boolean lockState, String password,
+ int serviceClass, String appId, Message response) {
+ }
+
+ @Override
+ public void sendUSSD(String ussdString, Message response) {
+ }
+
+ @Override
+ public void cancelPendingUssd(Message response) {
+ }
+
+ @Override
+ public void resetRadio(Message result) {
+ }
+
+ @Override
+ public void setBandMode(int bandMode, Message response) {
+ }
+
+ @Override
+ public void queryAvailableBandMode(Message response) {
+ }
+
+ @Override
+ public void setPreferredNetworkType(int networkType, Message response) {
+ }
+
+ @Override
+ public void getPreferredNetworkType(Message response) {
+ }
+
+ @Override
+ public void getNeighboringCids(Message response) {
+ }
+
+ @Override
+ public void setLocationUpdates(boolean enable, Message response) {
+ }
+
+ @Override
+ public void getSmscAddress(Message result) {
+ }
+
+ @Override
+ public void setSmscAddress(String address, Message result) {
+ }
+
+ @Override
+ public void reportSmsMemoryStatus(boolean available, Message result) {
+ }
+
+ @Override
+ public void reportStkServiceIsRunning(Message result) {
+ }
+
+ @Override
+ public void invokeOemRilRequestRaw(byte[] data, Message response) {
+ }
+
+ @Override
+ public void invokeOemRilRequestStrings(String[] strings, Message response) {
+ }
+
+ @Override
+ public void sendTerminalResponse(String contents, Message response) {
+ }
+
+ @Override
+ public void sendEnvelope(String contents, Message response) {
+ }
+
+ @Override
+ public void handleCallSetupRequestFromSim(boolean accept, Message response) {
+ }
+
+ @Override
+ public void setGsmBroadcastActivation(boolean activate, Message result) {
+ }
+
+ @Override
+ public void setGsmBroadcastConfig(SmsBroadcastConfigInfo[] config, Message response) {
+ }
+
+ @Override
+ public void getGsmBroadcastConfig(Message response) {
+ }
+
+ @Override
+ public void getDeviceIdentity(Message response) {
+ }
+
+ @Override
+ public void getCDMASubscription(Message response) {
+ }
+
+ @Override
+ public void sendCDMAFeatureCode(String FeatureCode, Message response) {
+ }
+
+ @Override
+ public void setPhoneType(int phoneType) {
+ }
+
+ @Override
+ public void queryCdmaRoamingPreference(Message response) {
+ }
+
+ @Override
+ public void setCdmaRoamingPreference(int cdmaRoamingType, Message response) {
+ }
+
+ @Override
+ public void setCdmaSubscriptionSource(int cdmaSubscriptionType, Message response) {
+ }
+
+ @Override
+ public void getCdmaSubscriptionSource(Message response) {
+ }
+
+ @Override
+ public void setTTYMode(int ttyMode, Message response) {
+ }
+
+ @Override
+ public void queryTTYMode(Message response) {
+ }
+
+ @Override
+ public void setupDataCall(String radioTechnology, String profile, String apn, String user,
+ String password, String authType, String protocol, Message result) {
+ }
+
+ @Override
+ public void deactivateDataCall(int cid, int reason, Message result) {
+ }
+
+ @Override
+ public void setCdmaBroadcastActivation(boolean activate, Message result) {
+ }
+
+ @Override
+ public void setCdmaBroadcastConfig(int[] configValuesArray, Message result) {
+ }
+
+ @Override
+ public void getCdmaBroadcastConfig(Message result) {
+ }
+
+ @Override
+ public void exitEmergencyCallbackMode(Message response) {
+ }
+
+ @Override
+ public void getIccCardStatus(Message result) {
+ }
+
+ @Override
+ public void requestIsimAuthentication(String nonce, Message response) {
+ }
+}
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadTest.java
new file mode 100644
index 0000000..6c8ba5e
--- /dev/null
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadTest.java
@@ -0,0 +1,144 @@
+/*
+ * 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.os.HandlerThread;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.nio.charset.Charset;
+
+/**
+ * Test SMS-PP data download to UICC.
+ * Uses test messages from 3GPP TS 31.124 section 27.22.5.
+ */
+public class UsimDataDownloadTest extends AndroidTestCase {
+ private static final String TAG = "UsimDataDownloadTest";
+
+ class TestHandlerThread extends HandlerThread {
+ private UsimDataDownloadHandler mHandler;
+
+ TestHandlerThread() {
+ super("TestHandlerThread");
+ }
+
+ @Override
+ protected void onLooperPrepared() {
+ synchronized (this) {
+ mHandler = new UsimDataDownloadHandler(mCm);
+ notifyAll();
+ }
+ }
+
+ UsimDataDownloadHandler getHandler() {
+ synchronized (this) {
+ while (mHandler == null) {
+ try {
+ wait();
+ } catch (InterruptedException ignored) {}
+ }
+ return mHandler;
+ }
+ }
+ }
+
+ private UsimDataDownloadCommands mCm;
+ private TestHandlerThread mHandlerThread;
+ UsimDataDownloadHandler mHandler;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mCm = new UsimDataDownloadCommands(mContext);
+ mHandlerThread = new TestHandlerThread();
+ mHandlerThread.start();
+ mHandler = mHandlerThread.getHandler();
+ Log.d(TAG, "mHandler is constructed");
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mHandlerThread.quit();
+ super.tearDown();
+ }
+
+ // SMS-PP Message 3.1.1
+ private static final byte[] SMS_PP_MESSAGE_3_1_1 = {
+ // Service center address
+ 0x09, (byte) 0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, (byte) 0xf8,
+
+ 0x04, 0x04, (byte) 0x91, 0x21, 0x43, 0x7f, 0x16, (byte) 0x89, 0x10, 0x10, 0x00, 0x00,
+ 0x00, 0x00, 0x0d, 0x54, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61,
+ 0x67, 0x65, 0x20, 0x31
+ };
+
+ // SMS-PP Download Envelope 3.1.1
+ private static final String SMS_PP_ENVELOPE_3_1_1 = "d12d8202838106099111223344556677f88b1c04"
+ + "049121437f16891010000000000d546573744d6573736167652031";
+
+ // SMS-PP Message 3.1.5
+ private static final byte[] SMS_PP_MESSAGE_3_1_5 = {
+ // Service center address
+ 0x09, (byte) 0x91, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, (byte) 0xf8,
+
+ 0x44, 0x04, (byte) 0x91, 0x21, 0x43, 0x7f, (byte) 0xf6, (byte) 0x89, 0x10, 0x10, 0x00,
+ 0x00, 0x00, 0x00, 0x1e, 0x02, 0x70, 0x00, 0x00, 0x19, 0x00, 0x0d, 0x00, 0x00,
+ 0x00, 0x00, (byte) 0xbf, (byte) 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+ (byte) 0xdc, (byte) 0xdc, (byte) 0xdc, (byte) 0xdc, (byte) 0xdc, (byte) 0xdc,
+ (byte) 0xdc, (byte) 0xdc, (byte) 0xdc, (byte) 0xdc
+ };
+
+ // SMS-PP Download Envelope 3.1.5
+ private static final String SMS_PP_ENVELOPE_3_1_5 = "d13e8202838106099111223344556677f88b2d44"
+ + "049121437ff6891010000000001e0270000019000d00000000bfff00000000000100"
+ + "dcdcdcdcdcdcdcdcdcdc";
+
+ public void testDataDownloadMessage1() {
+ SmsMessage message = SmsMessage.createFromPdu(SMS_PP_MESSAGE_3_1_1);
+ assertTrue("message is SMS-PP data download", message.isUsimDataDownload());
+
+ mCm.expectSendEnvelope(SMS_PP_ENVELOPE_3_1_1, 0x90, 0x00, "");
+ mCm.expectAcknowledgeGsmSms(true, 0);
+ mHandler.startDataDownload(message);
+ mCm.assertExpectedMethodsCalled();
+
+ mCm.expectSendEnvelope(SMS_PP_ENVELOPE_3_1_1, 0x90, 0x00, "0123456789");
+ mCm.expectAcknowledgeGsmSmsWithPdu(true, "00077f16050123456789");
+ mHandler.startDataDownload(message);
+ mCm.assertExpectedMethodsCalled();
+
+ mCm.expectSendEnvelope(SMS_PP_ENVELOPE_3_1_1, 0x62, 0xff, "0123456789abcdef");
+ mCm.expectAcknowledgeGsmSmsWithPdu(false, "00d5077f16080123456789abcdef");
+ mHandler.startDataDownload(message);
+ mCm.assertExpectedMethodsCalled();
+ }
+
+ public void testDataDownloadMessage5() {
+ SmsMessage message = SmsMessage.createFromPdu(SMS_PP_MESSAGE_3_1_5);
+ assertTrue("message is SMS-PP data download", message.isUsimDataDownload());
+
+ mCm.expectSendEnvelope(SMS_PP_ENVELOPE_3_1_5, 0x90, 0x00, "9876543210");
+ mCm.expectAcknowledgeGsmSmsWithPdu(true, "00077ff6059876543210");
+ mHandler.startDataDownload(message);
+ mCm.assertExpectedMethodsCalled();
+
+ mCm.expectSendEnvelope(SMS_PP_ENVELOPE_3_1_5, 0x93, 0x00, "");
+ mCm.expectAcknowledgeGsmSms(false, 0xd4); // SIM toolkit busy
+ mHandler.startDataDownload(message);
+ mCm.assertExpectedMethodsCalled();
+ }
+}