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
This commit is contained in:
Jake Hamby
2011-09-30 22:17:40 -07:00
parent ab5040cf85
commit ac09d2af14
14 changed files with 1191 additions and 59 deletions

View File

@@ -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.

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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 "

View File

@@ -248,6 +248,7 @@ public class SIMRecords extends IccRecords {
return msisdn;
}
@Override
public UsimServiceTable getUsimServiceTable() {
return mUsimServiceTable;
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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) {
}
}

View File

@@ -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();
}
}