diff options
Diffstat (limited to 'telephony')
72 files changed, 3119 insertions, 534 deletions
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java index 5542c42..8e4f6fc 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -55,6 +55,12 @@ public class PhoneNumberUtils public static final char WILD = 'N'; /* + * Calling Line Identification Restriction (CLIR) + */ + private static final String CLIR_ON = "*31#+"; + private static final String CLIR_OFF = "#31#+"; + + /* * TOA = TON + NPI * See TS 24.008 section 10.5.4.7 for details. * These are the only really useful TOA values @@ -179,8 +185,6 @@ public class PhoneNumberUtils * Please note that the GSM wild character is allowed in the result. * This must be resolved before dialing. * - * Allows + only in the first position in the result string. - * * Returns null if phoneNumber == null */ public static String @@ -203,6 +207,11 @@ public class PhoneNumberUtils } } + int pos = addPlusChar(phoneNumber); + if (pos >= 0 && ret.length() > pos) { + ret.insert(pos, '+'); + } + return ret.toString(); } @@ -304,6 +313,28 @@ public class PhoneNumberUtils } } + /** GSM codes + * Finds if a GSM code includes the international prefix (+). + * + * @param number the number to dial. + * + * @return the position where the + char will be inserted, -1 if the GSM code was not found. + */ + private static int + addPlusChar(String number) { + int pos = -1; + + if (number.startsWith(CLIR_OFF)) { + pos = CLIR_OFF.length() - 1; + } + + if (number.startsWith(CLIR_ON)) { + pos = CLIR_ON.length() - 1; + } + + return pos; + } + /** * Extracts the post-dial sequence of DTMF control digits, pauses, and * waits. Strips separators. This string may be empty, but will not be null @@ -1119,7 +1150,7 @@ public class PhoneNumberUtils && text.charAt(2) == '1') { formatType = FORMAT_JAPAN; } else { - return; + formatType = FORMAT_UNKNOWN; } } @@ -1130,6 +1161,9 @@ public class PhoneNumberUtils case FORMAT_JAPAN: formatJapaneseNumber(text); return; + case FORMAT_UNKNOWN: + removeDashes(text); + return; } } @@ -1165,14 +1199,7 @@ public class PhoneNumberUtils CharSequence saved = text.subSequence(0, length); // Strip the dashes first, as we're going to add them back - int p = 0; - while (p < text.length()) { - if (text.charAt(p) == '-') { - text.delete(p, p + 1); - } else { - p++; - } - } + removeDashes(text); length = text.length(); // When scanning the number we record where dashes need to be added, @@ -1276,6 +1303,22 @@ public class PhoneNumberUtils JapanesePhoneNumberFormatter.format(text); } + /** + * Removes all dashes from the number. + * + * @param text the number to clear from dashes + */ + private static void removeDashes(Editable text) { + int p = 0; + while (p < text.length()) { + if (text.charAt(p) == '-') { + text.delete(p, p + 1); + } else { + p++; + } + } + } + // Three and four digit phone numbers for either special services, // or 3-6 digit addresses from the network (eg carrier-originated SMS messages) should // not match. diff --git a/telephony/java/android/telephony/SmsCbMessage.java b/telephony/java/android/telephony/SmsCbMessage.java new file mode 100644 index 0000000..3543275 --- /dev/null +++ b/telephony/java/android/telephony/SmsCbMessage.java @@ -0,0 +1,267 @@ +/* + * Copyright (C) 2010 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 android.telephony; + +import com.android.internal.telephony.GsmAlphabet; +import com.android.internal.telephony.gsm.SmsCbHeader; + +import java.io.UnsupportedEncodingException; + +/** + * Describes an SMS-CB message. + * + * {@hide} + */ +public class SmsCbMessage { + + /** + * Cell wide immediate geographical scope + */ + public static final int GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE = 0; + + /** + * PLMN wide geographical scope + */ + public static final int GEOGRAPHICAL_SCOPE_PLMN_WIDE = 1; + + /** + * Location / service area wide geographical scope + */ + public static final int GEOGRAPHICAL_SCOPE_LA_WIDE = 2; + + /** + * Cell wide geographical scope + */ + public static final int GEOGRAPHICAL_SCOPE_CELL_WIDE = 3; + + /** + * Create an instance of this class from a received PDU + * + * @param pdu PDU bytes + * @return An instance of this class, or null if invalid pdu + */ + public static SmsCbMessage createFromPdu(byte[] pdu) { + try { + return new SmsCbMessage(pdu); + } catch (IllegalArgumentException e) { + return null; + } + } + + /** + * Languages in the 0000xxxx DCS group as defined in 3GPP TS 23.038, section 5. + */ + private static final String[] LANGUAGE_CODES_GROUP_0 = { + "de", "en", "it", "fr", "es", "nl", "sv", "da", "pt", "fi", "no", "el", "tr", "hu", + "pl", null + }; + + /** + * Languages in the 0010xxxx DCS group as defined in 3GPP TS 23.038, section 5. + */ + private static final String[] LANGUAGE_CODES_GROUP_2 = { + "cs", "he", "ar", "ru", "is", null, null, null, null, null, null, null, null, null, + null, null + }; + + private static final char CARRIAGE_RETURN = 0x0d; + + private SmsCbHeader mHeader; + + private String mLanguage; + + private String mBody; + + private SmsCbMessage(byte[] pdu) throws IllegalArgumentException { + mHeader = new SmsCbHeader(pdu); + parseBody(pdu); + } + + /** + * Return the geographical scope of this message, one of + * {@link #GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE}, + * {@link #GEOGRAPHICAL_SCOPE_PLMN_WIDE}, + * {@link #GEOGRAPHICAL_SCOPE_LA_WIDE}, + * {@link #GEOGRAPHICAL_SCOPE_CELL_WIDE} + * + * @return Geographical scope + */ + public int getGeographicalScope() { + return mHeader.geographicalScope; + } + + /** + * Get the ISO-639-1 language code for this message, or null if unspecified + * + * @return Language code + */ + public String getLanguageCode() { + return mLanguage; + } + + /** + * Get the body of this message, or null if no body available + * + * @return Body, or null + */ + public String getMessageBody() { + return mBody; + } + + /** + * Get the message identifier of this message (0-65535) + * + * @return Message identifier + */ + public int getMessageIdentifier() { + return mHeader.messageIdentifier; + } + + /** + * Get the message code of this message (0-1023) + * + * @return Message code + */ + public int getMessageCode() { + return mHeader.messageCode; + } + + /** + * Get the update number of this message (0-15) + * + * @return Update number + */ + public int getUpdateNumber() { + return mHeader.updateNumber; + } + + private void parseBody(byte[] pdu) { + int encoding; + boolean hasLanguageIndicator = false; + + // Extract encoding and language from DCS, as defined in 3gpp TS 23.038, + // section 5. + switch ((mHeader.dataCodingScheme & 0xf0) >> 4) { + case 0x00: + encoding = SmsMessage.ENCODING_7BIT; + mLanguage = LANGUAGE_CODES_GROUP_0[mHeader.dataCodingScheme & 0x0f]; + break; + + case 0x01: + hasLanguageIndicator = true; + if ((mHeader.dataCodingScheme & 0x0f) == 0x01) { + encoding = SmsMessage.ENCODING_16BIT; + } else { + encoding = SmsMessage.ENCODING_7BIT; + } + break; + + case 0x02: + encoding = SmsMessage.ENCODING_7BIT; + mLanguage = LANGUAGE_CODES_GROUP_2[mHeader.dataCodingScheme & 0x0f]; + break; + + case 0x03: + encoding = SmsMessage.ENCODING_7BIT; + break; + + case 0x04: + case 0x05: + switch ((mHeader.dataCodingScheme & 0x0c) >> 2) { + case 0x01: + encoding = SmsMessage.ENCODING_8BIT; + break; + + case 0x02: + encoding = SmsMessage.ENCODING_16BIT; + break; + + case 0x00: + default: + encoding = SmsMessage.ENCODING_7BIT; + break; + } + break; + + case 0x06: + case 0x07: + // Compression not supported + case 0x09: + // UDH structure not supported + case 0x0e: + // Defined by the WAP forum not supported + encoding = SmsMessage.ENCODING_UNKNOWN; + break; + + case 0x0f: + if (((mHeader.dataCodingScheme & 0x04) >> 2) == 0x01) { + encoding = SmsMessage.ENCODING_8BIT; + } else { + encoding = SmsMessage.ENCODING_7BIT; + } + break; + + default: + // Reserved values are to be treated as 7-bit + encoding = SmsMessage.ENCODING_7BIT; + break; + } + + switch (encoding) { + case SmsMessage.ENCODING_7BIT: + mBody = GsmAlphabet.gsm7BitPackedToString(pdu, SmsCbHeader.PDU_HEADER_LENGTH, + (pdu.length - SmsCbHeader.PDU_HEADER_LENGTH) * 8 / 7); + + if (hasLanguageIndicator && mBody != null && mBody.length() > 2) { + mLanguage = mBody.substring(0, 2); + mBody = mBody.substring(3); + } + break; + + case SmsMessage.ENCODING_16BIT: + int offset = SmsCbHeader.PDU_HEADER_LENGTH; + + if (hasLanguageIndicator && pdu.length >= SmsCbHeader.PDU_HEADER_LENGTH + 2) { + mLanguage = GsmAlphabet.gsm7BitPackedToString(pdu, + SmsCbHeader.PDU_HEADER_LENGTH, 2); + offset += 2; + } + + try { + mBody = new String(pdu, offset, (pdu.length & 0xfffe) - offset, "utf-16"); + } catch (UnsupportedEncodingException e) { + // Eeeek + } + break; + + default: + break; + } + + if (mBody != null) { + // Remove trailing carriage return + for (int i = mBody.length() - 1; i >= 0; i--) { + if (mBody.charAt(i) != CARRIAGE_RETURN) { + mBody = mBody.substring(0, i + 1); + break; + } + } + } else { + mBody = ""; + } + } +} diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index f5e9751..0ecd854 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -341,7 +341,67 @@ public final class SmsManager { } return createMessageListFromRawRecords(records); - } + } + + /** + * Enable reception of cell broadcast (SMS-CB) messages with the given + * message identifier. Note that if two different clients enable the same + * message identifier, they must both disable it for the device to stop + * receiving those messages. All received messages will be broadcast in an + * intent with the action "android.provider.telephony.SMS_CB_RECEIVED". + * Note: This call is blocking, callers may want to avoid calling it from + * the main thread of an application. + * + * @param messageIdentifier Message identifier as specified in TS 23.041 + * @return true if successful, false otherwise + * @see #disableCellBroadcast(int) + * + * {@hide} + */ + public boolean enableCellBroadcast(int messageIdentifier) { + boolean success = false; + + try { + ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms")); + if (iccISms != null) { + success = iccISms.enableCellBroadcast(messageIdentifier); + } + } catch (RemoteException ex) { + // ignore it + } + + return success; + } + + /** + * Disable reception of cell broadcast (SMS-CB) messages with the given + * message identifier. Note that if two different clients enable the same + * message identifier, they must both disable it for the device to stop + * receiving those messages. + * Note: This call is blocking, callers may want to avoid calling it from + * the main thread of an application. + * + * @param messageIdentifier Message identifier as specified in TS 23.041 + * @return true if successful, false otherwise + * + * @see #enableCellBroadcast(int) + * + * {@hide} + */ + public boolean disableCellBroadcast(int messageIdentifier) { + boolean success = false; + + try { + ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms")); + if (iccISms != null) { + success = iccISms.disableCellBroadcast(messageIdentifier); + } + } catch (RemoteException ex) { + // ignore it + } + + return success; + } /** * Create a list of <code>SmsMessage</code>s from a list of RawSmsData diff --git a/telephony/java/com/android/internal/telephony/AdnRecordCache.java b/telephony/java/com/android/internal/telephony/AdnRecordCache.java index c8c0658..a175d49 100644 --- a/telephony/java/com/android/internal/telephony/AdnRecordCache.java +++ b/telephony/java/com/android/internal/telephony/AdnRecordCache.java @@ -186,7 +186,12 @@ public final class AdnRecordCache extends Handler implements IccConstants { } ArrayList<AdnRecord> oldAdnList; - oldAdnList = getRecordsIfLoaded(efid); + + if (efid == EF_PBR) { + oldAdnList = mUsimPhoneBookManager.loadEfFilesFromUsim(); + } else { + oldAdnList = getRecordsIfLoaded(efid); + } if (oldAdnList == null) { sendErrorResponse(response, "Adn list not exist for EF:" + efid); @@ -208,6 +213,17 @@ public final class AdnRecordCache extends Handler implements IccConstants { return; } + if (efid == EF_PBR) { + AdnRecord foundAdn = oldAdnList.get(index-1); + efid = foundAdn.efid; + extensionEF = foundAdn.extRecord; + index = foundAdn.recordNumber; + + newAdn.efid = efid; + newAdn.extRecord = extensionEF; + newAdn.recordNumber = index; + } + Message pendingResponse = userWriteResponse.get(efid); if (pendingResponse != null) { @@ -331,6 +347,7 @@ public final class AdnRecordCache extends Handler implements IccConstants { if (ar.exception == null) { adnLikeFiles.get(efid).set(index - 1, adn); + mUsimPhoneBookManager.invalidateCache(); } Message response = userWriteResponse.get(efid); diff --git a/telephony/java/com/android/internal/telephony/BaseCommands.java b/telephony/java/com/android/internal/telephony/BaseCommands.java index b962375..815fbfb 100644 --- a/telephony/java/com/android/internal/telephony/BaseCommands.java +++ b/telephony/java/com/android/internal/telephony/BaseCommands.java @@ -73,10 +73,10 @@ public abstract class BaseCommands implements CommandsInterface { protected Registrant mSmsOnSimRegistrant; protected Registrant mSmsStatusRegistrant; protected Registrant mSsnRegistrant; - protected Registrant mStkSessionEndRegistrant; - protected Registrant mStkProCmdRegistrant; - protected Registrant mStkEventRegistrant; - protected Registrant mStkCallSetUpRegistrant; + protected Registrant mCatSessionEndRegistrant; + protected Registrant mCatProCmdRegistrant; + protected Registrant mCatEventRegistrant; + protected Registrant mCatCallSetUpRegistrant; protected Registrant mIccSmsFullRegistrant; protected Registrant mEmergencyCallbackModeRegistrant; protected Registrant mIccRefreshRegistrant; @@ -395,36 +395,36 @@ public abstract class BaseCommands implements CommandsInterface { mSsnRegistrant.clear(); } - public void setOnStkSessionEnd(Handler h, int what, Object obj) { - mStkSessionEndRegistrant = new Registrant (h, what, obj); + public void setOnCatSessionEnd(Handler h, int what, Object obj) { + mCatSessionEndRegistrant = new Registrant (h, what, obj); } - public void unSetOnStkSessionEnd(Handler h) { - mStkSessionEndRegistrant.clear(); + public void unSetOnCatSessionEnd(Handler h) { + mCatSessionEndRegistrant.clear(); } - public void setOnStkProactiveCmd(Handler h, int what, Object obj) { - mStkProCmdRegistrant = new Registrant (h, what, obj); + public void setOnCatProactiveCmd(Handler h, int what, Object obj) { + mCatProCmdRegistrant = new Registrant (h, what, obj); } - public void unSetOnStkProactiveCmd(Handler h) { - mStkProCmdRegistrant.clear(); + public void unSetOnCatProactiveCmd(Handler h) { + mCatProCmdRegistrant.clear(); } - public void setOnStkEvent(Handler h, int what, Object obj) { - mStkEventRegistrant = new Registrant (h, what, obj); + public void setOnCatEvent(Handler h, int what, Object obj) { + mCatEventRegistrant = new Registrant (h, what, obj); } - public void unSetOnStkEvent(Handler h) { - mStkEventRegistrant.clear(); + public void unSetOnCatEvent(Handler h) { + mCatEventRegistrant.clear(); } - public void setOnStkCallSetUp(Handler h, int what, Object obj) { - mStkCallSetUpRegistrant = new Registrant (h, what, obj); + public void setOnCatCallSetUp(Handler h, int what, Object obj) { + mCatCallSetUpRegistrant = new Registrant (h, what, obj); } - public void unSetOnStkCallSetUp(Handler h) { - mStkCallSetUpRegistrant.clear(); + public void unSetOnCatCallSetUp(Handler h) { + mCatCallSetUpRegistrant.clear(); } public void setOnIccSmsFull(Handler h, int what, Object obj) { diff --git a/telephony/java/com/android/internal/telephony/CommandsInterface.java b/telephony/java/com/android/internal/telephony/CommandsInterface.java index 8e03c5a..5de0426 100644 --- a/telephony/java/com/android/internal/telephony/CommandsInterface.java +++ b/telephony/java/com/android/internal/telephony/CommandsInterface.java @@ -374,48 +374,48 @@ public interface CommandsInterface { void unSetOnSuppServiceNotification(Handler h); /** - * Sets the handler for Session End Notifications for STK. + * Sets the handler for Session End Notifications for CAT. * Unlike the register* methods, there's only one notification handler * * @param h Handler for notification message. * @param what User-defined message code. * @param obj User object. */ - void setOnStkSessionEnd(Handler h, int what, Object obj); - void unSetOnStkSessionEnd(Handler h); + void setOnCatSessionEnd(Handler h, int what, Object obj); + void unSetOnCatSessionEnd(Handler h); /** - * Sets the handler for Proactive Commands for STK. + * Sets the handler for Proactive Commands for CAT. * Unlike the register* methods, there's only one notification handler * * @param h Handler for notification message. * @param what User-defined message code. * @param obj User object. */ - void setOnStkProactiveCmd(Handler h, int what, Object obj); - void unSetOnStkProactiveCmd(Handler h); + void setOnCatProactiveCmd(Handler h, int what, Object obj); + void unSetOnCatProactiveCmd(Handler h); /** - * Sets the handler for Event Notifications for STK. + * Sets the handler for Event Notifications for CAT. * Unlike the register* methods, there's only one notification handler * * @param h Handler for notification message. * @param what User-defined message code. * @param obj User object. */ - void setOnStkEvent(Handler h, int what, Object obj); - void unSetOnStkEvent(Handler h); + void setOnCatEvent(Handler h, int what, Object obj); + void unSetOnCatEvent(Handler h); /** - * Sets the handler for Call Set Up Notifications for STK. + * Sets the handler for Call Set Up Notifications for CAT. * Unlike the register* methods, there's only one notification handler * * @param h Handler for notification message. * @param what User-defined message code. * @param obj User object. */ - void setOnStkCallSetUp(Handler h, int what, Object obj); - void unSetOnStkCallSetUp(Handler h); + void setOnCatCallSetUp(Handler h, int what, Object obj); + void unSetOnCatCallSetUp(Handler h); /** * Enables/disbables supplementary service related notifications from diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl index 65bad96..90de5e1 100644 --- a/telephony/java/com/android/internal/telephony/ISms.aidl +++ b/telephony/java/com/android/internal/telephony/ISms.aidl @@ -144,4 +144,30 @@ interface ISms { in List<String> parts, in List<PendingIntent> sentIntents, in List<PendingIntent> deliveryIntents); + /** + * Enable reception of cell broadcast (SMS-CB) messages with the given + * message identifier. Note that if two different clients enable the same + * message identifier, they must both disable it for the device to stop + * receiving those messages. + * + * @param messageIdentifier Message identifier as specified in TS 23.041 + * @return true if successful, false otherwise + * + * @see #disableCellBroadcast(int) + */ + boolean enableCellBroadcast(int messageIdentifier); + + /** + * Disable reception of cell broadcast (SMS-CB) messages with the given + * message identifier. Note that if two different clients enable the same + * message identifier, they must both disable it for the device to stop + * receiving those messages. + * + * @param messageIdentifier Message identifier as specified in TS 23.041 + * @return true if successful, false otherwise + * + * @see #enableCellBroadcast(int) + */ + boolean disableCellBroadcast(int messageIdentifier); + } diff --git a/telephony/java/com/android/internal/telephony/IccCard.java b/telephony/java/com/android/internal/telephony/IccCard.java index d3a34ec..90f9e8c 100644 --- a/telephony/java/com/android/internal/telephony/IccCard.java +++ b/telephony/java/com/android/internal/telephony/IccCard.java @@ -672,12 +672,11 @@ public abstract class IccCard { * @return true if a ICC card is present */ public boolean hasIccCard() { - boolean isIccPresent; - if (mPhone.getPhoneName().equals("GSM")) { - return mIccCardStatus.getCardState().isCardPresent(); - } else { - // TODO: Make work with a CDMA device with a RUIM card. + if (mIccCardStatus == null) { return false; + } else { + // Returns ICC card status for both GSM and CDMA mode + return mIccCardStatus.getCardState().isCardPresent(); } } diff --git a/telephony/java/com/android/internal/telephony/IccConstants.java b/telephony/java/com/android/internal/telephony/IccConstants.java index acc9197..b12d2d4 100644 --- a/telephony/java/com/android/internal/telephony/IccConstants.java +++ b/telephony/java/com/android/internal/telephony/IccConstants.java @@ -52,6 +52,7 @@ public interface IccConstants { static final int EF_SPN_CPHS = 0x6f14; static final int EF_SPN_SHORT_CPHS = 0x6f18; static final int EF_INFO_CPHS = 0x6f16; + static final int EF_CSP_CPHS = 0x6f15; // CDMA RUIM file ids from 3GPP2 C.S0023-0 static final int EF_CST = 0x6f32; diff --git a/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java b/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java index 9f8e57f..2f22d74 100644 --- a/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java +++ b/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java @@ -62,8 +62,8 @@ public abstract class IccPhoneBookInterfaceManager extends IIccPhoneBook.Stub { logd("GET_RECORD_SIZE Size " + recordSize[0] + " total " + recordSize[1] + " #record " + recordSize[2]); - mLock.notifyAll(); } + mLock.notifyAll(); } break; case EVENT_UPDATE_DONE: @@ -144,6 +144,9 @@ public abstract class IccPhoneBookInterfaceManager extends IIccPhoneBook.Stub { if (DBG) logd("updateAdnRecordsInEfBySearch: efid=" + efid + " ("+ oldTag + "," + oldPhoneNumber + ")"+ "==>" + " ("+ newTag + "," + newPhoneNumber + ")"+ " pin2=" + pin2); + + efid = updateEfForIccType(efid); + synchronized(mLock) { checkThread(); success = false; diff --git a/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java b/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java index 1910a9c..5049249 100644 --- a/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java +++ b/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java @@ -68,4 +68,12 @@ public class IccSmsInterfaceManagerProxy extends ISms.Stub { parts, sentIntents, deliveryIntents); } + public boolean enableCellBroadcast(int messageIdentifier) throws android.os.RemoteException { + return mIccSmsInterfaceManager.enableCellBroadcast(messageIdentifier); + } + + public boolean disableCellBroadcast(int messageIdentifier) throws android.os.RemoteException { + return mIccSmsInterfaceManager.disableCellBroadcast(messageIdentifier); + } + } diff --git a/telephony/java/com/android/internal/telephony/IccUtils.java b/telephony/java/com/android/internal/telephony/IccUtils.java index 71936f1..957eddd 100644 --- a/telephony/java/com/android/internal/telephony/IccUtils.java +++ b/telephony/java/com/android/internal/telephony/IccUtils.java @@ -51,6 +51,8 @@ public class IccUtils { ret.append((char)('0' + v)); v = (data[i] >> 4) & 0xf; + // Some PLMNs have 'f' as high nibble, ignore it + if (v == 0xf) continue; if (v > 9) break; ret.append((char)('0' + v)); } diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java index d5791eb..e426e94 100644 --- a/telephony/java/com/android/internal/telephony/Phone.java +++ b/telephony/java/com/android/internal/telephony/Phone.java @@ -1715,4 +1715,15 @@ public interface Phone { void unsetOnEcbModeExitResponse(Handler h); + /** + * TODO: Adding a function for each property is not good. + * A fucntion of type getPhoneProp(propType) where propType is an + * enum of GSM+CDMA+LTE props would be a better approach. + * + * Get "Restriction of menu options for manual PLMN selection" bit + * status from EF_CSP data, this belongs to "Value Added Services Group". + * @return true if this bit is set or EF_CSP data is unavailable, + * false otherwise + */ + boolean isCspPlmnEnabled(); } diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java index 53503a5..1674ad6 100644 --- a/telephony/java/com/android/internal/telephony/PhoneBase.java +++ b/telephony/java/com/android/internal/telephony/PhoneBase.java @@ -1022,6 +1022,13 @@ public abstract class PhoneBase extends Handler implements Phone { } } + public boolean isCspPlmnEnabled() { + // This function should be overridden by the class GSMPhone. + // Not implemented in CDMAPhone. + logUnexpectedGsmMethodCall("isCspPlmnEnabled"); + return false; + } + /** * Common error logger method for unexpected calls to CDMA-only methods. */ @@ -1030,4 +1037,12 @@ public abstract class PhoneBase extends Handler implements Phone { Log.e(LOG_TAG, "Error! " + name + "() in PhoneBase should not be " + "called, CDMAPhone inactive."); } + + /** + * Common error logger method for unexpected calls to GSM/WCDMA-only methods. + */ + private void logUnexpectedGsmMethodCall(String name) { + Log.e(LOG_TAG, "Error! " + name + "() in PhoneBase should not be " + + "called, GSMPhone inactive."); + } } diff --git a/telephony/java/com/android/internal/telephony/PhoneProxy.java b/telephony/java/com/android/internal/telephony/PhoneProxy.java index 6f08868..77f1e6c 100644 --- a/telephony/java/com/android/internal/telephony/PhoneProxy.java +++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java @@ -843,4 +843,8 @@ public class PhoneProxy extends Handler implements Phone { public void unsetOnEcbModeExitResponse(Handler h){ mActivePhone.unsetOnEcbModeExitResponse(h); } + + public boolean isCspPlmnEnabled() { + return mActivePhone.isCspPlmnEnabled(); + } } diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java index 3d410fd..2f7aa21 100644 --- a/telephony/java/com/android/internal/telephony/RIL.java +++ b/telephony/java/com/android/internal/telephony/RIL.java @@ -141,6 +141,7 @@ class RILRequest { this.mNext = sPool; sPool = this; sPoolSize++; + mResult = null; } } } @@ -1961,26 +1962,30 @@ public final class RIL extends BaseCommands implements CommandsInterface { sendScreenState(true); } - private void setRadioStateFromRILInt(int state) { - RadioState newState; + private RadioState getRadioStateFromInt(int stateInt) { + RadioState state; /* RIL_RadioState ril.h */ - switch(state) { - case 0: newState = RadioState.RADIO_OFF; break; - case 1: newState = RadioState.RADIO_UNAVAILABLE; break; - case 2: newState = RadioState.SIM_NOT_READY; break; - case 3: newState = RadioState.SIM_LOCKED_OR_ABSENT; break; - case 4: newState = RadioState.SIM_READY; break; - case 5: newState = RadioState.RUIM_NOT_READY; break; - case 6: newState = RadioState.RUIM_READY; break; - case 7: newState = RadioState.RUIM_LOCKED_OR_ABSENT; break; - case 8: newState = RadioState.NV_NOT_READY; break; - case 9: newState = RadioState.NV_READY; break; + switch(stateInt) { + case 0: state = RadioState.RADIO_OFF; break; + case 1: state = RadioState.RADIO_UNAVAILABLE; break; + case 2: state = RadioState.SIM_NOT_READY; break; + case 3: state = RadioState.SIM_LOCKED_OR_ABSENT; break; + case 4: state = RadioState.SIM_READY; break; + case 5: state = RadioState.RUIM_NOT_READY; break; + case 6: state = RadioState.RUIM_READY; break; + case 7: state = RadioState.RUIM_LOCKED_OR_ABSENT; break; + case 8: state = RadioState.NV_NOT_READY; break; + case 9: state = RadioState.NV_READY; break; default: throw new RuntimeException( - "Unrecognized RIL_RadioState: " +state); + "Unrecognized RIL_RadioState: " + stateInt); } + return state; + } + + private void switchToRadioState(RadioState newState) { if (mInitialRadioStateChange) { if (newState.isOn()) { @@ -2339,7 +2344,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { case RIL_UNSOL_RESTRICTED_STATE_CHANGED: ret = responseInts(p); break; case RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED: ret = responseVoid(p); break; case RIL_UNSOL_RESPONSE_CDMA_NEW_SMS: ret = responseCdmaSms(p); break; - case RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS: ret = responseString(p); break; + case RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS: ret = responseRaw(p); break; case RIL_UNSOL_CDMA_RUIM_SMS_STORAGE_FULL: ret = responseVoid(p); break; case RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE: ret = responseVoid(p); break; case RIL_UNSOL_CDMA_CALL_WAITING: ret = responseCdmaCallWaiting(p); break; @@ -2361,9 +2366,10 @@ public final class RIL extends BaseCommands implements CommandsInterface { switch(response) { case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED: /* has bonus radio state int */ - setRadioStateFromRILInt(p.readInt()); + RadioState newState = getRadioStateFromInt(p.readInt()); + if (RILJ_LOGD) unsljLogMore(response, newState.toString()); - if (RILJ_LOGD) unsljLogMore(response, mState.toString()); + switchToRadioState(newState); break; case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED: if (RILJ_LOGD) unsljLog(response); @@ -2481,8 +2487,8 @@ public final class RIL extends BaseCommands implements CommandsInterface { case RIL_UNSOL_STK_SESSION_END: if (RILJ_LOGD) unsljLog(response); - if (mStkSessionEndRegistrant != null) { - mStkSessionEndRegistrant.notifyRegistrant( + if (mCatSessionEndRegistrant != null) { + mCatSessionEndRegistrant.notifyRegistrant( new AsyncResult (null, ret, null)); } break; @@ -2490,8 +2496,8 @@ public final class RIL extends BaseCommands implements CommandsInterface { case RIL_UNSOL_STK_PROACTIVE_COMMAND: if (RILJ_LOGD) unsljLogRet(response, ret); - if (mStkProCmdRegistrant != null) { - mStkProCmdRegistrant.notifyRegistrant( + if (mCatProCmdRegistrant != null) { + mCatProCmdRegistrant.notifyRegistrant( new AsyncResult (null, ret, null)); } break; @@ -2499,8 +2505,8 @@ public final class RIL extends BaseCommands implements CommandsInterface { case RIL_UNSOL_STK_EVENT_NOTIFY: if (RILJ_LOGD) unsljLogRet(response, ret); - if (mStkEventRegistrant != null) { - mStkEventRegistrant.notifyRegistrant( + if (mCatEventRegistrant != null) { + mCatEventRegistrant.notifyRegistrant( new AsyncResult (null, ret, null)); } break; @@ -2508,8 +2514,8 @@ public final class RIL extends BaseCommands implements CommandsInterface { case RIL_UNSOL_STK_CALL_SETUP: if (RILJ_LOGD) unsljLogRet(response, ret); - if (mStkCallSetUpRegistrant != null) { - mStkCallSetUpRegistrant.notifyRegistrant( + if (mCatCallSetUpRegistrant != null) { + mCatCallSetUpRegistrant.notifyRegistrant( new AsyncResult (null, ret, null)); } break; @@ -3406,6 +3412,8 @@ public final class RIL extends BaseCommands implements CommandsInterface { RILRequest rr = RILRequest.obtain( RILConstants.RIL_REQUEST_QUERY_TTY_MODE, response); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); + send(rr); } @@ -3419,6 +3427,9 @@ public final class RIL extends BaseCommands implements CommandsInterface { rr.mp.writeInt(1); rr.mp.writeInt(ttyMode); + if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + + " : " + ttyMode); + send(rr); } diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java index 917e1d8..f93e494 100644 --- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java @@ -112,6 +112,9 @@ public abstract class SMSDispatcher extends Handler { /** Radio is ON */ static final protected int EVENT_RADIO_ON = 12; + /** New broadcast SMS */ + static final protected int EVENT_NEW_BROADCAST_SMS = 13; + protected Phone mPhone; protected Context mContext; protected ContentResolver mResolver; @@ -389,6 +392,9 @@ public abstract class SMSDispatcher extends Handler { mCm.reportSmsMemoryStatus(mStorageAvailable, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE)); } + + case EVENT_NEW_BROADCAST_SMS: + handleBroadcastSms((AsyncResult)msg.obj); break; } } @@ -984,4 +990,17 @@ public abstract class SMSDispatcher extends Handler { } } }; + + protected abstract void handleBroadcastSms(AsyncResult ar); + + protected void dispatchBroadcastPdus(byte[][] pdus) { + Intent intent = new Intent("android.provider.telephony.SMS_CB_RECEIVED"); + intent.putExtra("pdus", pdus); + + if (Config.LOGD) + Log.d(TAG, "Dispatching " + pdus.length + " SMS CB pdus"); + + dispatch(intent, "android.permission.RECEIVE_SMS"); + } + } diff --git a/telephony/java/com/android/internal/telephony/WapPushOverSms.java b/telephony/java/com/android/internal/telephony/WapPushOverSms.java index 168b63b..2f5d3ec 100644 --- a/telephony/java/com/android/internal/telephony/WapPushOverSms.java +++ b/telephony/java/com/android/internal/telephony/WapPushOverSms.java @@ -24,7 +24,6 @@ import android.provider.Telephony.Sms.Intents; import android.util.Config; import android.util.Log; - /** * WAP push handler class. * @@ -59,7 +58,7 @@ public class WapPushOverSms { */ public int dispatchWapPdu(byte[] pdu) { - if (Config.LOGD) Log.d(LOG_TAG, "Rx: " + IccUtils.bytesToHexString(pdu)); + if (Config.DEBUG) Log.d(LOG_TAG, "Rx: " + IccUtils.bytesToHexString(pdu)); int index = 0; int transactionId = pdu[index++] & 0xFF; @@ -68,7 +67,7 @@ public class WapPushOverSms { if ((pduType != WspTypeDecoder.PDU_TYPE_PUSH) && (pduType != WspTypeDecoder.PDU_TYPE_CONFIRMED_PUSH)) { - if (Config.LOGD) Log.w(LOG_TAG, "Received non-PUSH WAP PDU. Type = " + pduType); + if (Config.DEBUG) Log.w(LOG_TAG, "Received non-PUSH WAP PDU. Type = " + pduType); return Intents.RESULT_SMS_HANDLED; } @@ -81,7 +80,7 @@ public class WapPushOverSms { * So it will be encoded in no more than 5 octets. */ if (pduDecoder.decodeUintvarInteger(index) == false) { - if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Header Length error."); + if (Config.DEBUG) Log.w(LOG_TAG, "Received PDU. Header Length error."); return Intents.RESULT_SMS_GENERIC_ERROR; } headerLength = (int)pduDecoder.getValue32(); @@ -102,141 +101,44 @@ public class WapPushOverSms { * Length = Uintvar-integer */ if (pduDecoder.decodeContentType(index) == false) { - if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Header Content-Type error."); + if (Config.DEBUG) Log.w(LOG_TAG, "Received PDU. Header Content-Type error."); return Intents.RESULT_SMS_GENERIC_ERROR; } - int binaryContentType; + String mimeType = pduDecoder.getValueString(); - if (mimeType == null) { - binaryContentType = (int)pduDecoder.getValue32(); - // TODO we should have more generic way to map binaryContentType code to mimeType. - switch (binaryContentType) { - case WspTypeDecoder.CONTENT_TYPE_B_DRM_RIGHTS_XML: - mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_DRM_RIGHTS_XML; - break; - case WspTypeDecoder.CONTENT_TYPE_B_DRM_RIGHTS_WBXML: - mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_DRM_RIGHTS_WBXML; - break; - case WspTypeDecoder.CONTENT_TYPE_B_PUSH_SI: - mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_SI; - break; - case WspTypeDecoder.CONTENT_TYPE_B_PUSH_SL: - mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_SL; - break; - case WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO: - mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_CO; - break; - case WspTypeDecoder.CONTENT_TYPE_B_MMS: - mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_MMS; - break; - case WspTypeDecoder.CONTENT_TYPE_B_VND_DOCOMO_PF: - mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_VND_DOCOMO_PF; - break; - case WspTypeDecoder.CONTENT_TYPE_B_SUPL_INIT: - mimeType = WspTypeDecoder.CONTENT_MIME_TYPE_B_SUPL_INIT; - break; - default: - if (Config.LOGD) { - Log.w(LOG_TAG, - "Received PDU. Unsupported Content-Type = " + binaryContentType); - } - return Intents.RESULT_SMS_HANDLED; - } - } else { - if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_DRM_RIGHTS_XML)) { - binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_DRM_RIGHTS_XML; - } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_DRM_RIGHTS_WBXML)) { - binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_DRM_RIGHTS_WBXML; - } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_SI)) { - binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_PUSH_SI; - } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_SL)) { - binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_PUSH_SL; - } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_CO)) { - binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO; - } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_MMS)) { - binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_MMS; - } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_VND_DOCOMO_PF)) { - binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_VND_DOCOMO_PF; - } else if (mimeType.equals(WspTypeDecoder.CONTENT_MIME_TYPE_B_SUPL_INIT)) { - binaryContentType = WspTypeDecoder.CONTENT_TYPE_B_SUPL_INIT; - } else { - if (Config.LOGD) Log.w(LOG_TAG, "Received PDU. Unknown Content-Type = " + mimeType); - return Intents.RESULT_SMS_HANDLED; - } - } - index += pduDecoder.getDecodedDataLength(); - boolean dispatchedByApplication = false; - switch (binaryContentType) { - case WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO: - dispatchWapPdu_PushCO(pdu, transactionId, pduType, headerStartIndex, headerLength); - dispatchedByApplication = true; - break; - case WspTypeDecoder.CONTENT_TYPE_B_MMS: - dispatchWapPdu_MMS(pdu, transactionId, pduType, headerStartIndex, headerLength); - dispatchedByApplication = true; - break; - default: - break; - } - if (dispatchedByApplication == false) { - dispatchWapPdu_default(pdu, transactionId, pduType, mimeType, - headerStartIndex, headerLength); - } - return Activity.RESULT_OK; - } + index += pduDecoder.getDecodedDataLength(); - private void dispatchWapPdu_default(byte[] pdu, int transactionId, int pduType, - String mimeType, int headerStartIndex, int headerLength) { byte[] header = new byte[headerLength]; System.arraycopy(pdu, headerStartIndex, header, 0, header.length); - int dataIndex = headerStartIndex + headerLength; - byte[] data; - data = new byte[pdu.length - dataIndex]; - System.arraycopy(pdu, dataIndex, data, 0, data.length); + byte[] intentData; + String permission; - Intent intent = new Intent(Intents.WAP_PUSH_RECEIVED_ACTION); - intent.setType(mimeType); - intent.putExtra("transactionId", transactionId); - intent.putExtra("pduType", pduType); - intent.putExtra("header", header); - intent.putExtra("data", data); - - mSmsDispatcher.dispatch(intent, "android.permission.RECEIVE_WAP_PUSH"); - } + if (mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_PUSH_CO)) { + intentData = pdu; + } else { + int dataIndex = headerStartIndex + headerLength; + intentData = new byte[pdu.length - dataIndex]; + System.arraycopy(pdu, dataIndex, intentData, 0, intentData.length); + } - private void dispatchWapPdu_PushCO(byte[] pdu, int transactionId, int pduType, - int headerStartIndex, int headerLength) { - byte[] header = new byte[headerLength]; - System.arraycopy(pdu, headerStartIndex, header, 0, header.length); + if (mimeType.equals(WspTypeDecoder.CONTENT_TYPE_B_MMS)) { + permission = "android.permission.RECEIVE_MMS"; + } else { + permission = "android.permission.RECEIVE_WAP_PUSH"; + } Intent intent = new Intent(Intents.WAP_PUSH_RECEIVED_ACTION); - intent.setType(WspTypeDecoder.CONTENT_MIME_TYPE_B_PUSH_CO); + intent.setType(mimeType); intent.putExtra("transactionId", transactionId); intent.putExtra("pduType", pduType); intent.putExtra("header", header); - intent.putExtra("data", pdu); + intent.putExtra("data", intentData); + intent.putExtra("contentTypeParameters", pduDecoder.getContentParameters()); - mSmsDispatcher.dispatch(intent, "android.permission.RECEIVE_WAP_PUSH"); - } + mSmsDispatcher.dispatch(intent, permission); - private void dispatchWapPdu_MMS(byte[] pdu, int transactionId, int pduType, - int headerStartIndex, int headerLength) { - byte[] header = new byte[headerLength]; - System.arraycopy(pdu, headerStartIndex, header, 0, header.length); - int dataIndex = headerStartIndex + headerLength; - byte[] data = new byte[pdu.length - dataIndex]; - System.arraycopy(pdu, dataIndex, data, 0, data.length); - - Intent intent = new Intent(Intents.WAP_PUSH_RECEIVED_ACTION); - intent.setType(WspTypeDecoder.CONTENT_MIME_TYPE_B_MMS); - intent.putExtra("transactionId", transactionId); - intent.putExtra("pduType", pduType); - intent.putExtra("header", header); - intent.putExtra("data", data); - - mSmsDispatcher.dispatch(intent, "android.permission.RECEIVE_MMS"); + return Activity.RESULT_OK; } -} - +}
\ No newline at end of file diff --git a/telephony/java/com/android/internal/telephony/WspTypeDecoder.java b/telephony/java/com/android/internal/telephony/WspTypeDecoder.java index 5dc89f0..6bf6b13 100644 --- a/telephony/java/com/android/internal/telephony/WspTypeDecoder.java +++ b/telephony/java/com/android/internal/telephony/WspTypeDecoder.java @@ -16,11 +16,12 @@ package com.android.internal.telephony; +import java.util.HashMap; /** - * Implement the WSP data type decoder. + * Implement the WSP data type decoder. * - * @hide + * @hide */ public class WspTypeDecoder { @@ -30,37 +31,176 @@ public class WspTypeDecoder { public static final int PDU_TYPE_PUSH = 0x06; public static final int PDU_TYPE_CONFIRMED_PUSH = 0x07; - // TODO we should have mapping between those binary code and mime type string. - // see http://www.openmobilealliance.org/tech/omna/omna-wsp-content-type.aspx - - public static final int CONTENT_TYPE_B_DRM_RIGHTS_XML = 0x4a; - public static final int CONTENT_TYPE_B_DRM_RIGHTS_WBXML = 0x4b; - public static final int CONTENT_TYPE_B_PUSH_SI = 0x2e; - public static final int CONTENT_TYPE_B_PUSH_SL = 0x30; - public static final int CONTENT_TYPE_B_PUSH_CO = 0x32; - public static final int CONTENT_TYPE_B_MMS = 0x3e; - public static final int CONTENT_TYPE_B_VND_DOCOMO_PF = 0x0310; - public static final int CONTENT_TYPE_B_SUPL_INIT = 0x312; - - public static final String CONTENT_MIME_TYPE_B_DRM_RIGHTS_XML = - "application/vnd.oma.drm.rights+xml"; - public static final String CONTENT_MIME_TYPE_B_DRM_RIGHTS_WBXML = - "application/vnd.oma.drm.rights+wbxml"; - public static final String CONTENT_MIME_TYPE_B_PUSH_SI = "application/vnd.wap.sic"; - public static final String CONTENT_MIME_TYPE_B_PUSH_SL = "application/vnd.wap.slc"; - public static final String CONTENT_MIME_TYPE_B_PUSH_CO = "application/vnd.wap.coc"; - public static final String CONTENT_MIME_TYPE_B_MMS = "application/vnd.wap.mms-message"; - public static final String CONTENT_MIME_TYPE_B_VND_DOCOMO_PF = "application/vnd.docomo.pf"; - public static final String CONTENT_MIME_TYPE_B_SUPL_INIT = "application/vnd.omaloc-supl-init"; - - public static final int PARAMETER_ID_X_WAP_APPLICATION_ID = 0x2f; + private final static HashMap<Integer, String> WELL_KNOWN_MIME_TYPES = + new HashMap<Integer, String>(); + + private final static HashMap<Integer, String> WELL_KNOWN_PARAMETERS = + new HashMap<Integer, String>(); + + private static final int Q_VALUE = 0x00; + + static { + WELL_KNOWN_MIME_TYPES.put(0x00, "*/*"); + WELL_KNOWN_MIME_TYPES.put(0x01, "text/*"); + WELL_KNOWN_MIME_TYPES.put(0x02, "text/html"); + WELL_KNOWN_MIME_TYPES.put(0x03, "text/plain"); + WELL_KNOWN_MIME_TYPES.put(0x04, "text/x-hdml"); + WELL_KNOWN_MIME_TYPES.put(0x05, "text/x-ttml"); + WELL_KNOWN_MIME_TYPES.put(0x06, "text/x-vCalendar"); + WELL_KNOWN_MIME_TYPES.put(0x07, "text/x-vCard"); + WELL_KNOWN_MIME_TYPES.put(0x08, "text/vnd.wap.wml"); + WELL_KNOWN_MIME_TYPES.put(0x09, "text/vnd.wap.wmlscript"); + WELL_KNOWN_MIME_TYPES.put(0x0A, "text/vnd.wap.wta-event"); + WELL_KNOWN_MIME_TYPES.put(0x0B, "multipart/*"); + WELL_KNOWN_MIME_TYPES.put(0x0C, "multipart/mixed"); + WELL_KNOWN_MIME_TYPES.put(0x0D, "multipart/form-data"); + WELL_KNOWN_MIME_TYPES.put(0x0E, "multipart/byterantes"); + WELL_KNOWN_MIME_TYPES.put(0x0F, "multipart/alternative"); + WELL_KNOWN_MIME_TYPES.put(0x10, "application/*"); + WELL_KNOWN_MIME_TYPES.put(0x11, "application/java-vm"); + WELL_KNOWN_MIME_TYPES.put(0x12, "application/x-www-form-urlencoded"); + WELL_KNOWN_MIME_TYPES.put(0x13, "application/x-hdmlc"); + WELL_KNOWN_MIME_TYPES.put(0x14, "application/vnd.wap.wmlc"); + WELL_KNOWN_MIME_TYPES.put(0x15, "application/vnd.wap.wmlscriptc"); + WELL_KNOWN_MIME_TYPES.put(0x16, "application/vnd.wap.wta-eventc"); + WELL_KNOWN_MIME_TYPES.put(0x17, "application/vnd.wap.uaprof"); + WELL_KNOWN_MIME_TYPES.put(0x18, "application/vnd.wap.wtls-ca-certificate"); + WELL_KNOWN_MIME_TYPES.put(0x19, "application/vnd.wap.wtls-user-certificate"); + WELL_KNOWN_MIME_TYPES.put(0x1A, "application/x-x509-ca-cert"); + WELL_KNOWN_MIME_TYPES.put(0x1B, "application/x-x509-user-cert"); + WELL_KNOWN_MIME_TYPES.put(0x1C, "image/*"); + WELL_KNOWN_MIME_TYPES.put(0x1D, "image/gif"); + WELL_KNOWN_MIME_TYPES.put(0x1E, "image/jpeg"); + WELL_KNOWN_MIME_TYPES.put(0x1F, "image/tiff"); + WELL_KNOWN_MIME_TYPES.put(0x20, "image/png"); + WELL_KNOWN_MIME_TYPES.put(0x21, "image/vnd.wap.wbmp"); + WELL_KNOWN_MIME_TYPES.put(0x22, "application/vnd.wap.multipart.*"); + WELL_KNOWN_MIME_TYPES.put(0x23, "application/vnd.wap.multipart.mixed"); + WELL_KNOWN_MIME_TYPES.put(0x24, "application/vnd.wap.multipart.form-data"); + WELL_KNOWN_MIME_TYPES.put(0x25, "application/vnd.wap.multipart.byteranges"); + WELL_KNOWN_MIME_TYPES.put(0x26, "application/vnd.wap.multipart.alternative"); + WELL_KNOWN_MIME_TYPES.put(0x27, "application/xml"); + WELL_KNOWN_MIME_TYPES.put(0x28, "text/xml"); + WELL_KNOWN_MIME_TYPES.put(0x29, "application/vnd.wap.wbxml"); + WELL_KNOWN_MIME_TYPES.put(0x2A, "application/x-x968-cross-cert"); + WELL_KNOWN_MIME_TYPES.put(0x2B, "application/x-x968-ca-cert"); + WELL_KNOWN_MIME_TYPES.put(0x2C, "application/x-x968-user-cert"); + WELL_KNOWN_MIME_TYPES.put(0x2D, "text/vnd.wap.si"); + WELL_KNOWN_MIME_TYPES.put(0x2E, "application/vnd.wap.sic"); + WELL_KNOWN_MIME_TYPES.put(0x2F, "text/vnd.wap.sl"); + WELL_KNOWN_MIME_TYPES.put(0x30, "application/vnd.wap.slc"); + WELL_KNOWN_MIME_TYPES.put(0x31, "text/vnd.wap.co"); + WELL_KNOWN_MIME_TYPES.put(0x32, "application/vnd.wap.coc"); + WELL_KNOWN_MIME_TYPES.put(0x33, "application/vnd.wap.multipart.related"); + WELL_KNOWN_MIME_TYPES.put(0x34, "application/vnd.wap.sia"); + WELL_KNOWN_MIME_TYPES.put(0x35, "text/vnd.wap.connectivity-xml"); + WELL_KNOWN_MIME_TYPES.put(0x36, "application/vnd.wap.connectivity-wbxml"); + WELL_KNOWN_MIME_TYPES.put(0x37, "application/pkcs7-mime"); + WELL_KNOWN_MIME_TYPES.put(0x38, "application/vnd.wap.hashed-certificate"); + WELL_KNOWN_MIME_TYPES.put(0x39, "application/vnd.wap.signed-certificate"); + WELL_KNOWN_MIME_TYPES.put(0x3A, "application/vnd.wap.cert-response"); + WELL_KNOWN_MIME_TYPES.put(0x3B, "application/xhtml+xml"); + WELL_KNOWN_MIME_TYPES.put(0x3C, "application/wml+xml"); + WELL_KNOWN_MIME_TYPES.put(0x3D, "text/css"); + WELL_KNOWN_MIME_TYPES.put(0x3E, "application/vnd.wap.mms-message"); + WELL_KNOWN_MIME_TYPES.put(0x3F, "application/vnd.wap.rollover-certificate"); + WELL_KNOWN_MIME_TYPES.put(0x40, "application/vnd.wap.locc+wbxml"); + WELL_KNOWN_MIME_TYPES.put(0x41, "application/vnd.wap.loc+xml"); + WELL_KNOWN_MIME_TYPES.put(0x42, "application/vnd.syncml.dm+wbxml"); + WELL_KNOWN_MIME_TYPES.put(0x43, "application/vnd.syncml.dm+xml"); + WELL_KNOWN_MIME_TYPES.put(0x44, "application/vnd.syncml.notification"); + WELL_KNOWN_MIME_TYPES.put(0x45, "application/vnd.wap.xhtml+xml"); + WELL_KNOWN_MIME_TYPES.put(0x46, "application/vnd.wv.csp.cir"); + WELL_KNOWN_MIME_TYPES.put(0x47, "application/vnd.oma.dd+xml"); + WELL_KNOWN_MIME_TYPES.put(0x48, "application/vnd.oma.drm.message"); + WELL_KNOWN_MIME_TYPES.put(0x49, "application/vnd.oma.drm.content"); + WELL_KNOWN_MIME_TYPES.put(0x4A, "application/vnd.oma.drm.rights+xml"); + WELL_KNOWN_MIME_TYPES.put(0x4B, "application/vnd.oma.drm.rights+wbxml"); + WELL_KNOWN_MIME_TYPES.put(0x4C, "application/vnd.wv.csp+xml"); + WELL_KNOWN_MIME_TYPES.put(0x4D, "application/vnd.wv.csp+wbxml"); + WELL_KNOWN_MIME_TYPES.put(0x4E, "application/vnd.syncml.ds.notification"); + WELL_KNOWN_MIME_TYPES.put(0x4F, "audio/*"); + WELL_KNOWN_MIME_TYPES.put(0x50, "video/*"); + WELL_KNOWN_MIME_TYPES.put(0x51, "application/vnd.oma.dd2+xml"); + WELL_KNOWN_MIME_TYPES.put(0x52, "application/mikey"); + WELL_KNOWN_MIME_TYPES.put(0x53, "application/vnd.oma.dcd"); + WELL_KNOWN_MIME_TYPES.put(0x54, "application/vnd.oma.dcdc"); + + WELL_KNOWN_MIME_TYPES.put(0x0201, "application/vnd.uplanet.cacheop-wbxml"); + WELL_KNOWN_MIME_TYPES.put(0x0202, "application/vnd.uplanet.signal"); + WELL_KNOWN_MIME_TYPES.put(0x0203, "application/vnd.uplanet.alert-wbxml"); + WELL_KNOWN_MIME_TYPES.put(0x0204, "application/vnd.uplanet.list-wbxml"); + WELL_KNOWN_MIME_TYPES.put(0x0205, "application/vnd.uplanet.listcmd-wbxml"); + WELL_KNOWN_MIME_TYPES.put(0x0206, "application/vnd.uplanet.channel-wbxml"); + WELL_KNOWN_MIME_TYPES.put(0x0207, "application/vnd.uplanet.provisioning-status-uri"); + WELL_KNOWN_MIME_TYPES.put(0x0208, "x-wap.multipart/vnd.uplanet.header-set"); + WELL_KNOWN_MIME_TYPES.put(0x0209, "application/vnd.uplanet.bearer-choice-wbxml"); + WELL_KNOWN_MIME_TYPES.put(0x020A, "application/vnd.phonecom.mmc-wbxml"); + WELL_KNOWN_MIME_TYPES.put(0x020B, "application/vnd.nokia.syncset+wbxml"); + WELL_KNOWN_MIME_TYPES.put(0x020C, "image/x-up-wpng"); + WELL_KNOWN_MIME_TYPES.put(0x0300, "application/iota.mmc-wbxml"); + WELL_KNOWN_MIME_TYPES.put(0x0301, "application/iota.mmc-xml"); + WELL_KNOWN_MIME_TYPES.put(0x0302, "application/vnd.syncml+xml"); + WELL_KNOWN_MIME_TYPES.put(0x0303, "application/vnd.syncml+wbxml"); + WELL_KNOWN_MIME_TYPES.put(0x0304, "text/vnd.wap.emn+xml"); + WELL_KNOWN_MIME_TYPES.put(0x0305, "text/calendar"); + WELL_KNOWN_MIME_TYPES.put(0x0306, "application/vnd.omads-email+xml"); + WELL_KNOWN_MIME_TYPES.put(0x0307, "application/vnd.omads-file+xml"); + WELL_KNOWN_MIME_TYPES.put(0x0308, "application/vnd.omads-folder+xml"); + WELL_KNOWN_MIME_TYPES.put(0x0309, "text/directory;profile=vCard"); + WELL_KNOWN_MIME_TYPES.put(0x030A, "application/vnd.wap.emn+wbxml"); + WELL_KNOWN_MIME_TYPES.put(0x030B, "application/vnd.nokia.ipdc-purchase-response"); + WELL_KNOWN_MIME_TYPES.put(0x030C, "application/vnd.motorola.screen3+xml"); + WELL_KNOWN_MIME_TYPES.put(0x030D, "application/vnd.motorola.screen3+gzip"); + WELL_KNOWN_MIME_TYPES.put(0x030E, "application/vnd.cmcc.setting+wbxml"); + WELL_KNOWN_MIME_TYPES.put(0x030F, "application/vnd.cmcc.bombing+wbxml"); + WELL_KNOWN_MIME_TYPES.put(0x0310, "application/vnd.docomo.pf"); + WELL_KNOWN_MIME_TYPES.put(0x0311, "application/vnd.docomo.ub"); + WELL_KNOWN_MIME_TYPES.put(0x0312, "application/vnd.omaloc-supl-init"); + WELL_KNOWN_MIME_TYPES.put(0x0313, "application/vnd.oma.group-usage-list+xml"); + WELL_KNOWN_MIME_TYPES.put(0x0314, "application/oma-directory+xml"); + WELL_KNOWN_MIME_TYPES.put(0x0315, "application/vnd.docomo.pf2"); + WELL_KNOWN_MIME_TYPES.put(0x0316, "application/vnd.oma.drm.roap-trigger+wbxml"); + WELL_KNOWN_MIME_TYPES.put(0x0317, "application/vnd.sbm.mid2"); + WELL_KNOWN_MIME_TYPES.put(0x0318, "application/vnd.wmf.bootstrap"); + WELL_KNOWN_MIME_TYPES.put(0x0319, "application/vnc.cmcc.dcd+xml"); + WELL_KNOWN_MIME_TYPES.put(0x031A, "application/vnd.sbm.cid"); + WELL_KNOWN_MIME_TYPES.put(0x031B, "application/vnd.oma.bcast.provisioningtrigger"); + + WELL_KNOWN_PARAMETERS.put(0x00, "Q"); + WELL_KNOWN_PARAMETERS.put(0x01, "Charset"); + WELL_KNOWN_PARAMETERS.put(0x02, "Level"); + WELL_KNOWN_PARAMETERS.put(0x03, "Type"); + WELL_KNOWN_PARAMETERS.put(0x07, "Differences"); + WELL_KNOWN_PARAMETERS.put(0x08, "Padding"); + WELL_KNOWN_PARAMETERS.put(0x09, "Type"); + WELL_KNOWN_PARAMETERS.put(0x0E, "Max-Age"); + WELL_KNOWN_PARAMETERS.put(0x10, "Secure"); + WELL_KNOWN_PARAMETERS.put(0x11, "SEC"); + WELL_KNOWN_PARAMETERS.put(0x12, "MAC"); + WELL_KNOWN_PARAMETERS.put(0x13, "Creation-date"); + WELL_KNOWN_PARAMETERS.put(0x14, "Modification-date"); + WELL_KNOWN_PARAMETERS.put(0x15, "Read-date"); + WELL_KNOWN_PARAMETERS.put(0x16, "Size"); + WELL_KNOWN_PARAMETERS.put(0x17, "Name"); + WELL_KNOWN_PARAMETERS.put(0x18, "Filename"); + WELL_KNOWN_PARAMETERS.put(0x19, "Start"); + WELL_KNOWN_PARAMETERS.put(0x1A, "Start-info"); + WELL_KNOWN_PARAMETERS.put(0x1B, "Comment"); + WELL_KNOWN_PARAMETERS.put(0x1C, "Domain"); + WELL_KNOWN_PARAMETERS.put(0x1D, "Path"); + } + public static final String CONTENT_TYPE_B_PUSH_CO = "application/vnd.wap.coc"; + public static final String CONTENT_TYPE_B_MMS = "application/vnd.wap.mms-message"; byte[] wspData; int dataLength; long unsigned32bit; String stringValue; + HashMap<String, String> contentParameters; + public WspTypeDecoder(byte[] pdu) { wspData = pdu; } @@ -71,17 +211,17 @@ public class WspTypeDecoder { * @param startIndex The starting position of the "Text-string" in this pdu * * @return false when error(not a Text-string) occur - * return value can be retrieved by getValueString() method - * length of data in pdu can be retrieved by getValue32() method + * return value can be retrieved by getValueString() method length of data in pdu can be + * retrieved by getDecodedDataLength() method */ public boolean decodeTextString(int startIndex) { int index = startIndex; while (wspData[index] != 0) { index++; } - dataLength = index - startIndex + 1; + dataLength = index - startIndex + 1; if (wspData[startIndex] == 127) { - stringValue = new String(wspData, startIndex+1, dataLength - 2); + stringValue = new String(wspData, startIndex + 1, dataLength - 2); } else { stringValue = new String(wspData, startIndex, dataLength - 1); } @@ -89,13 +229,33 @@ public class WspTypeDecoder { } /** + * Decode the "Token-text" type for WSP pdu + * + * @param startIndex The starting position of the "Token-text" in this pdu + * + * @return always true + * return value can be retrieved by getValueString() method + * length of data in pdu can be retrieved by getDecodedDataLength() method + */ + public boolean decodeTokenText(int startIndex) { + int index = startIndex; + while (wspData[index] != 0) { + index++; + } + dataLength = index - startIndex + 1; + stringValue = new String(wspData, startIndex, dataLength - 1); + + return true; + } + + /** * Decode the "Short-integer" type for WSP pdu * * @param startIndex The starting position of the "Short-integer" in this pdu * * @return false when error(not a Short-integer) occur * return value can be retrieved by getValue32() method - * length of data in pdu can be retrieved by getValue32() method + * length of data in pdu can be retrieved by getDecodedDataLength() method */ public boolean decodeShortInteger(int startIndex) { if ((wspData[startIndex] & 0x80) == 0) { @@ -113,7 +273,7 @@ public class WspTypeDecoder { * * @return false when error(not a Long-integer) occur * return value can be retrieved by getValue32() method - * length of data in pdu can be retrieved by getValue32() method + * length of data in pdu can be retrieved by getDecodedDataLength() method */ public boolean decodeLongInteger(int startIndex) { int lengthMultiOctet = wspData[startIndex] & 0xff; @@ -122,10 +282,10 @@ public class WspTypeDecoder { return false; } unsigned32bit = 0; - for (int i=1; i<=lengthMultiOctet; i++) { - unsigned32bit = (unsigned32bit << 8) | (wspData[startIndex+i] & 0xff); + for (int i = 1; i <= lengthMultiOctet; i++) { + unsigned32bit = (unsigned32bit << 8) | (wspData[startIndex + i] & 0xff); } - dataLength = 1+lengthMultiOctet; + dataLength = 1 + lengthMultiOctet; return true; } @@ -136,7 +296,7 @@ public class WspTypeDecoder { * * @return false when error(not a Integer-Value) occur * return value can be retrieved by getValue32() method - * length of data in pdu can be retrieved by getValue32() method + * length of data in pdu can be retrieved by getDecodedDataLength() method */ public boolean decodeIntegerValue(int startIndex) { if (decodeShortInteger(startIndex) == true) { @@ -152,10 +312,10 @@ public class WspTypeDecoder { * * @return false when error(not a Uintvar-integer) occur * return value can be retrieved by getValue32() method - * length of data in pdu can be retrieved by getValue32() method + * length of data in pdu can be retrieved by getDecodedDataLength() method */ public boolean decodeUintvarInteger(int startIndex) { - int index = startIndex; + int index = startIndex; unsigned32bit = 0; while ((wspData[index] & 0x80) != 0) { @@ -177,7 +337,7 @@ public class WspTypeDecoder { * * @return false when error(not a Value-length) occur * return value can be retrieved by getValue32() method - * length of data in pdu can be retrieved by getValue32() method + * length of data in pdu can be retrieved by getDecodedDataLength() method */ public boolean decodeValueLength(int startIndex) { if ((wspData[startIndex] & 0xff) > WAP_PDU_LENGTH_QUOTE) { @@ -187,22 +347,22 @@ public class WspTypeDecoder { unsigned32bit = wspData[startIndex]; dataLength = 1; } else { - decodeUintvarInteger(startIndex+1); - dataLength ++; + decodeUintvarInteger(startIndex + 1); + dataLength++; } return true; } /** - * Decode the "Extension-media" type for WSP PDU. - * - * @param startIndex The starting position of the "Extension-media" in this PDU. - * - * @return false on error, such as if there is no Extension-media at startIndex. - * Side-effects: updates stringValue (available with getValueString()), which will be - * null on error. The length of the data in the PDU is available with getValue32(), 0 - * on error. - */ + * Decode the "Extension-media" type for WSP PDU. + * + * @param startIndex The starting position of the "Extension-media" in this PDU. + * + * @return false on error, such as if there is no Extension-media at startIndex. + * Side-effects: updates stringValue (available with + * getValueString()), which will be null on error. The length of the + * data in the PDU is available with getValue32(), 0 on error. + */ public boolean decodeExtensionMedia(int startIndex) { int index = startIndex; dataLength = 0; @@ -214,7 +374,7 @@ public class WspTypeDecoder { index++; } - dataLength = index - startIndex + 1; + dataLength = index - startIndex + 1; stringValue = new String(wspData, startIndex, dataLength - 1); return rtrn; @@ -227,7 +387,7 @@ public class WspTypeDecoder { * * @return false when error(not a Constrained-encoding) occur * return value can be retrieved first by getValueString() and second by getValue32() method - * length of data in pdu can be retrieved by getValue32() method + * length of data in pdu can be retrieved by getDecodedDataLength() method */ public boolean decodeConstrainedEncoding(int startIndex) { if (decodeShortInteger(startIndex) == true) { @@ -242,29 +402,160 @@ public class WspTypeDecoder { * * @param startIndex The starting position of the "Content-type" in this pdu * - * @return false when error(not a Content-type) occur - * return value can be retrieved first by getValueString() and second by getValue32() - * method length of data in pdu can be retrieved by getValue32() method + * @return false when error(not a Content-type) occurs + * If a content type exists in the headers (either as inline string, or as well-known + * value), getValueString() will return it. If a 'well known value' is encountered that + * cannot be mapped to a string mime type, getValueString() will return null, and + * getValue32() will return the unknown content type value. + * length of data in pdu can be retrieved by getDecodedDataLength() method + * Any content type parameters will be accessible via getContentParameters() */ public boolean decodeContentType(int startIndex) { int mediaPrefixLength; - long mediaFieldLength; - - if (decodeValueLength(startIndex) == false) { - return decodeConstrainedEncoding(startIndex); + contentParameters = new HashMap<String, String>(); + + try { + if (decodeValueLength(startIndex) == false) { + boolean found = decodeConstrainedEncoding(startIndex); + if (found) { + expandWellKnownMimeType(); + } + return found; + } + int headersLength = (int) unsigned32bit; + mediaPrefixLength = getDecodedDataLength(); + if (decodeIntegerValue(startIndex + mediaPrefixLength) == true) { + dataLength += mediaPrefixLength; + int readLength = dataLength; + stringValue = null; + expandWellKnownMimeType(); + long wellKnownValue = unsigned32bit; + String mimeType = stringValue; + if (readContentParameters(startIndex + dataLength, + (headersLength - (dataLength - mediaPrefixLength)), 0)) { + dataLength += readLength; + unsigned32bit = wellKnownValue; + stringValue = mimeType; + return true; + } + return false; + } + if (decodeExtensionMedia(startIndex + mediaPrefixLength) == true) { + dataLength += mediaPrefixLength; + int readLength = dataLength; + expandWellKnownMimeType(); + long wellKnownValue = unsigned32bit; + String mimeType = stringValue; + if (readContentParameters(startIndex + dataLength, + (headersLength - (dataLength - mediaPrefixLength)), 0)) { + dataLength += readLength; + unsigned32bit = wellKnownValue; + stringValue = mimeType; + return true; + } + } + } catch (ArrayIndexOutOfBoundsException e) { + //something doesn't add up + return false; } - mediaPrefixLength = getDecodedDataLength(); - mediaFieldLength = getValue32(); - if (decodeIntegerValue(startIndex + mediaPrefixLength) == true) { - dataLength += mediaPrefixLength; - stringValue = null; + return false; + } + + private boolean readContentParameters(int startIndex, int leftToRead, int accumulator) { + + int totalRead = 0; + + if (leftToRead > 0) { + byte nextByte = wspData[startIndex]; + String value = null; + String param = null; + if ((nextByte & 0x80) == 0x00 && nextByte > 31) { // untyped + decodeTokenText(startIndex); + param = stringValue; + totalRead += dataLength; + } else { // typed + if (decodeIntegerValue(startIndex)) { + totalRead += dataLength; + int wellKnownParameterValue = (int) unsigned32bit; + param = WELL_KNOWN_PARAMETERS.get(wellKnownParameterValue); + if (param == null) { + param = "unassigned/0x" + Long.toHexString(wellKnownParameterValue); + } + // special case for the "Q" parameter, value is a uintvar + if (wellKnownParameterValue == Q_VALUE) { + if (decodeUintvarInteger(startIndex + totalRead)) { + totalRead += dataLength; + value = String.valueOf(unsigned32bit); + contentParameters.put(param, value); + return readContentParameters(startIndex + totalRead, leftToRead + - totalRead, accumulator + totalRead); + } else { + return false; + } + } + } else { + return false; + } + } + + if (decodeNoValue(startIndex + totalRead)) { + totalRead += dataLength; + value = null; + } else if (decodeIntegerValue(startIndex + totalRead)) { + totalRead += dataLength; + int intValue = (int) unsigned32bit; + if (intValue == 0) { + value = ""; + } else { + value = String.valueOf(intValue); + } + } else { + decodeTokenText(startIndex + totalRead); + totalRead += dataLength; + value = stringValue; + if (value.startsWith("\"")) { + // quoted string, so remove the quote + value = value.substring(1); + } + } + contentParameters.put(param, value); + return readContentParameters(startIndex + totalRead, leftToRead - totalRead, + accumulator + totalRead); + + } else { + dataLength = accumulator; return true; } - if (decodeExtensionMedia(startIndex + mediaPrefixLength) == true) { - dataLength += mediaPrefixLength; + } + + /** + * Check if the next byte is No-Value + * + * @param startIndex The starting position of the "Content length" in this pdu + * + * @return true if and only if the next byte is 0x00 + */ + private boolean decodeNoValue(int startIndex) { + if (wspData[startIndex] == 0) { + dataLength = 1; return true; + } else { + return false; + } + } + + /** + * Populate stringValue with the mime type corresponding to the value in unsigned32bit + * + * Sets unsigned32bit to -1 if stringValue is already populated + */ + private void expandWellKnownMimeType() { + if (stringValue == null) { + int binaryContentType = (int) unsigned32bit; + stringValue = WELL_KNOWN_MIME_TYPES.get(binaryContentType); + } else { + unsigned32bit = -1; } - return false; } /** @@ -274,7 +565,7 @@ public class WspTypeDecoder { * * @return false when error(not a Content length) occur * return value can be retrieved by getValue32() method - * length of data in pdu can be retrieved by getValue32() method + * length of data in pdu can be retrieved by getDecodedDataLength() method */ public boolean decodeContentLength(int startIndex) { return decodeIntegerValue(startIndex); @@ -287,7 +578,7 @@ public class WspTypeDecoder { * * @return false when error(not a Content location) occur * return value can be retrieved by getValueString() method - * length of data in pdu can be retrieved by getValue32() method + * length of data in pdu can be retrieved by getDecodedDataLength() method */ public boolean decodeContentLocation(int startIndex) { return decodeTextString(startIndex); @@ -300,7 +591,8 @@ public class WspTypeDecoder { * * @return false when error(not a X-Wap-Application-Id) occur * return value can be retrieved first by getValueString() and second by getValue32() - * method length of data in pdu can be retrieved by getValue32() method + * method + * length of data in pdu can be retrieved by getDecodedDataLength() method */ public boolean decodeXWapApplicationId(int startIndex) { if (decodeIntegerValue(startIndex) == true) { @@ -317,7 +609,7 @@ public class WspTypeDecoder { * * @return false when error(not a X-Wap-Content-URI) occur * return value can be retrieved by getValueString() method - * length of data in pdu can be retrieved by getValue32() method + * length of data in pdu can be retrieved by getDecodedDataLength() method */ public boolean decodeXWapContentURI(int startIndex) { return decodeTextString(startIndex); @@ -330,7 +622,7 @@ public class WspTypeDecoder { * * @return false when error(not a X-Wap-Initiator-URI) occur * return value can be retrieved by getValueString() method - * length of data in pdu can be retrieved by getValue32() method + * length of data in pdu can be retrieved by getDecodedDataLength() method */ public boolean decodeXWapInitiatorURI(int startIndex) { return decodeTextString(startIndex); @@ -356,4 +648,18 @@ public class WspTypeDecoder { public String getValueString() { return stringValue; } + + /** + * Any parameters encountered as part of a decodeContentType() invocation. + * + * @return a map of content parameters keyed by their names, or null if + * decodeContentType() has not been called If any unassigned + * well-known parameters are encountered, the key of the map will be + * 'unassigned/0x...', where '...' is the hex value of the + * unassigned parameter. If a parameter has No-Value the value will be null. + * + */ + public HashMap<String, String> getContentParameters() { + return contentParameters; + } } diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/AppInterface.java b/telephony/java/com/android/internal/telephony/cat/AppInterface.java index 58f1f97..2eb6ccb 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/AppInterface.java +++ b/telephony/java/com/android/internal/telephony/cat/AppInterface.java @@ -14,29 +14,29 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; /** - * Interface for communication between STK App and STK Telephony + * Interface for communication between STK App and CAT Telephony * * {@hide} */ public interface AppInterface { /* - * Intent's actions which are broadcasted by the Telephony once a new STK + * Intent's actions which are broadcasted by the Telephony once a new CAT * proactive command, session end arrive. */ - public static final String STK_CMD_ACTION = + public static final String CAT_CMD_ACTION = "android.intent.action.stk.command"; - public static final String STK_SESSION_END_ACTION = + public static final String CAT_SESSION_END_ACTION = "android.intent.action.stk.session_end"; /* * Callback function from app to telephony to pass a result code and user's - * input back to the SIM. + * input back to the ICC. */ - void onCmdResponse(StkResponseMessage resMsg); + void onCmdResponse(CatResponseMessage resMsg); /* * Enumeration for representing "Type of Command" of proactive commands. @@ -58,7 +58,8 @@ public interface AppInterface { SET_UP_EVENT_LIST(0x05), SET_UP_IDLE_MODE_TEXT(0x28), SET_UP_MENU(0x25), - SET_UP_CALL(0x10); + SET_UP_CALL(0x10), + PROVIDE_LOCAL_INFORMATION(0x26); private int mValue; diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/BerTlv.java b/telephony/java/com/android/internal/telephony/cat/BerTlv.java index 19d3279..774bfa3 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/BerTlv.java +++ b/telephony/java/com/android/internal/telephony/cat/BerTlv.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; import java.util.List; diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/StkCmdMessage.java b/telephony/java/com/android/internal/telephony/cat/CatCmdMessage.java index 5425a43..5155bb2 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/StkCmdMessage.java +++ b/telephony/java/com/android/internal/telephony/cat/CatCmdMessage.java @@ -14,17 +14,17 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; import android.os.Parcel; import android.os.Parcelable; /** - * Class used to pass STK messages from telephony to application. Application + * Class used to pass CAT messages from telephony to application. Application * should call getXXX() to get commands's specific values. * */ -public class StkCmdMessage implements Parcelable { +public class CatCmdMessage implements Parcelable { // members CommandDetails mCmdDet; private TextMessage mTextMsg; @@ -50,7 +50,7 @@ public class StkCmdMessage implements Parcelable { public TextMessage callMsg; } - StkCmdMessage(CommandParams cmdParams) { + CatCmdMessage(CommandParams cmdParams) { mCmdDet = cmdParams.cmdDet; switch(getCmdType()) { case SET_UP_MENU: @@ -88,7 +88,7 @@ public class StkCmdMessage implements Parcelable { } } - public StkCmdMessage(Parcel in) { + public CatCmdMessage(Parcel in) { mCmdDet = in.readParcelable(null); mTextMsg = in.readParcelable(null); mMenu = in.readParcelable(null); @@ -130,13 +130,13 @@ public class StkCmdMessage implements Parcelable { } } - public static final Parcelable.Creator<StkCmdMessage> CREATOR = new Parcelable.Creator<StkCmdMessage>() { - public StkCmdMessage createFromParcel(Parcel in) { - return new StkCmdMessage(in); + public static final Parcelable.Creator<CatCmdMessage> CREATOR = new Parcelable.Creator<CatCmdMessage>() { + public CatCmdMessage createFromParcel(Parcel in) { + return new CatCmdMessage(in); } - public StkCmdMessage[] newArray(int size) { - return new StkCmdMessage[size]; + public CatCmdMessage[] newArray(int size) { + return new CatCmdMessage[size]; } }; diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/StkException.java b/telephony/java/com/android/internal/telephony/cat/CatException.java index 86de366..1bf1369 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/StkException.java +++ b/telephony/java/com/android/internal/telephony/cat/CatException.java @@ -14,18 +14,18 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; import android.util.AndroidException; /** - * Base class for all the exceptions in STK service. + * Base class for all the exceptions in CAT service. * * {@hide} */ -class StkException extends AndroidException { - public StkException() { +class CatException extends AndroidException { + public CatException() { super(); } } diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/StkLog.java b/telephony/java/com/android/internal/telephony/cat/CatLog.java index bd6bc8f..e19ff43 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/StkLog.java +++ b/telephony/java/com/android/internal/telephony/cat/CatLog.java @@ -14,11 +14,11 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; import android.util.Log; -public abstract class StkLog { +public abstract class CatLog { static final boolean DEBUG = true; public static void d(Object caller, String msg) { @@ -27,7 +27,7 @@ public abstract class StkLog { } String className = caller.getClass().getName(); - Log.d("STK", className.substring(className.lastIndexOf('.') + 1) + ": " + Log.d("CAT", className.substring(className.lastIndexOf('.') + 1) + ": " + msg); } @@ -36,6 +36,6 @@ public abstract class StkLog { return; } - Log.d("STK", caller + ": " + msg); + Log.d("CAT", caller + ": " + msg); } } diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/StkResponseMessage.java b/telephony/java/com/android/internal/telephony/cat/CatResponseMessage.java index 04a52e6..cfcac36 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/StkResponseMessage.java +++ b/telephony/java/com/android/internal/telephony/cat/CatResponseMessage.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; -public class StkResponseMessage { +public class CatResponseMessage { CommandDetails cmdDet = null; ResultCode resCode = ResultCode.OK; int usersMenuSelection = 0; @@ -24,7 +24,7 @@ public class StkResponseMessage { boolean usersYesNoSelection = false; boolean usersConfirm = false; - public StkResponseMessage(StkCmdMessage cmdMsg) { + public CatResponseMessage(CatCmdMessage cmdMsg) { this.cmdDet = cmdMsg.mCmdDet; } diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/StkService.java b/telephony/java/com/android/internal/telephony/cat/CatService.java index 29ed95c..1e23e34 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/StkService.java +++ b/telephony/java/com/android/internal/telephony/cat/CatService.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; import android.content.Context; import android.content.Intent; @@ -22,12 +22,13 @@ import android.os.AsyncResult; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; +import android.os.SystemProperties; import com.android.internal.telephony.IccUtils; import com.android.internal.telephony.CommandsInterface; -import com.android.internal.telephony.gsm.SimCard; -import com.android.internal.telephony.gsm.SIMFileHandler; -import com.android.internal.telephony.gsm.SIMRecords; +import com.android.internal.telephony.IccCard; +import com.android.internal.telephony.IccFileHandler; +import com.android.internal.telephony.IccRecords; import android.util.Config; @@ -111,17 +112,17 @@ class RilMessage { * * {@hide} */ -public class StkService extends Handler implements AppInterface { +public class CatService extends Handler implements AppInterface { // Class members - private static SIMRecords mSimRecords; + private static IccRecords mIccRecords; // Service members. - private static StkService sInstance; + private static CatService sInstance; private CommandsInterface mCmdIf; private Context mContext; - private StkCmdMessage mCurrntCmd = null; - private StkCmdMessage mMenuCmd = null; + private CatCmdMessage mCurrntCmd = null; + private CatCmdMessage mMenuCmd = null; private RilMessageDecoder mMsgDecoder = null; @@ -136,7 +137,7 @@ public class StkService extends Handler implements AppInterface { static final int MSG_ID_RIL_MSG_DECODED = 10; // Events to signal SIM presence or absent in the device. - private static final int MSG_ID_SIM_LOADED = 20; + private static final int MSG_ID_ICC_RECORDS_LOADED = 20; private static final int DEV_ID_KEYPAD = 0x01; private static final int DEV_ID_DISPLAY = 0x02; @@ -146,10 +147,10 @@ public class StkService extends Handler implements AppInterface { private static final int DEV_ID_NETWORK = 0x83; /* Intentionally private for singleton */ - private StkService(CommandsInterface ci, SIMRecords sr, Context context, - SIMFileHandler fh, SimCard sc) { - if (ci == null || sr == null || context == null || fh == null - || sc == null) { + private CatService(CommandsInterface ci, IccRecords ir, Context context, + IccFileHandler fh, IccCard ic) { + if (ci == null || ir == null || context == null || fh == null + || ic == null) { throw new NullPointerException( "Service: Input parameters must not be null"); } @@ -160,33 +161,33 @@ public class StkService extends Handler implements AppInterface { mMsgDecoder = RilMessageDecoder.getInstance(this, fh); // Register ril events handling. - mCmdIf.setOnStkSessionEnd(this, MSG_ID_SESSION_END, null); - mCmdIf.setOnStkProactiveCmd(this, MSG_ID_PROACTIVE_COMMAND, null); - mCmdIf.setOnStkEvent(this, MSG_ID_EVENT_NOTIFY, null); - mCmdIf.setOnStkCallSetUp(this, MSG_ID_CALL_SETUP, null); + mCmdIf.setOnCatSessionEnd(this, MSG_ID_SESSION_END, null); + mCmdIf.setOnCatProactiveCmd(this, MSG_ID_PROACTIVE_COMMAND, null); + mCmdIf.setOnCatEvent(this, MSG_ID_EVENT_NOTIFY, null); + mCmdIf.setOnCatCallSetUp(this, MSG_ID_CALL_SETUP, null); //mCmdIf.setOnSimRefresh(this, MSG_ID_REFRESH, null); - mSimRecords = sr; + mIccRecords = ir; // Register for SIM ready event. - mSimRecords.registerForRecordsLoaded(this, MSG_ID_SIM_LOADED, null); + mIccRecords.registerForRecordsLoaded(this, MSG_ID_ICC_RECORDS_LOADED, null); mCmdIf.reportStkServiceIsRunning(null); - StkLog.d(this, "StkService: is running"); + CatLog.d(this, "Is running"); } public void dispose() { - mSimRecords.unregisterForRecordsLoaded(this); - mCmdIf.unSetOnStkSessionEnd(this); - mCmdIf.unSetOnStkProactiveCmd(this); - mCmdIf.unSetOnStkEvent(this); - mCmdIf.unSetOnStkCallSetUp(this); + mIccRecords.unregisterForRecordsLoaded(this); + mCmdIf.unSetOnCatSessionEnd(this); + mCmdIf.unSetOnCatProactiveCmd(this); + mCmdIf.unSetOnCatEvent(this); + mCmdIf.unSetOnCatCallSetUp(this); this.removeCallbacksAndMessages(null); } protected void finalize() { - StkLog.d(this, "Service finalized"); + CatLog.d(this, "Service finalized"); } private void handleRilMsg(RilMessage rilMsg) { @@ -241,55 +242,53 @@ public class StkService extends Handler implements AppInterface { * */ private void handleProactiveCommand(CommandParams cmdParams) { - StkLog.d(this, cmdParams.getCommandType().name()); + CatLog.d(this, cmdParams.getCommandType().name()); - StkCmdMessage cmdMsg = new StkCmdMessage(cmdParams); + CatCmdMessage cmdMsg = new CatCmdMessage(cmdParams); switch (cmdParams.getCommandType()) { - case SET_UP_MENU: - if (removeMenu(cmdMsg.getMenu())) { - mMenuCmd = null; - } else { - mMenuCmd = cmdMsg; - } - sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, - null); - break; - case DISPLAY_TEXT: - // when application is not required to respond, send an immediate - // response. - if (!cmdMsg.geTextMessage().responseNeeded) { - sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, - 0, null); - } - break; - case REFRESH: - // ME side only handles refresh commands which meant to remove IDLE - // MODE TEXT. - cmdParams.cmdDet.typeOfCommand = CommandType.SET_UP_IDLE_MODE_TEXT - .value(); - break; - case SET_UP_IDLE_MODE_TEXT: - sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, - 0, null); - break; - case LAUNCH_BROWSER: - case SELECT_ITEM: - case GET_INPUT: - case GET_INKEY: - case SEND_DTMF: - case SEND_SMS: - case SEND_SS: - case SEND_USSD: - case PLAY_TONE: - case SET_UP_CALL: - // nothing to do on telephony! - break; - default: - StkLog.d(this, "Unsupported command"); - return; + case SET_UP_MENU: + if (removeMenu(cmdMsg.getMenu())) { + mMenuCmd = null; + } else { + mMenuCmd = cmdMsg; + } + sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null); + break; + case DISPLAY_TEXT: + // when application is not required to respond, send an immediate response. + if (!cmdMsg.geTextMessage().responseNeeded) { + sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null); + } + break; + case REFRESH: + // ME side only handles refresh commands which meant to remove IDLE + // MODE TEXT. + cmdParams.cmdDet.typeOfCommand = CommandType.SET_UP_IDLE_MODE_TEXT.value(); + break; + case SET_UP_IDLE_MODE_TEXT: + sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null); + break; + case PROVIDE_LOCAL_INFORMATION: + sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null); + return; + case LAUNCH_BROWSER: + case SELECT_ITEM: + case GET_INPUT: + case GET_INKEY: + case SEND_DTMF: + case SEND_SMS: + case SEND_SS: + case SEND_USSD: + case PLAY_TONE: + case SET_UP_CALL: + // nothing to do on telephony! + break; + default: + CatLog.d(this, "Unsupported command"); + return; } mCurrntCmd = cmdMsg; - Intent intent = new Intent(AppInterface.STK_CMD_ACTION); + Intent intent = new Intent(AppInterface.CAT_CMD_ACTION); intent.putExtra("STK CMD", cmdMsg); mContext.sendBroadcast(intent); } @@ -299,10 +298,10 @@ public class StkService extends Handler implements AppInterface { * */ private void handleSessionEnd() { - StkLog.d(this, "SESSION END"); + CatLog.d(this, "SESSION END"); mCurrntCmd = mMenuCmd; - Intent intent = new Intent(AppInterface.STK_SESSION_END_ACTION); + Intent intent = new Intent(AppInterface.CAT_SESSION_END_ACTION); mContext.sendBroadcast(intent); } @@ -315,6 +314,11 @@ public class StkService extends Handler implements AppInterface { } ByteArrayOutputStream buf = new ByteArrayOutputStream(); + Input cmdInput = null; + if (mCurrntCmd != null) { + cmdInput = mCurrntCmd.geInput(); + } + // command details int tag = ComprehensionTlvTag.COMMAND_DETAILS.value(); if (cmdDet.compRequired) { @@ -327,7 +331,13 @@ public class StkService extends Handler implements AppInterface { buf.write(cmdDet.commandQualifier); // device identities - tag = 0x80 | ComprehensionTlvTag.DEVICE_IDENTITIES.value(); + // According to TS102.223/TS31.111 section 6.8 Structure of + // TERMINAL RESPONSE, "For all SIMPLE-TLV objects with Min=N, + // the ME should set the CR(comprehension required) flag to + // comprehension not required.(CR=0)" + // Since DEVICE_IDENTITIES and DURATION TLVs have Min=N, + // the CR flag is not set. + tag = ComprehensionTlvTag.DEVICE_IDENTITIES.value(); buf.write(tag); buf.write(0x02); // length buf.write(DEV_ID_TERMINAL); // source device id @@ -348,17 +358,65 @@ public class StkService extends Handler implements AppInterface { // Fill optional data for each corresponding command if (resp != null) { resp.format(buf); + } else { + encodeOptionalTags(cmdDet, resultCode, cmdInput, buf); } byte[] rawData = buf.toByteArray(); String hexString = IccUtils.bytesToHexString(rawData); if (Config.LOGD) { - StkLog.d(this, "TERMINAL RESPONSE: " + hexString); + CatLog.d(this, "TERMINAL RESPONSE: " + hexString); } mCmdIf.sendTerminalResponse(hexString, null); } + private void encodeOptionalTags(CommandDetails cmdDet, + ResultCode resultCode, Input cmdInput, ByteArrayOutputStream buf) { + switch (AppInterface.CommandType.fromInt(cmdDet.typeOfCommand)) { + case GET_INKEY: + // ETSI TS 102 384,27.22.4.2.8.4.2. + // If it is a response for GET_INKEY command and the response timeout + // occured, then add DURATION TLV for variable timeout case. + if ((resultCode.value() == ResultCode.NO_RESPONSE_FROM_USER.value()) && + (cmdInput != null) && (cmdInput.duration != null)) { + getInKeyResponse(buf, cmdInput); + } + break; + case PROVIDE_LOCAL_INFORMATION: + if ((cmdDet.commandQualifier == CommandParamsFactory.LANGUAGE_SETTING) && + (resultCode.value() == ResultCode.OK.value())) { + getPliResponse(buf); + } + break; + default: + CatLog.d(this, "encodeOptionalTags() Unsupported Cmd:" + cmdDet.typeOfCommand); + break; + } + } + + private void getInKeyResponse(ByteArrayOutputStream buf, Input cmdInput) { + int tag = ComprehensionTlvTag.DURATION.value(); + + buf.write(tag); + buf.write(0x02); // length + buf.write(cmdInput.duration.timeUnit.SECOND.value()); // Time (Unit,Seconds) + buf.write(cmdInput.duration.timeInterval); // Time Duration + } + + private void getPliResponse(ByteArrayOutputStream buf) { + + // Locale Language Setting + String lang = SystemProperties.get("persist.sys.language"); + + if (lang != null) { + // tag + int tag = ComprehensionTlvTag.LANGUAGE.value(); + buf.write(tag); + ResponseData.writeLength(buf, lang.length()); + buf.write(lang.getBytes(), 0, lang.length()); + } + } private void sendMenuSelection(int menuId, boolean helpRequired) { @@ -446,35 +504,35 @@ public class StkService extends Handler implements AppInterface { } /** - * Used for instantiating/updating the Service from the GsmPhone constructor. + * Used for instantiating/updating the Service from the GsmPhone or CdmaPhone constructor. * * @param ci CommandsInterface object - * @param sr SIMRecords object + * @param ir IccRecords object * @param context phone app context - * @param fh SIM file handler - * @param sc GSM SIM card + * @param fh Icc file handler + * @param ic Icc card * @return The only Service object in the system */ - public static StkService getInstance(CommandsInterface ci, SIMRecords sr, - Context context, SIMFileHandler fh, SimCard sc) { + public static CatService getInstance(CommandsInterface ci, IccRecords ir, + Context context, IccFileHandler fh, IccCard ic) { if (sInstance == null) { - if (ci == null || sr == null || context == null || fh == null - || sc == null) { + if (ci == null || ir == null || context == null || fh == null + || ic == null) { return null; } - HandlerThread thread = new HandlerThread("Stk Telephony service"); + HandlerThread thread = new HandlerThread("Cat Telephony service"); thread.start(); - sInstance = new StkService(ci, sr, context, fh, sc); - StkLog.d(sInstance, "NEW sInstance"); - } else if ((sr != null) && (mSimRecords != sr)) { - StkLog.d(sInstance, "Reinitialize the Service with SIMRecords"); - mSimRecords = sr; + sInstance = new CatService(ci, ir, context, fh, ic); + CatLog.d(sInstance, "NEW sInstance"); + } else if ((ir != null) && (mIccRecords != ir)) { + CatLog.d(sInstance, "Reinitialize the Service with SIMRecords"); + mIccRecords = ir; // re-Register for SIM ready event. - mSimRecords.registerForRecordsLoaded(sInstance, MSG_ID_SIM_LOADED, null); - StkLog.d(sInstance, "sr changed reinitialize and return current sInstance"); + mIccRecords.registerForRecordsLoaded(sInstance, MSG_ID_ICC_RECORDS_LOADED, null); + CatLog.d(sInstance, "sr changed reinitialize and return current sInstance"); } else { - StkLog.d(sInstance, "Return current sInstance"); + CatLog.d(sInstance, "Return current sInstance"); } return sInstance; } @@ -496,7 +554,7 @@ public class StkService extends Handler implements AppInterface { case MSG_ID_PROACTIVE_COMMAND: case MSG_ID_EVENT_NOTIFY: case MSG_ID_REFRESH: - StkLog.d(this, "ril message arrived"); + CatLog.d(this, "ril message arrived"); String data = null; if (msg.obj != null) { AsyncResult ar = (AsyncResult) msg.obj; @@ -513,20 +571,20 @@ public class StkService extends Handler implements AppInterface { case MSG_ID_CALL_SETUP: mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, null)); break; - case MSG_ID_SIM_LOADED: + case MSG_ID_ICC_RECORDS_LOADED: break; case MSG_ID_RIL_MSG_DECODED: handleRilMsg((RilMessage) msg.obj); break; case MSG_ID_RESPONSE: - handleCmdResponse((StkResponseMessage) msg.obj); + handleCmdResponse((CatResponseMessage) msg.obj); break; default: - throw new AssertionError("Unrecognized STK command: " + msg.what); + throw new AssertionError("Unrecognized CAT command: " + msg.what); } } - public synchronized void onCmdResponse(StkResponseMessage resMsg) { + public synchronized void onCmdResponse(CatResponseMessage resMsg) { if (resMsg == null) { return; } @@ -535,7 +593,7 @@ public class StkService extends Handler implements AppInterface { msg.sendToTarget(); } - private boolean validateResponse(StkResponseMessage resMsg) { + private boolean validateResponse(CatResponseMessage resMsg) { if (mCurrntCmd != null) { return (resMsg.cmdDet.compareTo(mCurrntCmd.mCmdDet)); } @@ -548,13 +606,13 @@ public class StkService extends Handler implements AppInterface { return true; } } catch (NullPointerException e) { - StkLog.d(this, "Unable to get Menu's items size"); + CatLog.d(this, "Unable to get Menu's items size"); return true; } return false; } - private void handleCmdResponse(StkResponseMessage resMsg) { + private void handleCmdResponse(CatResponseMessage resMsg) { // Make sure the response details match the last valid command. An invalid // response is a one that doesn't have a corresponding proactive command // and sending it can "confuse" the baseband/ril. @@ -563,7 +621,7 @@ public class StkService extends Handler implements AppInterface { // by the framework inside the history stack. That activity will be // available for relaunch using the latest application dialog // (long press on the home button). Relaunching that activity can send - // the same command's result again to the StkService and can cause it to + // the same command's result again to the CatService and can cause it to // get out of sync with the SIM. if (!validateResponse(resMsg)) { return; diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/CommandDetails.java b/telephony/java/com/android/internal/telephony/cat/CommandDetails.java index e81ff98..e3f0798 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/CommandDetails.java +++ b/telephony/java/com/android/internal/telephony/cat/CommandDetails.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; import android.os.Parcel; import android.os.Parcelable; diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/CommandParams.java b/telephony/java/com/android/internal/telephony/cat/CommandParams.java index 3da652f..22a5c8c 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/CommandParams.java +++ b/telephony/java/com/android/internal/telephony/cat/CommandParams.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; import android.graphics.Bitmap; diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/CommandParamsFactory.java b/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java index ce4c459..12204a0 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/CommandParamsFactory.java +++ b/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java @@ -14,14 +14,14 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; import android.graphics.Bitmap; import android.os.Handler; import android.os.Message; import com.android.internal.telephony.GsmAlphabet; -import com.android.internal.telephony.gsm.SIMFileHandler; +import com.android.internal.telephony.IccFileHandler; import java.util.Iterator; import java.util.List; @@ -52,8 +52,11 @@ class CommandParamsFactory extends Handler { static final int REFRESH_NAA_INIT = 0x03; static final int REFRESH_UICC_RESET = 0x04; + // Command Qualifier values for PLI command + static final int LANGUAGE_SETTING = 0x04; + static synchronized CommandParamsFactory getInstance(RilMessageDecoder caller, - SIMFileHandler fh) { + IccFileHandler fh) { if (sInstance != null) { return sInstance; } @@ -63,7 +66,7 @@ class CommandParamsFactory extends Handler { return null; } - private CommandParamsFactory(RilMessageDecoder caller, SIMFileHandler fh) { + private CommandParamsFactory(RilMessageDecoder caller, IccFileHandler fh) { mCaller = caller; mIconLoader = IconLoader.getInstance(this, fh); } @@ -79,7 +82,7 @@ class CommandParamsFactory extends Handler { try { cmdDet = ValueParser.retrieveCommandDetails(ctlvCmdDet); } catch (ResultException e) { - StkLog.d(this, "Failed to procees command details"); + CatLog.d(this, "Failed to procees command details"); } } } @@ -112,7 +115,10 @@ class CommandParamsFactory extends Handler { AppInterface.CommandType cmdType = AppInterface.CommandType .fromInt(cmdDet.typeOfCommand); if (cmdType == null) { - sendCmdParams(ResultCode.CMD_TYPE_NOT_UNDERSTOOD); + // This PROACTIVE COMMAND is presently not handled. Hence set + // result code as BEYOND_TERMINAL_CAPABILITY in TR. + mCmdParams = new CommandParams(cmdDet); + sendCmdParams(ResultCode.BEYOND_TERMINAL_CAPABILITY); return; } @@ -155,10 +161,13 @@ class CommandParamsFactory extends Handler { case PLAY_TONE: cmdPending = processPlayTone(cmdDet, ctlvs); break; + case PROVIDE_LOCAL_INFORMATION: + cmdPending = processProvideLocalInfo(cmdDet, ctlvs); + break; default: // unsupported proactive commands mCmdParams = new CommandParams(cmdDet); - sendCmdParams(ResultCode.CMD_TYPE_NOT_UNDERSTOOD); + sendCmdParams(ResultCode.BEYOND_TERMINAL_CAPABILITY); return; } } catch (ResultException e) { @@ -259,7 +268,7 @@ class CommandParamsFactory extends Handler { List<ComprehensionTlv> ctlvs) throws ResultException { - StkLog.d(this, "process DisplayText"); + CatLog.d(this, "process DisplayText"); TextMessage textMsg = new TextMessage(); IconId iconId = null; @@ -319,7 +328,7 @@ class CommandParamsFactory extends Handler { private boolean processSetUpIdleModeText(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs) throws ResultException { - StkLog.d(this, "process SetUpIdleModeText"); + CatLog.d(this, "process SetUpIdleModeText"); TextMessage textMsg = new TextMessage(); IconId iconId = null; @@ -362,7 +371,7 @@ class CommandParamsFactory extends Handler { private boolean processGetInkey(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs) throws ResultException { - StkLog.d(this, "process GetInkey"); + CatLog.d(this, "process GetInkey"); Input input = new Input(); IconId iconId = null; @@ -380,6 +389,12 @@ class CommandParamsFactory extends Handler { iconId = ValueParser.retrieveIconId(ctlv); } + // parse duration + ctlv = searchForTag(ComprehensionTlvTag.DURATION, ctlvs); + if (ctlv != null) { + input.duration = ValueParser.retrieveDuration(ctlv); + } + input.minLen = 1; input.maxLen = 1; @@ -412,7 +427,7 @@ class CommandParamsFactory extends Handler { private boolean processGetInput(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs) throws ResultException { - StkLog.d(this, "process GetInput"); + CatLog.d(this, "process GetInput"); Input input = new Input(); IconId iconId = null; @@ -476,7 +491,7 @@ class CommandParamsFactory extends Handler { private boolean processRefresh(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs) { - StkLog.d(this, "process Refresh"); + CatLog.d(this, "process Refresh"); // REFRESH proactive command is rerouted by the baseband and handled by // the telephony layer. IDLE TEXT should be removed for a REFRESH command @@ -505,7 +520,7 @@ class CommandParamsFactory extends Handler { private boolean processSelectItem(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs) throws ResultException { - StkLog.d(this, "process SelectItem"); + CatLog.d(this, "process SelectItem"); Menu menu = new Menu(); IconId titleIconId = null; @@ -534,7 +549,7 @@ class CommandParamsFactory extends Handler { ctlv = searchForTag(ComprehensionTlvTag.ITEM_ID, ctlvs); if (ctlv != null) { - // STK items are listed 1...n while list start at 0, need to + // CAT items are listed 1...n while list start at 0, need to // subtract one. menu.defaultItem = ValueParser.retrieveItemId(ctlv) - 1; } @@ -602,7 +617,7 @@ class CommandParamsFactory extends Handler { private boolean processEventNotify(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs) throws ResultException { - StkLog.d(this, "process EventNotify"); + CatLog.d(this, "process EventNotify"); TextMessage textMsg = new TextMessage(); IconId iconId = null; @@ -645,7 +660,7 @@ class CommandParamsFactory extends Handler { private boolean processSetUpEventList(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs) { - StkLog.d(this, "process SetUpEventList"); + CatLog.d(this, "process SetUpEventList"); // // ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.EVENT_LIST, // ctlvs); @@ -670,10 +685,10 @@ class CommandParamsFactory extends Handler { * asynchronous processing is required. * @throws ResultException */ - private boolean processLaunchBrowser(CommandDetails cmdDet, + private boolean processLaunchBrowser(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs) throws ResultException { - StkLog.d(this, "process LaunchBrowser"); + CatLog.d(this, "process LaunchBrowser"); TextMessage confirmMsg = new TextMessage(); IconId iconId = null; @@ -744,10 +759,10 @@ class CommandParamsFactory extends Handler { * asynchronous processing is required.t * @throws ResultException */ - private boolean processPlayTone(CommandDetails cmdDet, + private boolean processPlayTone(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs) throws ResultException { - StkLog.d(this, "process PlayTone"); + CatLog.d(this, "process PlayTone"); Tone tone = null; TextMessage textMsg = new TextMessage(); @@ -810,9 +825,9 @@ class CommandParamsFactory extends Handler { * @return true if the command is processing is pending and additional * asynchronous processing is required. */ - private boolean processSetupCall(CommandDetails cmdDet, + private boolean processSetupCall(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs) throws ResultException { - StkLog.d(this, "process SetupCall"); + CatLog.d(this, "process SetupCall"); Iterator<ComprehensionTlv> iter = ctlvs.iterator(); ComprehensionTlv ctlv = null; @@ -863,4 +878,20 @@ class CommandParamsFactory extends Handler { } return false; } + + private boolean processProvideLocalInfo(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs) + throws ResultException { + CatLog.d(this, "process ProvideLocalInfo"); + switch (cmdDet.commandQualifier) { + case LANGUAGE_SETTING: + CatLog.d(this, "PLI [LANGUAGE_SETTING]"); + mCmdParams = new CommandParams(cmdDet); + break; + default: + CatLog.d(this, "PLI[" + cmdDet.commandQualifier + "] Command Not Supported"); + mCmdParams = new CommandParams(cmdDet); + throw new ResultException(ResultCode.BEYOND_TERMINAL_CAPABILITY); + } + return false; + } } diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/ComprehensionTlv.java b/telephony/java/com/android/internal/telephony/cat/ComprehensionTlv.java index ffde6a3..99f662d 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/ComprehensionTlv.java +++ b/telephony/java/com/android/internal/telephony/cat/ComprehensionTlv.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; import java.util.ArrayList; import java.util.List; diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/Duration.java b/telephony/java/com/android/internal/telephony/cat/Duration.java index 9d8cc97..e8cd404 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/Duration.java +++ b/telephony/java/com/android/internal/telephony/cat/Duration.java @@ -14,14 +14,14 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; import android.os.Parcel; import android.os.Parcelable; /** - * Class for representing "Duration" object for STK. + * Class for representing "Duration" object for CAT. * * {@hide} */ diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/FontSize.java b/telephony/java/com/android/internal/telephony/cat/FontSize.java index bd4f49f..02c7ea0 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/FontSize.java +++ b/telephony/java/com/android/internal/telephony/cat/FontSize.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; /** diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/IconLoader.java b/telephony/java/com/android/internal/telephony/cat/IconLoader.java index fc02d2a..2fa1811 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/IconLoader.java +++ b/telephony/java/com/android/internal/telephony/cat/IconLoader.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; -import com.android.internal.telephony.gsm.SIMFileHandler; +import com.android.internal.telephony.IccFileHandler; import android.graphics.Bitmap; import android.graphics.Color; @@ -40,7 +40,7 @@ class IconLoader extends Handler { private ImageDescriptor mId = null; private Bitmap mCurrentIcon = null; private int mRecordNumber; - private SIMFileHandler mSimFH = null; + private IccFileHandler mSimFH = null; private Message mEndMsg = null; private byte[] mIconData = null; // multi icons state members @@ -68,19 +68,19 @@ class IconLoader extends Handler { private static final int CLUT_ENTRY_SIZE = 3; - private IconLoader(Looper looper , SIMFileHandler fh) { + private IconLoader(Looper looper , IccFileHandler fh) { super(looper); mSimFH = fh; mIconsCache = new HashMap<Integer, Bitmap>(50); } - static IconLoader getInstance(Handler caller, SIMFileHandler fh) { + static IconLoader getInstance(Handler caller, IccFileHandler fh) { if (sLoader != null) { return sLoader; } if (fh != null) { - HandlerThread thread = new HandlerThread("Stk Icon Loader"); + HandlerThread thread = new HandlerThread("Cat Icon Loader"); thread.start(); return new IconLoader(thread.getLooper(), fh); } @@ -163,7 +163,7 @@ class IconLoader extends Handler { break; } } catch (Exception e) { - StkLog.d(this, "Icon load failed"); + CatLog.d(this, "Icon load failed"); // post null icon back to the caller. postIcon(); } @@ -254,7 +254,7 @@ class IconLoader extends Handler { } if (pixelIndex != numOfPixels) { - StkLog.d("IconLoader", "parseToBnW; size error"); + CatLog.d("IconLoader", "parseToBnW; size error"); } return Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888); } diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/ImageDescriptor.java b/telephony/java/com/android/internal/telephony/cat/ImageDescriptor.java index 880b9e5..711d977 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/ImageDescriptor.java +++ b/telephony/java/com/android/internal/telephony/cat/ImageDescriptor.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; /** * {@hide} @@ -69,7 +69,7 @@ public class ImageDescriptor { d.length = ((rawData[valueIndex++] & 0xff) << 8 | (rawData[valueIndex++] & 0xff)); } catch (IndexOutOfBoundsException e) { - StkLog.d("ImageDescripter", "parse; failed parsing image descriptor"); + CatLog.d("ImageDescripter", "parse; failed parsing image descriptor"); d = null; } return d; diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/Input.java b/telephony/java/com/android/internal/telephony/cat/Input.java index 19f724b..13a5ad4 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/Input.java +++ b/telephony/java/com/android/internal/telephony/cat/Input.java @@ -14,14 +14,14 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; import android.graphics.Bitmap; import android.os.Parcel; import android.os.Parcelable; /** - * Container class for STK GET INPUT, GET IN KEY commands parameters. + * Container class for CAT GET INPUT, GET IN KEY commands parameters. * */ public class Input implements Parcelable { @@ -36,6 +36,7 @@ public class Input implements Parcelable { public boolean echo; public boolean yesNo; public boolean helpAvailable; + public Duration duration; Input() { text = ""; @@ -49,6 +50,7 @@ public class Input implements Parcelable { echo = false; yesNo = false; helpAvailable = false; + duration = null; } private Input(Parcel in) { @@ -63,6 +65,7 @@ public class Input implements Parcelable { echo = in.readInt() == 1 ? true : false; yesNo = in.readInt() == 1 ? true : false; helpAvailable = in.readInt() == 1 ? true : false; + duration = in.readParcelable(null); } public int describeContents() { @@ -81,6 +84,7 @@ public class Input implements Parcelable { dest.writeInt(echo ? 1 : 0); dest.writeInt(yesNo ? 1 : 0); dest.writeInt(helpAvailable ? 1 : 0); + dest.writeParcelable(duration, 0); } public static final Parcelable.Creator<Input> CREATOR = new Parcelable.Creator<Input>() { diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/Item.java b/telephony/java/com/android/internal/telephony/cat/Item.java index b2f338c..d4702bb 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/Item.java +++ b/telephony/java/com/android/internal/telephony/cat/Item.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; import android.graphics.Bitmap; import android.os.Parcel; diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/LaunchBrowserMode.java b/telephony/java/com/android/internal/telephony/cat/LaunchBrowserMode.java index 302273c..af043d1 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/LaunchBrowserMode.java +++ b/telephony/java/com/android/internal/telephony/cat/LaunchBrowserMode.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; /** diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/Menu.java b/telephony/java/com/android/internal/telephony/cat/Menu.java index 331f69d..7bbae01 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/Menu.java +++ b/telephony/java/com/android/internal/telephony/cat/Menu.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; import android.graphics.Bitmap; import android.os.Parcel; @@ -24,7 +24,7 @@ import java.util.ArrayList; import java.util.List; /** - * Container class for STK menu (SET UP MENU, SELECT ITEM) parameters. + * Container class for CAT menu (SET UP MENU, SELECT ITEM) parameters. * */ public class Menu implements Parcelable { diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/PresentationType.java b/telephony/java/com/android/internal/telephony/cat/PresentationType.java index 71bdcdc..7c8cd8c 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/PresentationType.java +++ b/telephony/java/com/android/internal/telephony/cat/PresentationType.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; /** diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/ResponseData.java b/telephony/java/com/android/internal/telephony/cat/ResponseData.java index afd1bba..677d66b 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/ResponseData.java +++ b/telephony/java/com/android/internal/telephony/cat/ResponseData.java @@ -14,7 +14,7 @@ * the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; import com.android.internal.telephony.EncodeException; import com.android.internal.telephony.GsmAlphabet; @@ -28,6 +28,16 @@ abstract class ResponseData { * the ByteArrayOutputStream object. */ public abstract void format(ByteArrayOutputStream buf); + + public static void writeLength(ByteArrayOutputStream buf, int length) { + // As per ETSI 102.220 Sec7.1.2, if the total length is greater + // than 0x7F, it should be coded in two bytes and the first byte + // should be 0x81. + if (length > 0x7F) { + buf.write(0x81); + } + buf.write(length); + } } class SelectItemResponseData extends ResponseData { @@ -120,7 +130,7 @@ class GetInkeyInputResponseData extends ResponseData { } // length - one more for data coding scheme. - buf.write(data.length + 1); + writeLength(buf, data.length + 1); // data coding scheme if (mIsUcs2) { diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/ResultCode.java b/telephony/java/com/android/internal/telephony/cat/ResultCode.java index b96a524..8544175 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/ResultCode.java +++ b/telephony/java/com/android/internal/telephony/cat/ResultCode.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; /** diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/ResultException.java b/telephony/java/com/android/internal/telephony/cat/ResultException.java index 2eb16c9..1c2cb63 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/ResultException.java +++ b/telephony/java/com/android/internal/telephony/cat/ResultException.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; /** @@ -22,7 +22,7 @@ package com.android.internal.telephony.gsm.stk; * * {@hide} */ -public class ResultException extends StkException { +public class ResultException extends CatException { private ResultCode mResult; private int mAdditionalInfo; diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/RilMessageDecoder.java b/telephony/java/com/android/internal/telephony/cat/RilMessageDecoder.java index a82177c..a197c9a 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/RilMessageDecoder.java +++ b/telephony/java/com/android/internal/telephony/cat/RilMessageDecoder.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; -import com.android.internal.telephony.gsm.SIMFileHandler; +import com.android.internal.telephony.IccFileHandler; import com.android.internal.telephony.IccUtils; import android.os.Handler; @@ -26,7 +26,7 @@ import android.os.Message; /** * Class used for queuing raw ril messages, decoding them into CommanParams - * objects and sending the result back to the STK Service. + * objects and sending the result back to the CAT Service. */ class RilMessageDecoder extends HierarchicalStateMachine { @@ -51,7 +51,7 @@ class RilMessageDecoder extends HierarchicalStateMachine { * @param fh * @return RilMesssageDecoder */ - public static synchronized RilMessageDecoder getInstance(Handler caller, SIMFileHandler fh) { + public static synchronized RilMessageDecoder getInstance(Handler caller, IccFileHandler fh) { if (sInstance == null) { sInstance = new RilMessageDecoder(caller, fh); sInstance.start(); @@ -85,12 +85,12 @@ class RilMessageDecoder extends HierarchicalStateMachine { } private void sendCmdForExecution(RilMessage rilMsg) { - Message msg = mCaller.obtainMessage(StkService.MSG_ID_RIL_MSG_DECODED, + Message msg = mCaller.obtainMessage(CatService.MSG_ID_RIL_MSG_DECODED, new RilMessage(rilMsg)); msg.sendToTarget(); } - private RilMessageDecoder(Handler caller, SIMFileHandler fh) { + private RilMessageDecoder(Handler caller, IccFileHandler fh) { super("RilMessageDecoder"); addState(mStateStart); @@ -108,7 +108,7 @@ class RilMessageDecoder extends HierarchicalStateMachine { transitionTo(mStateCmdParamsReady); } } else { - StkLog.d(this, "StateStart unexpected expecting START=" + + CatLog.d(this, "StateStart unexpected expecting START=" + CMD_START + " got " + msg.what); } return true; @@ -123,7 +123,7 @@ class RilMessageDecoder extends HierarchicalStateMachine { sendCmdForExecution(mCurrentRilMessage); transitionTo(mStateStart); } else { - StkLog.d(this, "StateCmdParamsReady expecting CMD_PARAMS_READY=" + CatLog.d(this, "StateCmdParamsReady expecting CMD_PARAMS_READY=" + CMD_PARAMS_READY + " got " + msg.what); deferMessage(msg); } @@ -136,21 +136,21 @@ class RilMessageDecoder extends HierarchicalStateMachine { mCurrentRilMessage = rilMsg; switch(rilMsg.mId) { - case StkService.MSG_ID_SESSION_END: - case StkService.MSG_ID_CALL_SETUP: + case CatService.MSG_ID_SESSION_END: + case CatService.MSG_ID_CALL_SETUP: mCurrentRilMessage.mResCode = ResultCode.OK; sendCmdForExecution(mCurrentRilMessage); decodingStarted = false; break; - case StkService.MSG_ID_PROACTIVE_COMMAND: - case StkService.MSG_ID_EVENT_NOTIFY: - case StkService.MSG_ID_REFRESH: + case CatService.MSG_ID_PROACTIVE_COMMAND: + case CatService.MSG_ID_EVENT_NOTIFY: + case CatService.MSG_ID_REFRESH: byte[] rawData = null; try { rawData = IccUtils.hexStringToBytes((String) rilMsg.mData); } catch (Exception e) { // zombie messages are dropped - StkLog.d(this, "decodeMessageParams dropping zombie messages"); + CatLog.d(this, "decodeMessageParams dropping zombie messages"); decodingStarted = false; break; } diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/TextAlignment.java b/telephony/java/com/android/internal/telephony/cat/TextAlignment.java index c5dd50e..7fb58a5 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/TextAlignment.java +++ b/telephony/java/com/android/internal/telephony/cat/TextAlignment.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; /** diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/TextAttribute.java b/telephony/java/com/android/internal/telephony/cat/TextAttribute.java index ace4300..0dea640 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/TextAttribute.java +++ b/telephony/java/com/android/internal/telephony/cat/TextAttribute.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; /** diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/TextColor.java b/telephony/java/com/android/internal/telephony/cat/TextColor.java index 126fc62..6447e74 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/TextColor.java +++ b/telephony/java/com/android/internal/telephony/cat/TextColor.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; /** diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/TextMessage.java b/telephony/java/com/android/internal/telephony/cat/TextMessage.java index 3b6a09a..5ffd076 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/TextMessage.java +++ b/telephony/java/com/android/internal/telephony/cat/TextMessage.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; import android.graphics.Bitmap; import android.os.Parcel; diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/Tone.java b/telephony/java/com/android/internal/telephony/cat/Tone.java index b64e777..27b4489 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/Tone.java +++ b/telephony/java/com/android/internal/telephony/cat/Tone.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; import android.os.Parcel; import android.os.Parcelable; diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/ToneSettings.java b/telephony/java/com/android/internal/telephony/cat/ToneSettings.java index 90cc6c1..6375afb 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/ToneSettings.java +++ b/telephony/java/com/android/internal/telephony/cat/ToneSettings.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; import android.os.Parcel; import android.os.Parcelable; diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/ValueParser.java b/telephony/java/com/android/internal/telephony/cat/ValueParser.java index 09a860e..34e4811 100644 --- a/telephony/java/com/android/internal/telephony/gsm/stk/ValueParser.java +++ b/telephony/java/com/android/internal/telephony/cat/ValueParser.java @@ -14,11 +14,11 @@ * the License. */ -package com.android.internal.telephony.gsm.stk; +package com.android.internal.telephony.cat; import com.android.internal.telephony.GsmAlphabet; import com.android.internal.telephony.IccUtils; -import com.android.internal.telephony.gsm.stk.Duration.TimeUnit; +import com.android.internal.telephony.cat.Duration.TimeUnit; import java.io.UnsupportedEncodingException; import java.util.ArrayList; @@ -182,7 +182,7 @@ abstract class ValueParser { */ static ItemsIconId retrieveItemsIconId(ComprehensionTlv ctlv) throws ResultException { - StkLog.d("ValueParser", "retrieveItemsIconId:"); + CatLog.d("ValueParser", "retrieveItemsIconId:"); ItemsIconId id = new ItemsIconId(); byte[] rawValue = ctlv.getRawValue(); diff --git a/telephony/java/com/android/internal/telephony/cat/package.html b/telephony/java/com/android/internal/telephony/cat/package.html new file mode 100644 index 0000000..5b6bfc6 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cat/package.html @@ -0,0 +1,5 @@ +<HTML> +<BODY> +Provides classes for ICC Toolkit Service (CAT). +</BODY> +</HTML> diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java index f48c956..f31bf24 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java +++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java @@ -42,6 +42,7 @@ import android.telephony.SignalStrength; import android.text.TextUtils; import android.util.Log; +import com.android.internal.telephony.cat.CatService; import com.android.internal.telephony.Call; import com.android.internal.telephony.CallStateException; import com.android.internal.telephony.CommandException; @@ -108,7 +109,7 @@ public class CDMAPhone extends PhoneBase { PhoneSubInfo mSubInfo; EriManager mEriManager; WakeLock mWakeLock; - + CatService mCcatService; // mNvLoadedRegistrants are informed after the EVENT_NV_READY private RegistrantList mNvLoadedRegistrants = new RegistrantList(); @@ -160,6 +161,8 @@ public class CDMAPhone extends PhoneBase { mRuimSmsInterfaceManager = new RuimSmsInterfaceManager(this, mSMS); mSubInfo = new PhoneSubInfo(this); mEriManager = new EriManager(this, context, EriManager.ERI_FROM_XML); + mCcatService = CatService.getInstance(mCM, mRuimRecords, mContext, + mIccFileHandler, mRuimCard); mCM.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null); mRuimRecords.registerForRecordsLoaded(this, EVENT_RUIM_RECORDS_LOADED, null); @@ -220,6 +223,7 @@ public class CDMAPhone extends PhoneBase { mCM.unregisterForNVReady(this); //EVENT_NV_READY mSST.unregisterForNetworkAttach(this); //EVENT_REGISTERED_TO_NETWORK mCM.unSetOnSuppServiceNotification(this); + removeCallbacks(mExitEcmRunnable); mPendingMmis.clear(); @@ -235,6 +239,7 @@ public class CDMAPhone extends PhoneBase { mRuimSmsInterfaceManager.dispose(); mSubInfo.dispose(); mEriManager.dispose(); + mCcatService.dispose(); } } @@ -250,6 +255,8 @@ public class CDMAPhone extends PhoneBase { this.mCT = null; this.mSST = null; this.mEriManager = null; + this.mCcatService = null; + this.mExitEcmRunnable = null; } protected void finalize() { diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java index dceff2a..d6fc134 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java @@ -483,6 +483,11 @@ final class CdmaSMSDispatcher extends SMSDispatcher { mCm.setCdmaBroadcastConfig(configValuesArray, response); } + protected void handleBroadcastSms(AsyncResult ar) { + // Not supported + Log.e(TAG, "Error! Not implemented for CDMA."); + } + private int resultToCause(int rc) { switch (rc) { case Activity.RESULT_OK: diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java index 2cad6cc..d2a4bd8 100644..100755 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java @@ -552,32 +552,53 @@ final class CdmaServiceStateTracker extends ServiceStateTracker { } @Override - protected void powerOffRadioSafely(){ - // clean data connection + protected void powerOffRadioSafely() { DataConnectionTracker dcTracker = phone.mDataConnection; Message msg = dcTracker.obtainMessage(DataConnectionTracker.EVENT_CLEAN_UP_CONNECTION); - msg.arg1 = 1; // tearDown is true msg.obj = CDMAPhone.REASON_RADIO_TURNED_OFF; - dcTracker.sendMessage(msg); - synchronized(this) { - if (!mPendingRadioPowerOffAfterDataOff) { - DataConnectionTracker.State currentState = dcTracker.getState(); - if (currentState != DataConnectionTracker.State.CONNECTED - && currentState != DataConnectionTracker.State.DISCONNECTING - && currentState != DataConnectionTracker.State.INITING) { - if (DBG) log("Data disconnected, turn off radio right away."); - hangupAndPowerOff(); - } - else if (sendEmptyMessageDelayed(EVENT_SET_RADIO_POWER_OFF, 30000)) { - if (DBG) { - log("Wait up to 30 sec for data to disconnect, then turn off radio."); + synchronized (this) { + if (networkType == ServiceState.RADIO_TECHNOLOGY_1xRTT) { + /* + * In 1x CDMA , during radio power off modem will disconnect the + * data call and sends the power down registration message along + * with the data call release message to the network + */ + + msg.arg1 = 0; // tearDown is false since modem does it anyway for 1X + dcTracker.sendMessage(msg); + + Log.w(LOG_TAG, "Turn off the radio right away"); + hangupAndPowerOff(); + } else { + if (!mPendingRadioPowerOffAfterDataOff) { + DataConnectionTracker.State currentState = dcTracker.getState(); + if (currentState != DataConnectionTracker.State.CONNECTED + && currentState != DataConnectionTracker.State.DISCONNECTING + && currentState != DataConnectionTracker.State.INITING) { + + msg.arg1 = 0; // tearDown is false as it is not needed. + dcTracker.sendMessage(msg); + + if (DBG) + log("Data disconnected, turn off radio right away."); + hangupAndPowerOff(); + } else { + // clean data connection + msg.arg1 = 1; // tearDown is true + dcTracker.sendMessage(msg); + + if (sendEmptyMessageDelayed(EVENT_SET_RADIO_POWER_OFF, 30000)) { + if (DBG) { + log("Wait upto 30s for data to disconnect, then turn off radio."); + } + mPendingRadioPowerOffAfterDataOff = true; + } else { + Log.w(LOG_TAG, "Cannot send delayed Msg, turn off radio right away."); + hangupAndPowerOff(); + } } - mPendingRadioPowerOffAfterDataOff = true; - } else { - Log.w(LOG_TAG, "Cannot send delayed Msg, turn off radio right away."); - hangupAndPowerOff(); } } } diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java index e97549d..f4a6d11 100644 --- a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java +++ b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java @@ -192,6 +192,18 @@ public class RuimSmsInterfaceManager extends IccSmsInterfaceManager { return mSms; } + public boolean enableCellBroadcast(int messageIdentifier) { + // Not implemented + Log.e(LOG_TAG, "Error! Not implemented for CDMA."); + return false; + } + + public boolean disableCellBroadcast(int messageIdentifier) { + // Not implemented + Log.e(LOG_TAG, "Error! Not implemented for CDMA."); + return false; + } + protected void log(String msg) { Log.d(LOG_TAG, "[RuimSmsInterfaceManager] " + msg); } diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java index 6f024ed..d563dc8 100755..100644 --- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java @@ -29,6 +29,7 @@ import com.android.internal.telephony.SmsMessageBase; import com.android.internal.telephony.TelephonyProperties; import com.android.internal.telephony.cdma.sms.BearerData; import com.android.internal.telephony.cdma.sms.CdmaSmsAddress; +import com.android.internal.telephony.cdma.sms.CdmaSmsSubaddress; import com.android.internal.telephony.cdma.sms.SmsEnvelope; import com.android.internal.telephony.cdma.sms.UserData; import com.android.internal.util.HexDump; @@ -138,6 +139,7 @@ public class SmsMessage extends SmsMessageBase { SmsMessage msg = new SmsMessage(); SmsEnvelope env = new SmsEnvelope(); CdmaSmsAddress addr = new CdmaSmsAddress(); + CdmaSmsSubaddress subaddr = new CdmaSmsSubaddress(); byte[] data; byte count; int countInt; @@ -180,15 +182,24 @@ public class SmsMessage extends SmsMessageBase { addr.origBytes = data; - // ignore subaddress - p.readInt(); //p_cur->sSubAddress.subaddressType - p.readInt(); //p_cur->sSubAddress.odd - count = p.readByte(); //p_cur->sSubAddress.number_of_digits - //p_cur->sSubAddress.digits[digitCount] : - for (int index=0; index < count; index++) { - p.readByte(); + subaddr.type = p.readInt(); // p_cur->sSubAddress.subaddressType + subaddr.odd = p.readByte(); // p_cur->sSubAddress.odd + count = p.readByte(); // p_cur->sSubAddress.number_of_digits + + if (count < 0) { + count = 0; } + // p_cur->sSubAddress.digits[digitCount] : + + data = new byte[count]; + + for (int index = 0; index < count; ++index) { + data[index] = p.readByte(); + } + + subaddr.origBytes = data; + /* currently not supported by the modem-lib: env.bearerReply env.replySeqNo @@ -210,6 +221,7 @@ public class SmsMessage extends SmsMessageBase { // link the the filled objects to the SMS env.origAddress = addr; + env.origSubaddress = subaddr; msg.originatingAddress = addr; msg.mEnvelope = env; @@ -818,6 +830,8 @@ public class SmsMessage extends SmsMessageBase { output.write(mEnvelope.teleService); output.write(mEnvelope.origAddress.origBytes, 0, mEnvelope.origAddress.origBytes.length); output.write(mEnvelope.bearerData, 0, mEnvelope.bearerData.length); + output.write(mEnvelope.origSubaddress.origBytes, 0, + mEnvelope.origSubaddress.origBytes.length); return output.toByteArray(); } diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsSubaddress.java b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsSubaddress.java new file mode 100644 index 0000000..f9cebf5 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsSubaddress.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2010 The Android Open Source Project. All rights reserved. + * Copyright (C) 2010 Code Aurora Forum. All rights reserved. + * + * 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.cdma.sms; + +public class CdmaSmsSubaddress { + public int type; + + public byte odd; + + public byte[] origBytes; +} + diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java index 0dcacc1..4f00163 100644 --- a/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java +++ b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java @@ -17,6 +17,8 @@ package com.android.internal.telephony.cdma.sms; +import com.android.internal.telephony.cdma.sms.CdmaSmsSubaddress; + public final class SmsEnvelope{ /** * Message Types @@ -74,17 +76,23 @@ public final class SmsEnvelope{ /** * The origination address identifies the originator of the SMS message. - * (See 3GPP2 C.S0015-B, v2, 3.4.3.4) + * (See 3GPP2 C.S0015-B, v2, 3.4.3.3) */ public CdmaSmsAddress origAddress; /** * The destination address identifies the target of the SMS message. - * (See 3GPP2 C.S0015-B, v2, 3.4.3.4) + * (See 3GPP2 C.S0015-B, v2, 3.4.3.3) */ public CdmaSmsAddress destAddress; /** + * The origination subaddress identifies the originator of the SMS message. + * (See 3GPP2 C.S0015-B, v2, 3.4.3.4) + */ + public CdmaSmsSubaddress origSubaddress; + + /** * The 6-bit bearer reply parameter is used to request the return of a * SMS Acknowledge Message. * (See 3GPP2 C.S0015-B, v2, 3.4.3.5) diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java index 689a972..3959c67 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java +++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java @@ -49,6 +49,7 @@ import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDI import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE; import static com.android.internal.telephony.TelephonyProperties.PROPERTY_BASEBAND_VERSION; +import com.android.internal.telephony.cat.CatService; import com.android.internal.telephony.Call; import com.android.internal.telephony.CallForwardInfo; import com.android.internal.telephony.CallStateException; @@ -68,7 +69,6 @@ import com.android.internal.telephony.PhoneProxy; import com.android.internal.telephony.PhoneSubInfo; import com.android.internal.telephony.TelephonyProperties; import com.android.internal.telephony.UUSInfo; -import com.android.internal.telephony.gsm.stk.StkService; import com.android.internal.telephony.test.SimulatedRadioControl; import com.android.internal.telephony.IccVmNotSupportedException; @@ -102,7 +102,7 @@ public class GSMPhone extends PhoneBase { GsmSMSDispatcher mSMS; SIMRecords mSIMRecords; SimCard mSimCard; - StkService mStkService; + CatService mStkService; ArrayList <GsmMmiCode> mPendingMMIs = new ArrayList<GsmMmiCode>(); SimPhoneBookInterfaceManager mSimPhoneBookIntManager; SimSmsInterfaceManager mSimSmsIntManager; @@ -150,7 +150,7 @@ public class GSMPhone extends PhoneBase { mSimSmsIntManager = new SimSmsInterfaceManager(this, mSMS); mSubInfo = new PhoneSubInfo(this); } - mStkService = StkService.getInstance(mCM, mSIMRecords, mContext, + mStkService = CatService.getInstance(mCM, mSIMRecords, mContext, (SIMFileHandler)mIccFileHandler, mSimCard); mCM.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null); @@ -968,7 +968,9 @@ public class GSMPhone extends PhoneBase { } public void getCallWaiting(Message onComplete) { - mCM.queryCallWaiting(CommandsInterface.SERVICE_CLASS_VOICE, onComplete); + //As per 3GPP TS 24.083, section 1.6 UE doesn't need to send service + //class parameter in call waiting interrogation to network + mCM.queryCallWaiting(CommandsInterface.SERVICE_CLASS_NONE, onComplete); } public void setCallWaiting(boolean enable, Message onComplete) { @@ -1221,7 +1223,8 @@ public class GSMPhone extends PhoneBase { // Check if this is a different SIM than the previous one. If so unset the // voice mail number. String imsi = getVmSimImsi(); - if (imsi != null && !getSubscriberId().equals(imsi)) { + String imsiFromSIM = getSubscriberId(); + if (imsi != null && imsiFromSIM != null && !imsiFromSIM.equals(imsi)) { storeVoiceMailNumber(null); setVmSimImsi(null); } @@ -1480,4 +1483,7 @@ public class GSMPhone extends PhoneBase { Log.e(LOG_TAG, "Error! This functionality is not implemented for GSM."); } + public boolean isCspPlmnEnabled() { + return mSIMRecords.isCspPlmnEnabled(); + } } diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java b/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java index aa16fa3..fe7a5cb 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java @@ -44,6 +44,13 @@ public final class GsmMmiCode extends Handler implements MmiCode { //***** Constants + // Max Size of the Short Code (aka Short String from TS 22.030 6.5.2) + static final int MAX_LENGTH_SHORT_CODE = 2; + + // TS 22.030 6.5.2 Every Short String USSD command will end with #-key + // (known as #-String) + static final char END_OF_USSD_COMMAND = '#'; + // From TS 22.030 6.5.2 static final String ACTION_ACTIVATE = "*"; static final String ACTION_DEACTIVATE = "#"; @@ -446,22 +453,69 @@ public final class GsmMmiCode extends Handler implements MmiCode { } /** - * Helper function for newFromDialString. Returns true if dialString appears to be a short code - * AND conditions are correct for it to be treated as such. + * Helper function for newFromDialString. Returns true if dialString appears + * to be a short code AND conditions are correct for it to be treated as + * such. */ static private boolean isShortCode(String dialString, GSMPhone phone) { // Refer to TS 22.030 Figure 3.5.3.2: - // A 1 or 2 digit "short code" is treated as USSD if it is entered while on a call or - // does not satisfy the condition (exactly 2 digits && starts with '1'). - return ((dialString != null && dialString.length() <= 2) - && !PhoneNumberUtils.isEmergencyNumber(dialString) - && (phone.isInCall() - || !((dialString.length() == 2 && dialString.charAt(0) == '1') - /* While contrary to TS 22.030, there is strong precedence - * for treating "0" and "00" as call setup strings. - */ - || dialString.equals("0") - || dialString.equals("00")))); + if (dialString == null) { + return false; + } + + // Illegal dial string characters will give a ZERO length. + // At this point we do not want to crash as any application with + // call privileges may send a non dial string. + // It return false as when the dialString is equal to NULL. + if (dialString.length() == 0) { + return false; + } + + if (PhoneNumberUtils.isEmergencyNumber(dialString)) { + return false; + } else { + return isShortCodeUSSD(dialString, phone); + } + } + + /** + * Helper function for isShortCode. Returns true if dialString appears to be + * a short code and it is a USSD structure + * + * According to the 3PGG TS 22.030 specification Figure 3.5.3.2: A 1 or 2 + * digit "short code" is treated as USSD if it is entered while on a call or + * does not satisfy the condition (exactly 2 digits && starts with '1'), there + * are however exceptions to this rule (see below) + * + * Exception (1) to Call initiation is: If the user of the device is already in a call + * and enters a Short String without any #-key at the end and the length of the Short String is + * equal or less then the MAX_LENGTH_SHORT_CODE [constant that is equal to 2] + * + * The phone shall initiate a USSD/SS commands. + * + * Exception (2) to Call initiation is: If the user of the device enters one + * Digit followed by the #-key. This rule defines this String as the + * #-String which is a USSD/SS command. + * + * The phone shall initiate a USSD/SS command. + */ + static private boolean isShortCodeUSSD(String dialString, GSMPhone phone) { + if (dialString != null) { + if (phone.isInCall()) { + // The maximum length of a Short Code (aka Short String) is 2 + if (dialString.length() <= MAX_LENGTH_SHORT_CODE) { + return true; + } + } + + // The maximum length of a Short Code (aka Short String) is 2 + if (dialString.length() <= MAX_LENGTH_SHORT_CODE) { + if (dialString.charAt(dialString.length() - 1) == END_OF_USSD_COMMAND) { + return true; + } + } + } + return false; } /** diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java index 3079a64..40ee0b0 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java @@ -22,20 +22,26 @@ import android.app.PendingIntent.CanceledException; import android.content.Intent; import android.os.AsyncResult; import android.os.Message; +import android.os.SystemProperties; import android.provider.Telephony.Sms.Intents; import android.telephony.ServiceState; +import android.telephony.SmsCbMessage; +import android.telephony.gsm.GsmCellLocation; import android.util.Config; import android.util.Log; +import com.android.internal.telephony.BaseCommands; import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.IccUtils; import com.android.internal.telephony.SMSDispatcher; import com.android.internal.telephony.SmsHeader; import com.android.internal.telephony.SmsMessageBase; import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails; +import com.android.internal.telephony.TelephonyProperties; import java.util.ArrayList; import java.util.HashMap; +import java.util.Iterator; import static android.telephony.SmsMessage.MessageClass; @@ -47,6 +53,8 @@ final class GsmSMSDispatcher extends SMSDispatcher { GsmSMSDispatcher(GSMPhone phone) { super(phone); mGsmPhone = phone; + + ((BaseCommands)mCm).setOnNewGsmBroadcastSms(this, EVENT_NEW_BROADCAST_SMS, null); } /** @@ -383,4 +391,162 @@ final class GsmSMSDispatcher extends SMSDispatcher { return CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR; } } + + /** + * Holds all info about a message page needed to assemble a complete + * concatenated message + */ + private static final class SmsCbConcatInfo { + private final SmsCbHeader mHeader; + + private final String mPlmn; + + private final int mLac; + + private final int mCid; + + public SmsCbConcatInfo(SmsCbHeader header, String plmn, int lac, int cid) { + mHeader = header; + mPlmn = plmn; + mLac = lac; + mCid = cid; + } + + @Override + public int hashCode() { + return mHeader.messageIdentifier * 31 + mHeader.updateNumber; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof SmsCbConcatInfo) { + SmsCbConcatInfo other = (SmsCbConcatInfo)obj; + + // Two pages match if all header attributes (except the page + // index) are identical, and both pages belong to the same + // location (which is also determined by the scope parameter) + if (mHeader.geographicalScope == other.mHeader.geographicalScope + && mHeader.messageCode == other.mHeader.messageCode + && mHeader.updateNumber == other.mHeader.updateNumber + && mHeader.messageIdentifier == other.mHeader.messageIdentifier + && mHeader.dataCodingScheme == other.mHeader.dataCodingScheme + && mHeader.nrOfPages == other.mHeader.nrOfPages) { + return matchesLocation(other.mPlmn, other.mLac, other.mCid); + } + } + + return false; + } + + /** + * Checks if this concatenation info matches the given location. The + * granularity of the match depends on the geographical scope. + * + * @param plmn PLMN + * @param lac Location area code + * @param cid Cell ID + * @return true if matching, false otherwise + */ + public boolean matchesLocation(String plmn, int lac, int cid) { + switch (mHeader.geographicalScope) { + case SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE: + case SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE: + if (mCid != cid) { + return false; + } + // deliberate fall-through + case SmsCbMessage.GEOGRAPHICAL_SCOPE_LA_WIDE: + if (mLac != lac) { + return false; + } + // deliberate fall-through + case SmsCbMessage.GEOGRAPHICAL_SCOPE_PLMN_WIDE: + return mPlmn != null && mPlmn.equals(plmn); + } + + return false; + } + } + + // This map holds incomplete concatenated messages waiting for assembly + private HashMap<SmsCbConcatInfo, byte[][]> mSmsCbPageMap = + new HashMap<SmsCbConcatInfo, byte[][]>(); + + protected void handleBroadcastSms(AsyncResult ar) { + try { + byte[][] pdus = null; + byte[] receivedPdu = (byte[])ar.result; + + if (Config.LOGD) { + for (int i = 0; i < receivedPdu.length; i += 8) { + StringBuilder sb = new StringBuilder("SMS CB pdu data: "); + for (int j = i; j < i + 8 && j < receivedPdu.length; j++) { + int b = receivedPdu[j] & 0xff; + if (b < 0x10) { + sb.append("0"); + } + sb.append(Integer.toHexString(b)).append(" "); + } + Log.d(TAG, sb.toString()); + } + } + + SmsCbHeader header = new SmsCbHeader(receivedPdu); + String plmn = SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC); + GsmCellLocation cellLocation = (GsmCellLocation)mGsmPhone.getCellLocation(); + int lac = cellLocation.getLac(); + int cid = cellLocation.getCid(); + + if (header.nrOfPages > 1) { + // Multi-page message + SmsCbConcatInfo concatInfo = new SmsCbConcatInfo(header, plmn, lac, cid); + + // Try to find other pages of the same message + pdus = mSmsCbPageMap.get(concatInfo); + + if (pdus == null) { + // This it the first page of this message, make room for all + // pages and keep until complete + pdus = new byte[header.nrOfPages][]; + + mSmsCbPageMap.put(concatInfo, pdus); + } + + // Page parameter is one-based + pdus[header.pageIndex - 1] = receivedPdu; + + for (int i = 0; i < pdus.length; i++) { + if (pdus[i] == null) { + // Still missing pages, exit + return; + } + } + + // Message complete, remove and dispatch + mSmsCbPageMap.remove(concatInfo); + } else { + // Single page message + pdus = new byte[1][]; + pdus[0] = receivedPdu; + } + + dispatchBroadcastPdus(pdus); + + // Remove messages that are out of scope to prevent the map from + // growing indefinitely, containing incomplete messages that were + // never assembled + Iterator<SmsCbConcatInfo> iter = mSmsCbPageMap.keySet().iterator(); + + while (iter.hasNext()) { + SmsCbConcatInfo info = iter.next(); + + if (!info.matchesLocation(plmn, lac, cid)) { + iter.remove(); + } + } + } catch (RuntimeException e) { + Log.e(TAG, "Error in decoding SMS CB pdu", e); + } + } + } diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMFileHandler.java b/telephony/java/com/android/internal/telephony/gsm/SIMFileHandler.java index 206e62f..e8d10f9 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SIMFileHandler.java +++ b/telephony/java/com/android/internal/telephony/gsm/SIMFileHandler.java @@ -81,6 +81,7 @@ public final class SIMFileHandler extends IccFileHandler implements IccConstants case EF_SPN_CPHS: case EF_SPN_SHORT_CPHS: case EF_INFO_CPHS: + case EF_CSP_CPHS: return MF_SIM + DF_GSM; case EF_PBR: diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java index b14896a..438996f 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java +++ b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java @@ -73,6 +73,7 @@ public final class SIMRecords extends IccRecords { * mCphsInfo[1] and mCphsInfo[2] is CPHS Service Table */ private byte[] mCphsInfo = null; + boolean mCspPlmnEnabled = true; byte[] efMWIS = null; byte[] efCPHS_MWI =null; @@ -93,6 +94,7 @@ public final class SIMRecords extends IccRecords { static final int SPN_RULE_SHOW_PLMN = 0x02; // From TS 51.011 EF[SPDI] section + static final int TAG_SPDI = 0xA3; static final int TAG_SPDI_PLMN_LIST = 0x80; // Full Name IEI from TS 24.008 @@ -140,6 +142,7 @@ public final class SIMRecords extends IccRecords { private static final int EVENT_SET_MSISDN_DONE = 30; private static final int EVENT_SIM_REFRESH = 31; private static final int EVENT_GET_CFIS_DONE = 32; + private static final int EVENT_GET_CSP_CPHS_DONE = 33; // ***** Constructor @@ -559,6 +562,13 @@ public final class SIMRecords extends IccRecords { break; case EVENT_GET_CPHS_MAILBOX_DONE: case EVENT_GET_MBDN_DONE: + //Resetting the voice mail number and voice mail tag to null + //as these should be updated from the data read from EF_MBDN. + //If they are not reset, incase of invalid data/exception these + //variables are retaining their previous values and are + //causing invalid voice mailbox info display to user. + voiceMailNum = null; + voiceMailTag = null; isRecordLoadResponse = true; ar = (AsyncResult)msg.obj; @@ -994,6 +1004,22 @@ public final class SIMRecords extends IccRecords { ((GSMPhone) phone).notifyCallForwardingIndicator(); break; + case EVENT_GET_CSP_CPHS_DONE: + isRecordLoadResponse = true; + + ar = (AsyncResult)msg.obj; + + if (ar.exception != null) { + Log.e(LOG_TAG,"Exception in fetching EF_CSP data " + ar.exception); + break; + } + + data = (byte[])ar.result; + + Log.i(LOG_TAG,"EF_CSP: " + IccUtils.bytesToHexString(data)); + handleEfCspData(data); + break; + }}catch (RuntimeException exc) { // I don't want these exceptions to be fatal Log.w(LOG_TAG, "Exception parsing SIM record", exc); @@ -1017,6 +1043,12 @@ public final class SIMRecords extends IccRecords { new AdnRecordLoader(phone).loadFromEF(EF_MAILBOX_CPHS, EF_EXT1, 1, obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE)); break; + case EF_CSP_CPHS: + recordsToLoad++; + Log.i(LOG_TAG, "[CSP] SIM Refresh for EF_CSP_CPHS"); + phone.getIccFileHandler().loadEFTransparent(EF_CSP_CPHS, + obtainMessage(EVENT_GET_CSP_CPHS_DONE)); + break; default: // For now, fetch all records if this is not a // voicemail number. @@ -1247,6 +1279,9 @@ public final class SIMRecords extends IccRecords { iccFh.loadEFTransparent(EF_INFO_CPHS, obtainMessage(EVENT_GET_INFO_CPHS_DONE)); recordsToLoad++; + iccFh.loadEFTransparent(EF_CSP_CPHS,obtainMessage(EVENT_GET_CSP_CPHS_DONE)); + recordsToLoad++; + // XXX should seek instead of examining them all if (false) { // XXX iccFh.loadEFLinearFixedAll(EF_SMS, obtainMessage(EVENT_GET_ALL_SMS_DONE)); @@ -1426,8 +1461,12 @@ public final class SIMRecords extends IccRecords { byte[] plmnEntries = null; - // There should only be one TAG_SPDI_PLMN_LIST for ( ; tlv.isValidObject() ; tlv.nextObject()) { + // Skip SPDI tag, if existant + if (tlv.getTag() == TAG_SPDI) { + tlv = new SimTlv(tlv.getData(), 0, tlv.getData().length); + } + // There should only be one TAG_SPDI_PLMN_LIST if (tlv.getTag() == TAG_SPDI_PLMN_LIST) { plmnEntries = tlv.getData(); break; @@ -1464,4 +1503,53 @@ public final class SIMRecords extends IccRecords { Log.d(LOG_TAG, "[SIMRecords] " + s); } + /** + * Return true if "Restriction of menu options for manual PLMN selection" + * bit is set or EF_CSP data is unavailable, return false otherwise. + */ + public boolean isCspPlmnEnabled() { + return mCspPlmnEnabled; + } + + /** + * Parse EF_CSP data and check if + * "Restriction of menu options for manual PLMN selection" is + * Enabled/Disabled + * + * @param data EF_CSP hex data. + */ + private void handleEfCspData(byte[] data) { + // As per spec CPHS4_2.WW6, CPHS B.4.7.1, EF_CSP contains CPHS defined + // 18 bytes (i.e 9 service groups info) and additional data specific to + // operator. The valueAddedServicesGroup is not part of standard + // services. This is operator specific and can be programmed any where. + // Normally this is programmed as 10th service after the standard + // services. + int usedCspGroups = data.length / 2; + // This is the "Servive Group Number" of "Value Added Services Group". + byte valueAddedServicesGroup = (byte)0xC0; + + mCspPlmnEnabled = true; + for (int i = 0; i < usedCspGroups; i++) { + if (data[2 * i] == valueAddedServicesGroup) { + Log.i(LOG_TAG, "[CSP] found ValueAddedServicesGroup, value " + + data[(2 * i) + 1]); + if ((data[(2 * i) + 1] & 0x80) == 0x80) { + // Bit 8 is for + // "Restriction of menu options for manual PLMN selection". + // Operator Selection menu should be enabled. + mCspPlmnEnabled = true; + } else { + mCspPlmnEnabled = false; + // Operator Selection menu should be disabled. + // Operator Selection Mode should be set to Automatic. + Log.i(LOG_TAG,"[CSP] Set Automatic Network Selection"); + phone.setNetworkSelectionModeAutomatic(null); + } + return; + } + } + + Log.w(LOG_TAG, "[CSP] Value Added Service Group (0xC0), not found!"); + } } diff --git a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java index 67ecc77..e55596b 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java +++ b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java @@ -17,7 +17,9 @@ package com.android.internal.telephony.gsm; import android.content.Context; +import android.content.pm.PackageManager; import android.os.AsyncResult; +import android.os.Binder; import android.os.Handler; import android.os.Message; import android.util.Log; @@ -30,7 +32,10 @@ import com.android.internal.telephony.SmsRawData; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Set; import static android.telephony.SmsManager.STATUS_ON_ICC_FREE; @@ -45,9 +50,15 @@ public class SimSmsInterfaceManager extends IccSmsInterfaceManager { private final Object mLock = new Object(); private boolean mSuccess; private List<SmsRawData> mSms; + private HashMap<Integer, HashSet<String>> mCellBroadcastSubscriptions = + new HashMap<Integer, HashSet<String>>(); private static final int EVENT_LOAD_DONE = 1; private static final int EVENT_UPDATE_DONE = 2; + private static final int EVENT_SET_BROADCAST_ACTIVATION_DONE = 3; + private static final int EVENT_SET_BROADCAST_CONFIG_DONE = 4; + private static final int SMS_CB_CODE_SCHEME_MIN = 0; + private static final int SMS_CB_CODE_SCHEME_MAX = 255; Handler mHandler = new Handler() { @Override @@ -75,6 +86,14 @@ public class SimSmsInterfaceManager extends IccSmsInterfaceManager { mLock.notifyAll(); } break; + case EVENT_SET_BROADCAST_ACTIVATION_DONE: + case EVENT_SET_BROADCAST_CONFIG_DONE: + ar = (AsyncResult) msg.obj; + synchronized (mLock) { + mSuccess = (ar.exception == null); + mLock.notifyAll(); + } + break; } } }; @@ -192,6 +211,126 @@ public class SimSmsInterfaceManager extends IccSmsInterfaceManager { return mSms; } + public boolean enableCellBroadcast(int messageIdentifier) { + if (DBG) log("enableCellBroadcast"); + + Context context = mPhone.getContext(); + + context.enforceCallingPermission( + "android.permission.RECEIVE_SMS", + "Enabling cell broadcast SMS"); + + String client = context.getPackageManager().getNameForUid( + Binder.getCallingUid()); + HashSet<String> clients = mCellBroadcastSubscriptions.get(messageIdentifier); + + if (clients == null) { + // This is a new message identifier + clients = new HashSet<String>(); + mCellBroadcastSubscriptions.put(messageIdentifier, clients); + + if (!updateCellBroadcastConfig()) { + mCellBroadcastSubscriptions.remove(messageIdentifier); + return false; + } + } + + clients.add(client); + + if (DBG) + log("Added cell broadcast subscription for MID " + messageIdentifier + + " from client " + client); + + return true; + } + + public boolean disableCellBroadcast(int messageIdentifier) { + if (DBG) log("disableCellBroadcast"); + + Context context = mPhone.getContext(); + + context.enforceCallingPermission( + "android.permission.RECEIVE_SMS", + "Disabling cell broadcast SMS"); + + String client = context.getPackageManager().getNameForUid( + Binder.getCallingUid()); + HashSet<String> clients = mCellBroadcastSubscriptions.get(messageIdentifier); + + if (clients != null && clients.remove(client)) { + if (DBG) + log("Removed cell broadcast subscription for MID " + messageIdentifier + + " from client " + client); + + if (clients.isEmpty()) { + mCellBroadcastSubscriptions.remove(messageIdentifier); + updateCellBroadcastConfig(); + } + return true; + } + + return false; + } + + private boolean updateCellBroadcastConfig() { + Set<Integer> messageIdentifiers = mCellBroadcastSubscriptions.keySet(); + + if (messageIdentifiers.size() > 0) { + SmsBroadcastConfigInfo[] configs = + new SmsBroadcastConfigInfo[messageIdentifiers.size()]; + int i = 0; + + for (int messageIdentifier : messageIdentifiers) { + configs[i++] = new SmsBroadcastConfigInfo(messageIdentifier, messageIdentifier, + SMS_CB_CODE_SCHEME_MIN, SMS_CB_CODE_SCHEME_MAX, true); + } + + return setCellBroadcastConfig(configs) && setCellBroadcastActivation(true); + } else { + return setCellBroadcastActivation(false); + } + } + + private boolean setCellBroadcastConfig(SmsBroadcastConfigInfo[] configs) { + if (DBG) + log("Calling setGsmBroadcastConfig with " + configs.length + " configurations"); + + synchronized (mLock) { + Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_CONFIG_DONE); + + mSuccess = false; + mPhone.mCM.setGsmBroadcastConfig(configs, response); + + try { + mLock.wait(); + } catch (InterruptedException e) { + log("interrupted while trying to set cell broadcast config"); + } + } + + return mSuccess; + } + + private boolean setCellBroadcastActivation(boolean activate) { + if (DBG) + log("Calling setCellBroadcastActivation(" + activate + ")"); + + synchronized (mLock) { + Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_ACTIVATION_DONE); + + mSuccess = false; + mPhone.mCM.setGsmBroadcastActivation(activate, response); + + try { + mLock.wait(); + } catch (InterruptedException e) { + log("interrupted while trying to set cell broadcast activation"); + } + } + + return mSuccess; + } + protected void log(String msg) { Log.d(LOG_TAG, "[SimSmsInterfaceManager] " + msg); } diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java new file mode 100644 index 0000000..5f27cfc --- /dev/null +++ b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2010 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; + +public class SmsCbHeader { + public static final int PDU_HEADER_LENGTH = 6; + + public final int geographicalScope; + + public final int messageCode; + + public final int updateNumber; + + public final int messageIdentifier; + + public final int dataCodingScheme; + + public final int pageIndex; + + public final int nrOfPages; + + public SmsCbHeader(byte[] pdu) throws IllegalArgumentException { + if (pdu == null || pdu.length < PDU_HEADER_LENGTH) { + throw new IllegalArgumentException("Illegal PDU"); + } + + geographicalScope = (pdu[0] & 0xc0) >> 6; + messageCode = ((pdu[0] & 0x3f) << 4) | ((pdu[1] & 0xf0) >> 4); + updateNumber = pdu[1] & 0x0f; + messageIdentifier = (pdu[2] << 8) | pdu[3]; + dataCodingScheme = pdu[4]; + + // Check for invalid page parameter + int pageIndex = (pdu[5] & 0xf0) >> 4; + int nrOfPages = pdu[5] & 0x0f; + + if (pageIndex == 0 || nrOfPages == 0 || pageIndex > nrOfPages) { + pageIndex = 1; + nrOfPages = 1; + } + + this.pageIndex = pageIndex; + this.nrOfPages = nrOfPages; + } +} diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java index 50dd402..9a3c476 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java @@ -930,6 +930,8 @@ public class SmsMessage extends SmsMessageBase{ // TP-Message-Type-Indicator // 9.2.3 case 0: + case 3: //GSM 03.40 9.2.3.1: MTI == 3 is Reserved. + //This should be processed in the same way as MTI == 0 (Deliver) parseSmsDeliver(p, firstByte); break; case 2: diff --git a/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java b/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java index 41e527c..b642541 100644..100755 --- a/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java +++ b/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java @@ -53,6 +53,7 @@ public class UsimPhoneBookManager extends Handler implements IccConstants { private ArrayList<byte[]> mIapFileRecord; private ArrayList<byte[]> mEmailFileRecord; private Map<Integer, ArrayList<String>> mEmailsForAdnRec; + private boolean mRefreshCache = false; private static final int EVENT_PBR_LOAD_DONE = 1; private static final int EVENT_USIM_ADN_LOAD_DONE = 2; @@ -91,11 +92,19 @@ public class UsimPhoneBookManager extends Handler implements IccConstants { mEmailFileRecord = null; mPbrFile = null; mIsPbrPresent = true; + mRefreshCache = false; } public ArrayList<AdnRecord> loadEfFilesFromUsim() { synchronized (mLock) { - if (!mPhoneBookRecords.isEmpty()) return mPhoneBookRecords; + if (!mPhoneBookRecords.isEmpty()) { + if (mRefreshCache) { + mRefreshCache = false; + refreshCache(); + } + return mPhoneBookRecords; + } + if (!mIsPbrPresent) return null; // Check if the PBR file is present in the cache, if not read it @@ -116,6 +125,20 @@ public class UsimPhoneBookManager extends Handler implements IccConstants { return mPhoneBookRecords; } + private void refreshCache() { + if (mPbrFile == null) return; + mPhoneBookRecords.clear(); + + int numRecs = mPbrFile.mFileIds.size(); + for (int i = 0; i < numRecs; i++) { + readAdnFileAndWait(i); + } + } + + public void invalidateCache() { + mRefreshCache = true; + } + private void readPbrFileAndWait() { mPhone.getIccFileHandler().loadEFLinearFixedAll(EF_PBR, obtainMessage(EVENT_PBR_LOAD_DONE)); try { diff --git a/telephony/java/com/android/internal/telephony/gsm/stk/package.html b/telephony/java/com/android/internal/telephony/gsm/stk/package.html deleted file mode 100644 index c285b57..0000000 --- a/telephony/java/com/android/internal/telephony/gsm/stk/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<HTML> -<BODY> -Provides classes for SIM Toolkit Service. -</BODY> -</HTML> diff --git a/telephony/tests/telephonytests/src/android/telephony/PhoneNumberUtilsTest.java b/telephony/tests/telephonytests/src/android/telephony/PhoneNumberUtilsTest.java index de59b81..67df510 100644 --- a/telephony/tests/telephonytests/src/android/telephony/PhoneNumberUtilsTest.java +++ b/telephony/tests/telephonytests/src/android/telephony/PhoneNumberUtilsTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.telephony; +package android.telephony; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; diff --git a/telephony/tests/telephonytests/src/android/telephony/PhoneNumberWatcherTest.java b/telephony/tests/telephonytests/src/android/telephony/PhoneNumberWatcherTest.java index 88eaecd..4c7ebca 100644 --- a/telephony/tests/telephonytests/src/android/telephony/PhoneNumberWatcherTest.java +++ b/telephony/tests/telephonytests/src/android/telephony/PhoneNumberWatcherTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.internal.telephony; +package android.telephony; import android.telephony.PhoneNumberFormattingTextWatcher; import android.test.suitebuilder.annotation.SmallTest; diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java new file mode 100644 index 0000000..7136ea0 --- /dev/null +++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2010 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; + +import android.telephony.SmsCbMessage; +import android.test.AndroidTestCase; + +/** + * Test cases for basic SmsCbMessage operations + */ +public class GsmSmsCbTest extends AndroidTestCase { + + private void doTestGeographicalScopeValue(byte[] pdu, byte b, int expectedGs) { + pdu[0] = b; + SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu); + + assertEquals("Unexpected geographical scope decoded", expectedGs, msg + .getGeographicalScope()); + } + + public void testCreateNullPdu() { + SmsCbMessage msg = SmsCbMessage.createFromPdu(null); + + assertNull("createFromPdu(byte[] with null pdu should return null", msg); + } + + public void testCreateTooShortPdu() { + byte[] pdu = new byte[4]; + SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu); + + assertNull("createFromPdu(byte[] with too short pdu should return null", msg); + } + + public void testGetGeographicalScope() { + byte[] pdu = { + (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x40, (byte)0x11, (byte)0x41, + (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6, + (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70, + (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5, + (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69, + (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9, + (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75, + (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69, + (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, + (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, + (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, + (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, + (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00 + }; + + doTestGeographicalScopeValue(pdu, (byte)0x00, + SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE); + doTestGeographicalScopeValue(pdu, (byte)0x40, SmsCbMessage.GEOGRAPHICAL_SCOPE_PLMN_WIDE); + doTestGeographicalScopeValue(pdu, (byte)0x80, SmsCbMessage.GEOGRAPHICAL_SCOPE_LA_WIDE); + doTestGeographicalScopeValue(pdu, (byte)0xC0, SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE); + } + + public void testGetMessageBody7Bit() { + byte[] pdu = { + (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x40, (byte)0x11, (byte)0x41, + (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6, + (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70, + (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5, + (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69, + (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9, + (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75, + (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69, + (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, + (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, + (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, + (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, + (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00 + }; + SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu); + + assertEquals("Unexpected 7-bit string decoded", + "A GSM default alphabet message with carriage return padding", + msg.getMessageBody()); + } + + public void testGetMessageBody7BitFull() { + byte[] pdu = { + (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x40, (byte)0x11, (byte)0x41, + (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6, + (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70, + (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5, + (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xC4, (byte)0xE5, + (byte)0xB4, (byte)0xFB, (byte)0x0C, (byte)0x2A, (byte)0xE3, (byte)0xC3, (byte)0x63, + (byte)0x3A, (byte)0x3B, (byte)0x0F, (byte)0xCA, (byte)0xCD, (byte)0x40, (byte)0x63, + (byte)0x74, (byte)0x58, (byte)0x1E, (byte)0x1E, (byte)0xD3, (byte)0xCB, (byte)0xF2, + (byte)0x39, (byte)0x88, (byte)0xFD, (byte)0x76, (byte)0x9F, (byte)0x59, (byte)0xA0, + (byte)0x76, (byte)0x39, (byte)0xEC, (byte)0x4E, (byte)0xBB, (byte)0xCF, (byte)0x20, + (byte)0x3A, (byte)0xBA, (byte)0x2C, (byte)0x2F, (byte)0x83, (byte)0xD2, (byte)0x73, + (byte)0x90, (byte)0xFB, (byte)0x0D, (byte)0x82, (byte)0x87, (byte)0xC9, (byte)0xE4, + (byte)0xB4, (byte)0xFB, (byte)0x1C, (byte)0x02 + }; + SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu); + + assertEquals( + "Unexpected 7-bit string decoded", + "A GSM default alphabet message being exactly 93 characters long, " + + "meaning there is no padding!", + msg.getMessageBody()); + } + + public void testGetMessageBody7BitWithLanguage() { + byte[] pdu = { + (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x04, (byte)0x11, (byte)0x41, + (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6, + (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70, + (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5, + (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69, + (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9, + (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75, + (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69, + (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, + (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, + (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, + (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, + (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00 + }; + SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu); + + assertEquals("Unexpected 7-bit string decoded", + "A GSM default alphabet message with carriage return padding", + msg.getMessageBody()); + + assertEquals("Unexpected language indicator decoded", "es", msg.getLanguageCode()); + } + + public void testGetMessageBody7BitWithLanguageInBody() { + byte[] pdu = { + (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x10, (byte)0x11, (byte)0x73, + (byte)0x7B, (byte)0x23, (byte)0x08, (byte)0x3A, (byte)0x4E, (byte)0x9B, (byte)0x20, + (byte)0x72, (byte)0xD9, (byte)0x1C, (byte)0xAE, (byte)0xB3, (byte)0xE9, (byte)0xA0, + (byte)0x30, (byte)0x1B, (byte)0x8E, (byte)0x0E, (byte)0x8B, (byte)0xCB, (byte)0x74, + (byte)0x50, (byte)0xBB, (byte)0x3C, (byte)0x9F, (byte)0x87, (byte)0xCF, (byte)0x65, + (byte)0xD0, (byte)0x3D, (byte)0x4D, (byte)0x47, (byte)0x83, (byte)0xC6, (byte)0x61, + (byte)0xB9, (byte)0x3C, (byte)0x1D, (byte)0x3E, (byte)0x97, (byte)0x41, (byte)0xF2, + (byte)0x32, (byte)0xBD, (byte)0x2E, (byte)0x77, (byte)0x83, (byte)0xE0, (byte)0x61, + (byte)0x32, (byte)0x39, (byte)0xED, (byte)0x3E, (byte)0x37, (byte)0x1A, (byte)0x8D, + (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, + (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, + (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, + (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00 + }; + SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu); + + assertEquals("Unexpected 7-bit string decoded", + "A GSM default alphabet message with carriage return padding", + msg.getMessageBody()); + + assertEquals("Unexpected language indicator decoded", "sv", msg.getLanguageCode()); + } + + public void testGetMessageBody8Bit() { + byte[] pdu = { + (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x44, (byte)0x11, (byte)0x41, + (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41, + (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41, + (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41, + (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41, + (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41, + (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41, + (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41, + (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41, + (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41, + (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41, + (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41, + (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45 + }; + SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu); + + assertEquals("8-bit message body should be empty", "", msg.getMessageBody()); + } + + public void testGetMessageBodyUcs2() { + byte[] pdu = { + (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x48, (byte)0x11, (byte)0x00, + (byte)0x41, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x55, (byte)0x00, (byte)0x43, + (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x20, (byte)0x00, + (byte)0x6D, (byte)0x00, (byte)0x65, (byte)0x00, (byte)0x73, (byte)0x00, (byte)0x73, + (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x67, (byte)0x00, (byte)0x65, (byte)0x00, + (byte)0x20, (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x6F, (byte)0x00, (byte)0x6E, + (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x69, (byte)0x00, + (byte)0x6E, (byte)0x00, (byte)0x69, (byte)0x00, (byte)0x6E, (byte)0x00, (byte)0x67, + (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x20, (byte)0x04, + (byte)0x34, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x68, + (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x72, (byte)0x00, (byte)0x61, (byte)0x00, + (byte)0x63, (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x65, (byte)0x00, (byte)0x72, + (byte)0x00, (byte)0x0D, (byte)0x00, (byte)0x0D + }; + SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu); + + assertEquals("Unexpected 7-bit string decoded", + "A UCS2 message containing a \u0434 character", msg.getMessageBody()); + } + + public void testGetMessageBodyUcs2WithLanguageInBody() { + byte[] pdu = { + (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x11, (byte)0x11, (byte)0x78, + (byte)0x3C, (byte)0x00, (byte)0x41, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x55, + (byte)0x00, (byte)0x43, (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x32, (byte)0x00, + (byte)0x20, (byte)0x00, (byte)0x6D, (byte)0x00, (byte)0x65, (byte)0x00, (byte)0x73, + (byte)0x00, (byte)0x73, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x67, (byte)0x00, + (byte)0x65, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x6F, + (byte)0x00, (byte)0x6E, (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x61, (byte)0x00, + (byte)0x69, (byte)0x00, (byte)0x6E, (byte)0x00, (byte)0x69, (byte)0x00, (byte)0x6E, + (byte)0x00, (byte)0x67, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x61, (byte)0x00, + (byte)0x20, (byte)0x04, (byte)0x34, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x63, + (byte)0x00, (byte)0x68, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x72, (byte)0x00, + (byte)0x61, (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x65, + (byte)0x00, (byte)0x72, (byte)0x00, (byte)0x0D + }; + SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu); + + assertEquals("Unexpected 7-bit string decoded", + "A UCS2 message containing a \u0434 character", msg.getMessageBody()); + + assertEquals("Unexpected language indicator decoded", "xx", msg.getLanguageCode()); + } + + public void testGetMessageIdentifier() { + byte[] pdu = { + (byte)0xC0, (byte)0x00, (byte)0x30, (byte)0x39, (byte)0x40, (byte)0x11, (byte)0x41, + (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6, + (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70, + (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5, + (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69, + (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9, + (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75, + (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69, + (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, + (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, + (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, + (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, + (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00 + }; + + SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu); + + assertEquals("Unexpected message identifier decoded", 12345, msg.getMessageIdentifier()); + } + + public void testGetMessageCode() { + byte[] pdu = { + (byte)0x2A, (byte)0xA5, (byte)0x30, (byte)0x39, (byte)0x40, (byte)0x11, (byte)0x41, + (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6, + (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70, + (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5, + (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69, + (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9, + (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75, + (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69, + (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, + (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, + (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, + (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, + (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00 + }; + + SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu); + + assertEquals("Unexpected message code decoded", 682, msg.getMessageCode()); + } + + public void testGetUpdateNumber() { + byte[] pdu = { + (byte)0x2A, (byte)0xA5, (byte)0x30, (byte)0x39, (byte)0x40, (byte)0x11, (byte)0x41, + (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6, + (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70, + (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5, + (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69, + (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9, + (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75, + (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69, + (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, + (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, + (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, + (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, + (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00 + }; + + SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu); + + assertEquals("Unexpected update number decoded", 5, msg.getUpdateNumber()); + } +} diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/Wap230WspContentTypeTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/Wap230WspContentTypeTest.java new file mode 100644 index 0000000..d31b294 --- /dev/null +++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/Wap230WspContentTypeTest.java @@ -0,0 +1,853 @@ +/* + * Copyright (C) 2010 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; + +import com.android.internal.telephony.WspTypeDecoder; +import com.android.internal.util.HexDump; + +import java.io.ByteArrayOutputStream; +import java.util.HashMap; +import java.util.Map; + +import junit.framework.TestCase; + +public class Wap230WspContentTypeTest extends TestCase { + + public static final Map<Integer, String> WELL_KNOWN_SHORT_MIME_TYPES + = new HashMap<Integer, String>(); + public static final Map<Integer, String> WELL_KNOWN_LONG_MIME_TYPES + = new HashMap<Integer, String>(); + public static final Map<Integer, String> WELL_KNOWN_PARAMETERS + = new HashMap<Integer, String>(); + + static { + WELL_KNOWN_SHORT_MIME_TYPES.put(0x00, "*/*"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x01, "text/*"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x02, "text/html"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x03, "text/plain"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x04, "text/x-hdml"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x05, "text/x-ttml"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x06, "text/x-vCalendar"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x07, "text/x-vCard"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x08, "text/vnd.wap.wml"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x09, "text/vnd.wap.wmlscript"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x0A, "text/vnd.wap.wta-event"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x0B, "multipart/*"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x0C, "multipart/mixed"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x0D, "multipart/form-data"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x0E, "multipart/byterantes"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x0F, "multipart/alternative"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x10, "application/*"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x11, "application/java-vm"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x12, "application/x-www-form-urlencoded"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x13, "application/x-hdmlc"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x14, "application/vnd.wap.wmlc"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x15, "application/vnd.wap.wmlscriptc"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x16, "application/vnd.wap.wta-eventc"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x17, "application/vnd.wap.uaprof"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x18, "application/vnd.wap.wtls-ca-certificate"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x19, "application/vnd.wap.wtls-user-certificate"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x1A, "application/x-x509-ca-cert"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x1B, "application/x-x509-user-cert"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x1C, "image/*"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x1D, "image/gif"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x1E, "image/jpeg"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x1F, "image/tiff"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x20, "image/png"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x21, "image/vnd.wap.wbmp"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x22, "application/vnd.wap.multipart.*"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x23, "application/vnd.wap.multipart.mixed"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x24, "application/vnd.wap.multipart.form-data"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x25, "application/vnd.wap.multipart.byteranges"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x26, "application/vnd.wap.multipart.alternative"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x27, "application/xml"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x28, "text/xml"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x29, "application/vnd.wap.wbxml"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x2A, "application/x-x968-cross-cert"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x2B, "application/x-x968-ca-cert"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x2C, "application/x-x968-user-cert"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x2D, "text/vnd.wap.si"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x2E, "application/vnd.wap.sic"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x2F, "text/vnd.wap.sl"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x30, "application/vnd.wap.slc"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x31, "text/vnd.wap.co"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x32, "application/vnd.wap.coc"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x33, "application/vnd.wap.multipart.related"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x34, "application/vnd.wap.sia"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x35, "text/vnd.wap.connectivity-xml"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x36, "application/vnd.wap.connectivity-wbxml"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x37, "application/pkcs7-mime"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x38, "application/vnd.wap.hashed-certificate"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x39, "application/vnd.wap.signed-certificate"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x3A, "application/vnd.wap.cert-response"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x3B, "application/xhtml+xml"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x3C, "application/wml+xml"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x3D, "text/css"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x3E, "application/vnd.wap.mms-message"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x3F, "application/vnd.wap.rollover-certificate"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x40, "application/vnd.wap.locc+wbxml"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x41, "application/vnd.wap.loc+xml"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x42, "application/vnd.syncml.dm+wbxml"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x43, "application/vnd.syncml.dm+xml"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x44, "application/vnd.syncml.notification"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x45, "application/vnd.wap.xhtml+xml"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x46, "application/vnd.wv.csp.cir"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x47, "application/vnd.oma.dd+xml"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x48, "application/vnd.oma.drm.message"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x49, "application/vnd.oma.drm.content"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x4A, "application/vnd.oma.drm.rights+xml"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x4B, "application/vnd.oma.drm.rights+wbxml"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x4C, "application/vnd.wv.csp+xml"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x4D, "application/vnd.wv.csp+wbxml"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x4E, "application/vnd.syncml.ds.notification"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x4F, "audio/*"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x50, "video/*"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x51, "application/vnd.oma.dd2+xml"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x52, "application/mikey"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x53, "application/vnd.oma.dcd"); + WELL_KNOWN_SHORT_MIME_TYPES.put(0x54, "application/vnd.oma.dcdc"); + + WELL_KNOWN_LONG_MIME_TYPES.put(0x0201, "application/vnd.uplanet.cacheop-wbxml"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x0202, "application/vnd.uplanet.signal"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x0203, "application/vnd.uplanet.alert-wbxml"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x0204, "application/vnd.uplanet.list-wbxml"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x0205, "application/vnd.uplanet.listcmd-wbxml"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x0206, "application/vnd.uplanet.channel-wbxml"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x0207, "application/vnd.uplanet.provisioning-status-uri"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x0208, "x-wap.multipart/vnd.uplanet.header-set"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x0209, "application/vnd.uplanet.bearer-choice-wbxml"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x020A, "application/vnd.phonecom.mmc-wbxml"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x020B, "application/vnd.nokia.syncset+wbxml"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x020C, "image/x-up-wpng"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x0300, "application/iota.mmc-wbxml"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x0301, "application/iota.mmc-xml"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x0302, "application/vnd.syncml+xml"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x0303, "application/vnd.syncml+wbxml"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x0304, "text/vnd.wap.emn+xml"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x0305, "text/calendar"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x0306, "application/vnd.omads-email+xml"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x0307, "application/vnd.omads-file+xml"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x0308, "application/vnd.omads-folder+xml"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x0309, "text/directory;profile=vCard"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x030A, "application/vnd.wap.emn+wbxml"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x030B, "application/vnd.nokia.ipdc-purchase-response"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x030C, "application/vnd.motorola.screen3+xml"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x030D, "application/vnd.motorola.screen3+gzip"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x030E, "application/vnd.cmcc.setting+wbxml"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x030F, "application/vnd.cmcc.bombing+wbxml"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x0310, "application/vnd.docomo.pf"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x0311, "application/vnd.docomo.ub"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x0312, "application/vnd.omaloc-supl-init"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x0313, "application/vnd.oma.group-usage-list+xml"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x0314, "application/oma-directory+xml"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x0315, "application/vnd.docomo.pf2"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x0316, "application/vnd.oma.drm.roap-trigger+wbxml"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x0317, "application/vnd.sbm.mid2"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x0318, "application/vnd.wmf.bootstrap"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x0319, "application/vnc.cmcc.dcd+xml"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x031A, "application/vnd.sbm.cid"); + WELL_KNOWN_LONG_MIME_TYPES.put(0x031B, "application/vnd.oma.bcast.provisioningtrigger"); + + WELL_KNOWN_PARAMETERS.put(0x00, "Q"); + WELL_KNOWN_PARAMETERS.put(0x01, "Charset"); + WELL_KNOWN_PARAMETERS.put(0x02, "Level"); + WELL_KNOWN_PARAMETERS.put(0x03, "Type"); + WELL_KNOWN_PARAMETERS.put(0x07, "Differences"); + WELL_KNOWN_PARAMETERS.put(0x08, "Padding"); + WELL_KNOWN_PARAMETERS.put(0x09, "Type"); + WELL_KNOWN_PARAMETERS.put(0x0E, "Max-Age"); + WELL_KNOWN_PARAMETERS.put(0x10, "Secure"); + WELL_KNOWN_PARAMETERS.put(0x11, "SEC"); + WELL_KNOWN_PARAMETERS.put(0x12, "MAC"); + WELL_KNOWN_PARAMETERS.put(0x13, "Creation-date"); + WELL_KNOWN_PARAMETERS.put(0x14, "Modification-date"); + WELL_KNOWN_PARAMETERS.put(0x15, "Read-date"); + WELL_KNOWN_PARAMETERS.put(0x16, "Size"); + WELL_KNOWN_PARAMETERS.put(0x17, "Name"); + WELL_KNOWN_PARAMETERS.put(0x18, "Filename"); + WELL_KNOWN_PARAMETERS.put(0x19, "Start"); + WELL_KNOWN_PARAMETERS.put(0x1A, "Start-info"); + WELL_KNOWN_PARAMETERS.put(0x1B, "Comment"); + WELL_KNOWN_PARAMETERS.put(0x1C, "Domain"); + WELL_KNOWN_PARAMETERS.put(0x1D, "Path"); + + } + + final int WSP_DEFINED_SHORT_MIME_TYPE_COUNT = 85; + final int WSP_DEFINED_LONG_MIME_TYPE_COUNT = 85; + + private static final byte WSP_STRING_TERMINATOR = 0x00; + private static final byte WSP_SHORT_INTEGER_MASK = (byte) 0x80; + private static final byte WSP_LENGTH_QUOTE = 0x1F; + private static final byte WSP_QUOTE = 0x22; + + private static final short LONG_MIME_TYPE_OMA_DIRECTORY_XML = 0x0314; + private static final short LONG_MIME_TYPE_UNASSIGNED = 0x052C; + + private static final byte SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE = 0x3F; + private static final byte SHORT_MIME_TYPE_UNASSIGNED = 0x60; + + private static final String STRING_MIME_TYPE_ROLLOVER_CERTIFICATE + = "application/vnd.wap.rollover-certificate"; + + private static final byte TYPED_PARAM_Q = 0x00; + private static final byte TYPED_PARAM_DOMAIN = 0x1C; + private static final byte PARAM_UNASSIGNED = 0x42; + private static final byte PARAM_NO_VALUE = 0x00; + private static final byte TYPED_PARAM_SEC = 0x11; + private static final byte TYPED_PARAM_MAC = 0x12; + + public void testHasExpectedNumberOfShortMimeTypes() { + assertEquals(WSP_DEFINED_SHORT_MIME_TYPE_COUNT, WELL_KNOWN_SHORT_MIME_TYPES.size()); + } + + public void testHasExpectedNumberOfLongMimeTypes() { + assertEquals(WSP_DEFINED_LONG_MIME_TYPE_COUNT, WELL_KNOWN_LONG_MIME_TYPES.size()); + } + + public void testWellKnownShortIntegerMimeTypeValues() { + + for (int value : Wap230WspContentTypeTest.WELL_KNOWN_SHORT_MIME_TYPES.keySet()) { + WspTypeDecoder unit = new WspTypeDecoder( + HexDump.toByteArray((byte) (value | WSP_SHORT_INTEGER_MASK))); + assertTrue(unit.decodeContentType(0)); + String mimeType = unit.getValueString(); + int wellKnownValue = (int) unit.getValue32(); + assertEquals(Wap230WspContentTypeTest.WELL_KNOWN_SHORT_MIME_TYPES.get(value), mimeType); + assertEquals(value, wellKnownValue); + assertEquals(1, unit.getDecodedDataLength()); + } + } + + public void testWellKnownLongIntegerMimeTypeValues() { + byte headerLength = 3; + byte typeLength = 2; + for (int value : Wap230WspContentTypeTest.WELL_KNOWN_SHORT_MIME_TYPES.keySet()) { + byte[] data = new byte[10]; + data[0] = headerLength; + data[1] = typeLength; + data[2] = (byte) (value >> 8); + data[3] = (byte) (value & 0xFF); + WspTypeDecoder unit = new WspTypeDecoder(data); + assertTrue(unit.decodeContentType(0)); + String mimeType = unit.getValueString(); + int wellKnownValue = (int) unit.getValue32(); + assertEquals(Wap230WspContentTypeTest.WELL_KNOWN_SHORT_MIME_TYPES.get(value), mimeType); + assertEquals(value, wellKnownValue); + assertEquals(4, unit.getDecodedDataLength()); + } + } + + public void testDecodeReturnsFalse_WhenOnlyAZeroBytePresent() { + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(0x00); + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertFalse(unit.decodeContentType(0)); + } + + public void testConstrainedMediaExtensionMedia() throws Exception { + + String testType = "application/wibble"; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(testType.getBytes("US-ASCII")); + out.write(WSP_STRING_TERMINATOR); + + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertTrue(unit.decodeContentType(0)); + String mimeType = unit.getValueString(); + assertEquals(testType, mimeType); + assertEquals(-1, unit.getValue32()); + assertEquals(19, unit.getDecodedDataLength()); + } + + public void testGeneralFormShortLengthExtensionMedia() throws Exception { + + String testType = "12345678901234567890123456789"; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(testType.length() + 1); + out.write(testType.getBytes("US-ASCII")); + out.write(WSP_STRING_TERMINATOR); + + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertTrue(unit.decodeContentType(0)); + + String mimeType = unit.getValueString(); + assertEquals(testType, mimeType); + assertEquals(-1, unit.getValue32()); + assertEquals(31, unit.getDecodedDataLength()); + } + + public void testGeneralFormShortLengthWellKnownShortInteger() { + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(0x01); + out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK); + + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertTrue(unit.decodeContentType(0)); + + String mimeType = unit.getValueString(); + assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType); + assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32()); + assertEquals(2, unit.getDecodedDataLength()); + + } + + public void testGeneralFormShortLengthWellKnownShortIntegerWithUnknownValue() { + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(0x01); + out.write(SHORT_MIME_TYPE_UNASSIGNED | WSP_SHORT_INTEGER_MASK); + + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertTrue(unit.decodeContentType(0)); + + String mimeType = unit.getValueString(); + assertNull(mimeType); + assertEquals(SHORT_MIME_TYPE_UNASSIGNED, unit.getValue32()); + assertEquals(2, unit.getDecodedDataLength()); + + } + + public void testGeneralFormShortLengthWellKnownLongInteger() { + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + out.write(0x03); // header length + out.write(0x02); // type length (2 octets) + out.write(LONG_MIME_TYPE_OMA_DIRECTORY_XML >> 8); + out.write(LONG_MIME_TYPE_OMA_DIRECTORY_XML & 0xFF); + + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertTrue(unit.decodeContentType(0)); + + String mimeType = unit.getValueString(); + + assertEquals("application/oma-directory+xml", mimeType); + assertEquals(LONG_MIME_TYPE_OMA_DIRECTORY_XML, unit.getValue32()); + assertEquals(4, unit.getDecodedDataLength()); + } + + public void testGeneralFormShortLengthWellKnownLongIntegerWithUnknownValue() { + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + out.write(0x03); // Value-length, short-length + out.write(0x02); // long-integer length (2 octets) + out.write(LONG_MIME_TYPE_UNASSIGNED >> 8); + out.write(LONG_MIME_TYPE_UNASSIGNED & 0xFF); + + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertTrue(unit.decodeContentType(0)); + + String mimeType = unit.getValueString(); + + assertNull(mimeType); + assertEquals(LONG_MIME_TYPE_UNASSIGNED, unit.getValue32()); + assertEquals(4, unit.getDecodedDataLength()); + + } + + public void testGeneralFormLengthQuoteWellKnownShortInteger() { + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + out.write(WSP_LENGTH_QUOTE); + out.write(0x01); // Length as UINTVAR + out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK); + + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertTrue(unit.decodeContentType(0)); + + String mimeType = unit.getValueString(); + assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType); + assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32()); + assertEquals(3, unit.getDecodedDataLength()); + + } + + public void testGeneralFormLengthQuoteWellKnownShortIntegerWithUnknownValue() { + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + out.write(WSP_LENGTH_QUOTE); + out.write(0x01); // Length as UINTVAR + out.write(SHORT_MIME_TYPE_UNASSIGNED | WSP_SHORT_INTEGER_MASK); + + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertTrue(unit.decodeContentType(0)); + + String mimeType = unit.getValueString(); + assertNull(mimeType); + assertEquals(SHORT_MIME_TYPE_UNASSIGNED, unit.getValue32()); + assertEquals(3, unit.getDecodedDataLength()); + } + + public void testGeneralFormLengthQuoteWellKnownLongInteger() { + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + out.write(WSP_LENGTH_QUOTE); + out.write(0x03); // Length as UINTVAR + out.write(0x02); // long-integer length (2 octets) + out.write(LONG_MIME_TYPE_OMA_DIRECTORY_XML >> 8); + out.write(LONG_MIME_TYPE_OMA_DIRECTORY_XML & 0xFF); + + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertTrue(unit.decodeContentType(0)); + + String mimeType = unit.getValueString(); + + assertEquals("application/oma-directory+xml", mimeType); + assertEquals(LONG_MIME_TYPE_OMA_DIRECTORY_XML, unit.getValue32()); + assertEquals(5, unit.getDecodedDataLength()); + + } + + public void testGeneralFormLengthQuoteWellKnownLongIntegerWithUnknownValue() { + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + out.write(WSP_LENGTH_QUOTE); + out.write(0x03); // Length as UINTVAR + out.write(0x02); // long-integer length (2 octets) + out.write(LONG_MIME_TYPE_UNASSIGNED >> 8); + out.write(LONG_MIME_TYPE_UNASSIGNED & 0xFF); + + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertTrue(unit.decodeContentType(0)); + + String mimeType = unit.getValueString(); + + assertNull(mimeType); + assertEquals(LONG_MIME_TYPE_UNASSIGNED, unit.getValue32()); + assertEquals(5, unit.getDecodedDataLength()); + + } + + public void testGeneralFormLengthQuoteExtensionMedia() throws Exception { + + String testType = "application/wibble"; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + out.write(WSP_LENGTH_QUOTE); + out.write(testType.length() + 1); // Length as UINTVAR + + out.write(testType.getBytes("US-ASCII")); + out.write(WSP_STRING_TERMINATOR); + + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertTrue(unit.decodeContentType(0)); + + String mimeType = unit.getValueString(); + + assertEquals(testType, mimeType); + assertEquals(-1, unit.getValue32()); + assertEquals(21, unit.getDecodedDataLength()); + + } + + public void testGeneralFormLengthQuoteExtensionMediaWithNiceLongMimeType() throws Exception { + + String testType = + "01234567890123456789012345678901234567890123456789012345678901234567890123456789" + +"01234567890123456789012345678901234567890123456789012345678901234567890123456789"; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + out.write(WSP_LENGTH_QUOTE); + out.write(0x81); // Length as UINTVAR (161 decimal, 0xA1), 2 bytes + out.write(0x21); + + out.write(testType.getBytes("US-ASCII")); + out.write(WSP_STRING_TERMINATOR); + + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertTrue(unit.decodeContentType(0)); + + String mimeType = unit.getValueString(); + + assertEquals(testType, mimeType); + assertEquals(-1, unit.getValue32()); + assertEquals(164, unit.getDecodedDataLength()); + + } + + public void testConstrainedMediaExtensionMediaWithSpace() throws Exception { + + String testType = " application/wibble"; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(testType.getBytes("US-ASCII")); + out.write(WSP_STRING_TERMINATOR); + + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertTrue(unit.decodeContentType(0)); + + String mimeType = unit.getValueString(); + + assertEquals(testType, mimeType); + assertEquals(-1, unit.getValue32()); + assertEquals(20, unit.getDecodedDataLength()); + + } + + public void testTypedParamWellKnownShortIntegerNoValue() { + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(0x03); // Value-length, short-length + out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK); + out.write(TYPED_PARAM_DOMAIN | WSP_SHORT_INTEGER_MASK); + out.write(PARAM_NO_VALUE); + + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertTrue(unit.decodeContentType(0)); + + String mimeType = unit.getValueString(); + + assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType); + assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32()); + + assertEquals(4, unit.getDecodedDataLength()); + + Map<String, String> params = unit.getContentParameters(); + assertEquals(null, params.get("Domain")); + + } + + public void testTypedParamWellKnownShortIntegerTokenText() throws Exception { + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(0x14); // Value-length, short-length + out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK); + out.write(TYPED_PARAM_DOMAIN | WSP_SHORT_INTEGER_MASK); + out.write("wdstechnology.com".getBytes("US-ASCII")); + out.write(WSP_STRING_TERMINATOR); + + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertTrue(unit.decodeContentType(0)); + + String mimeType = unit.getValueString(); + + assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType); + assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32()); + + assertEquals(out.toByteArray().length, unit.getDecodedDataLength()); + + Map<String, String> params = unit.getContentParameters(); + assertEquals("wdstechnology.com", params.get("Domain")); + + } + + public void testTypedParamWellKnownLongIntegerTokenText() throws Exception { + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(0x15); + out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK); + out.write(0x01); + out.write(TYPED_PARAM_DOMAIN); + out.write("wdstechnology.com".getBytes("US-ASCII")); + out.write(WSP_STRING_TERMINATOR); + + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertTrue(unit.decodeContentType(0)); + + String mimeType = unit.getValueString(); + + assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType); + assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32()); + + assertEquals(22, unit.getDecodedDataLength()); + + Map<String, String> params = unit.getContentParameters(); + assertEquals("wdstechnology.com", params.get("Domain")); + + } + + public void testTypedParamWellKnownShortIntegerQuotedText() throws Exception { + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(0x15); + out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK); + out.write(TYPED_PARAM_DOMAIN | WSP_SHORT_INTEGER_MASK); + out.write(WSP_QUOTE); + out.write("wdstechnology.com".getBytes("US-ASCII")); + out.write(WSP_STRING_TERMINATOR); + + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertTrue(unit.decodeContentType(0)); + + String mimeType = unit.getValueString(); + + assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType); + assertEquals(0x3F, unit.getValue32()); + assertEquals(22, unit.getDecodedDataLength()); + + Map<String, String> params = unit.getContentParameters(); + assertEquals("wdstechnology.com", params.get("Domain")); + + } + + public void testTypedParamWellKnownShortIntegerCompactIntegerValue() { + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(0x3); + out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK); + out.write(TYPED_PARAM_SEC | WSP_SHORT_INTEGER_MASK); + out.write(0x01 | WSP_SHORT_INTEGER_MASK); + + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertTrue(unit.decodeContentType(0)); + + String mimeType = unit.getValueString(); + + assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType); + assertEquals(0x3F, unit.getValue32()); + assertEquals(4, unit.getDecodedDataLength()); + + Map<String, String> params = unit.getContentParameters(); + assertEquals("1", params.get("SEC")); + + } + + public void testTypedParamWellKnownShortIntegerMultipleParameters() throws Exception { + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(0x0B); + out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK); + out.write(TYPED_PARAM_SEC | WSP_SHORT_INTEGER_MASK); + out.write(0x01 | WSP_SHORT_INTEGER_MASK); + out.write(TYPED_PARAM_MAC | WSP_SHORT_INTEGER_MASK); + out.write(WSP_QUOTE); + out.write("imapc".getBytes("US-ASCII")); + out.write(WSP_STRING_TERMINATOR); + + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertTrue(unit.decodeContentType(0)); + + String mimeType = unit.getValueString(); + + assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType); + assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32()); + assertEquals(12, unit.getDecodedDataLength()); + + Map<String, String> params = unit.getContentParameters(); + assertEquals("1", params.get("SEC")); + assertEquals("imapc", params.get("MAC")); + } + + public void testUntypedParamIntegerValueShortInteger() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(0x0A); + out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK); + out.write("MYPARAM".getBytes("US-ASCII")); + out.write(WSP_STRING_TERMINATOR); // EOS + out.write(0x45 | WSP_SHORT_INTEGER_MASK); + + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertTrue(unit.decodeContentType(0)); + + String mimeType = unit.getValueString(); + + assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType); + assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32()); + assertEquals(11, unit.getDecodedDataLength()); + + Map<String, String> params = unit.getContentParameters(); + assertEquals("69", params.get("MYPARAM")); + } + + public void testUntypedParamIntegerValueLongInteger() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(0x0C); + out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK); + out.write("MYPARAM".getBytes("US-ASCII")); + out.write(WSP_STRING_TERMINATOR); + out.write(0x02); // Short Length + out.write(0x42); // Long Integer byte 1 + out.write(0x69); // Long Integer byte 2 + + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertTrue(unit.decodeContentType(0)); + + String mimeType = unit.getValueString(); + + assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType); + assertEquals(0x3F, unit.getValue32()); + assertEquals(13, unit.getDecodedDataLength()); + + Map<String, String> params = unit.getContentParameters(); + assertEquals("17001", params.get("MYPARAM")); + } + + public void testUntypedParamTextNoValue() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(0x0A); + out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK); + out.write("MYPARAM".getBytes("US-ASCII")); + out.write(WSP_STRING_TERMINATOR); + out.write(PARAM_NO_VALUE); + + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertTrue(unit.decodeContentType(0)); + String mimeType = unit.getValueString(); + + assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType); + assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32()); + assertEquals(11, unit.getDecodedDataLength()); + + Map<String, String> params = unit.getContentParameters(); + assertEquals(null, params.get("MYPARAM")); + + } + + public void testUntypedParamTextTokenText() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(0x11); + out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK); + out.write("MYPARAM".getBytes("US-ASCII")); + out.write(WSP_STRING_TERMINATOR); + out.write("myvalue".getBytes("US-ASCII")); + out.write(WSP_STRING_TERMINATOR); + + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertTrue(unit.decodeContentType(0)); + String mimeType = unit.getValueString(); + + assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType); + assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32()); + assertEquals(18, unit.getDecodedDataLength()); + + Map<String, String> params = unit.getContentParameters(); + assertEquals("myvalue", params.get("MYPARAM")); + } + + public void testUntypedParamTextQuotedString() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(0x11); + out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK); + out.write("MYPARAM".getBytes("US-ASCII")); + out.write(WSP_STRING_TERMINATOR); + out.write(WSP_QUOTE); + out.write("myvalue".getBytes("US-ASCII")); + out.write(WSP_STRING_TERMINATOR); + + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertTrue(unit.decodeContentType(0)); + String mimeType = unit.getValueString(); + + assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType); + assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32()); + assertEquals(19, unit.getDecodedDataLength()); + + Map<String, String> params = unit.getContentParameters(); + assertEquals("myvalue", params.get("MYPARAM")); + + } + + public void testDecodesReturnsFalse_ForParamWithMissingValue() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(0x09); + out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK); + out.write("MYPARAM".getBytes("US-ASCII")); + out.write(WSP_STRING_TERMINATOR); + + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertFalse(unit.decodeContentType(0)); + } + + public void testTypedParamTextQValue() { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(0x04); + out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK); + out.write(TYPED_PARAM_Q); + out.write(0x83); // Q value byte 1 + out.write(0x31); // Q value byte 2 + + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertTrue(unit.decodeContentType(0)); + String mimeType = unit.getValueString(); + + assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType); + assertEquals(0x3F, unit.getValue32()); + assertEquals(5, unit.getDecodedDataLength()); + + Map<String, String> params = unit.getContentParameters(); + assertEquals("433", params.get("Q")); + + } + + public void testTypedParamUnassignedWellKnownShortIntegerTokenText() throws Exception { + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(0x14); + out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK); + out.write(PARAM_UNASSIGNED | WSP_SHORT_INTEGER_MASK); + out.write("wdstechnology.com".getBytes("US-ASCII")); + out.write(WSP_STRING_TERMINATOR); + + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertTrue(unit.decodeContentType(0)); + + String mimeType = unit.getValueString(); + + assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType); + assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32()); + + assertEquals(21, unit.getDecodedDataLength()); + + Map<String, String> params = unit.getContentParameters(); + assertEquals("wdstechnology.com", params.get("unassigned/0x42")); + + } + + public void testTypedParamUnassignedWellKnownLongIntegerTokenText() throws Exception { + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(0x15); + out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK); + out.write(0x01); // Short-length of well-known parameter token + out.write(PARAM_UNASSIGNED); + out.write("wdstechnology.com".getBytes("US-ASCII")); + out.write(WSP_STRING_TERMINATOR); + + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertTrue(unit.decodeContentType(0)); + + String mimeType = unit.getValueString(); + + assertEquals(STRING_MIME_TYPE_ROLLOVER_CERTIFICATE, mimeType); + assertEquals(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE, unit.getValue32()); + + assertEquals(22, unit.getDecodedDataLength()); + + Map<String, String> params = unit.getContentParameters(); + assertEquals("wdstechnology.com", params.get("unassigned/0x42")); + } + + public void testDecodesReturnsFalse_WhenParamValueNotTerminated() throws Exception { + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(0x15); + out.write(SHORT_MIME_TYPE_ROLLOVER_CERTIFICATE | WSP_SHORT_INTEGER_MASK); + out.write(0x01); + out.write(PARAM_UNASSIGNED); + out.write("wdstechnology.com".getBytes("US-ASCII")); + + WspTypeDecoder unit = new WspTypeDecoder(out.toByteArray()); + assertFalse(unit.decodeContentType(0)); + } +}
\ No newline at end of file |