From b7945cae5ad0feaad09378d0db77f2b3105e5235 Mon Sep 17 00:00:00 2001 From: Jake Hamby Date: Thu, 22 Sep 2011 14:50:25 -0700 Subject: [PATCH] Enable support for multiple SMSDispatchers in CDMALTEPhone. Refactor framework to support multiple SMSDispatcher objects on dual-mode devices that require support for both 3GPP and 3GPP2 format SMS messages. Each dispatcher registers to receive events for the appropriate message format. Note: All applications which handle incoming SMS messages by processing the SMS_RECEIVED_ACTION broadcast intent MUST pass the "format" extra from the intent into the new createPdu() method in android.telephony.SmsMessage that takes an extra format parameter. This is required in order to correctly decode the PDU on devices which require support for both 3GPP and 3GPP2 formats at the same time, such as CDMA/LTE devices and GSM/CDMA world phones. - moved code to manage device storage events from SMSDispatcher to a new class, SmsStorageMonitor, which is shared among all dispatchers. - moved code to monitor per-application outgoing SMS usage from SMSDispatcher.SmsCounter to a new class, SmsUsageMonitor, which is shared among all dispatchers. - fixed a bug that prevented CDMALTEPhone from setting the MCC/MNC operator numeric value in the telephony provider from the UICC, as GSMPhone does, when the SIM records have loaded. Change-Id: I2789ac07b6ca2948138bca7f75481f9b31514f20 --- core/java/android/provider/Telephony.java | 3 +- .../java/android/telephony/SmsManager.java | 4 +- .../java/android/telephony/SmsMessage.java | 163 ++--- .../android/telephony/gsm/SmsMessage.java | 98 --- .../internal/telephony/BaseCommands.java | 19 +- .../internal/telephony/CommandsInterface.java | 26 +- .../com/android/internal/telephony/Phone.java | 11 +- .../android/internal/telephony/PhoneBase.java | 67 +- .../internal/telephony/PhoneProxy.java | 34 +- .../com/android/internal/telephony/RIL.java | 8 +- .../internal/telephony/SMSDispatcher.java | 638 +++++++++--------- .../internal/telephony/SmsStorageMonitor.java | 162 +++++ .../internal/telephony/SmsUsageMonitor.java | 128 ++++ .../internal/telephony/cdma/CDMALTEPhone.java | 32 +- .../internal/telephony/cdma/CDMAPhone.java | 99 ++- .../telephony/cdma/CdmaLteUiccRecords.java | 10 + .../telephony/cdma/CdmaSMSDispatcher.java | 295 ++------ .../internal/telephony/cdma/SmsMessage.java | 24 - .../internal/telephony/gsm/GSMPhone.java | 44 +- .../telephony/gsm/GsmSMSDispatcher.java | 305 +++------ .../internal/telephony/gsm/SIMRecords.java | 14 +- .../internal/telephony/gsm/SmsMessage.java | 17 - 22 files changed, 1030 insertions(+), 1171 deletions(-) create mode 100644 telephony/java/com/android/internal/telephony/SmsStorageMonitor.java create mode 100644 telephony/java/com/android/internal/telephony/SmsUsageMonitor.java diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index 237a89250cbeb..79995d05dd09b 100755 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -666,6 +666,7 @@ public final class Telephony { public static SmsMessage[] getMessagesFromIntent( Intent intent) { Object[] messages = (Object[]) intent.getSerializableExtra("pdus"); + String format = intent.getStringExtra("format"); byte[][] pduObjs = new byte[messages.length][]; for (int i = 0; i < messages.length; i++) { @@ -676,7 +677,7 @@ public final class Telephony { SmsMessage[] msgs = new SmsMessage[pduCount]; for (int i = 0; i < pduCount; i++) { pdus[i] = pduObjs[i]; - msgs[i] = SmsMessage.createFromPdu(pdus[i]); + msgs[i] = SmsMessage.createFromPdu(pdus[i], format); } return msgs; } diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index 5bdc146792f35..44bdaeb7876e6 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -325,7 +325,7 @@ public final class SmsManager { * * {@hide} */ - public ArrayList getAllMessagesFromIcc() { + public static ArrayList getAllMessagesFromIcc() { List records = null; try { @@ -470,7 +470,7 @@ public final class SmsManager { * getAllMessagesFromIcc * @return ArrayList of SmsMessage objects. */ - private ArrayList createMessageListFromRawRecords(List records) { + private static ArrayList createMessageListFromRawRecords(List records) { ArrayList messages = new ArrayList(); if (records != null) { int count = records.size(); diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java index e75d96d0fde43..fc8a145fcd71f 100644 --- a/telephony/java/android/telephony/SmsMessage.java +++ b/telephony/java/android/telephony/SmsMessage.java @@ -36,7 +36,6 @@ import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA; * A Short Message Service message. */ public class SmsMessage { - private static final boolean LOCAL_DEBUG = true; private static final String LOG_TAG = "SMS"; /** @@ -78,6 +77,18 @@ public class SmsMessage { */ public static final int MAX_USER_DATA_SEPTETS_WITH_HEADER = 153; + /** + * Indicates a 3GPP format SMS message. + * @hide pending API council approval + */ + public static final String FORMAT_3GPP = "3gpp"; + + /** + * Indicates a 3GPP2 format SMS message. + * @hide pending API council approval + */ + public static final String FORMAT_3GPP2 = "3gpp2"; + /** Contains actual SmsMessage. Only public for debugging and for framework layer. * * @hide @@ -106,30 +117,47 @@ public class SmsMessage { } - /** - * Constructor - * - * @hide - */ - public SmsMessage() { - this(getSmsFacility()); - } - private SmsMessage(SmsMessageBase smb) { mWrappedSmsMessage = smb; } /** * Create an SmsMessage from a raw PDU. + * + *

This method will soon be deprecated and all applications which handle + * incoming SMS messages by processing the {@code SMS_RECEIVED_ACTION} broadcast + * intent must now pass the new {@code format} String extra from the intent + * into the new method {@code createFromPdu(byte[], String)} which takes an + * extra format parameter. This is required in order to correctly decode the PDU on + * devices that require support for both 3GPP and 3GPP2 formats at the same time, + * such as dual-mode GSM/CDMA and CDMA/LTE phones. */ public static SmsMessage createFromPdu(byte[] pdu) { - SmsMessageBase wrappedMessage; int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(); + String format = (PHONE_TYPE_CDMA == activePhone) ? FORMAT_3GPP2 : FORMAT_3GPP; + return createFromPdu(pdu, format); + } - if (PHONE_TYPE_CDMA == activePhone) { + /** + * Create an SmsMessage from a raw PDU with the specified message format. The + * message format is passed in the {@code SMS_RECEIVED_ACTION} as the {@code format} + * String extra, and will be either "3gpp" for GSM/UMTS/LTE messages in 3GPP format + * or "3gpp2" for CDMA/LTE messages in 3GPP2 format. + * + * @param pdu the message PDU from the SMS_RECEIVED_ACTION intent + * @param format the format extra from the SMS_RECEIVED_ACTION intent + * @hide pending API council approval + */ + public static SmsMessage createFromPdu(byte[] pdu, String format) { + SmsMessageBase wrappedMessage; + + if (FORMAT_3GPP2.equals(format)) { wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromPdu(pdu); - } else { + } else if (FORMAT_3GPP.equals(format)) { wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromPdu(pdu); + } else { + Log.e(LOG_TAG, "createFromPdu(): unsupported message format " + format); + return null; } return new SmsMessage(wrappedMessage); @@ -144,57 +172,19 @@ public class SmsMessage { * * {@hide} */ - public static SmsMessage newFromCMT(String[] lines){ - SmsMessageBase wrappedMessage; - int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(); - - if (PHONE_TYPE_CDMA == activePhone) { - wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.newFromCMT(lines); - } else { - wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.newFromCMT(lines); - } - - return new SmsMessage(wrappedMessage); - } - - /** @hide */ - protected static SmsMessage newFromCMTI(String line) { - SmsMessageBase wrappedMessage; - int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(); - - if (PHONE_TYPE_CDMA == activePhone) { - wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.newFromCMTI(line); - } else { - wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.newFromCMTI(line); - } - - return new SmsMessage(wrappedMessage); - } - - /** @hide */ - public static SmsMessage newFromCDS(String line) { - SmsMessageBase wrappedMessage; - int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(); - - if (PHONE_TYPE_CDMA == activePhone) { - wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.newFromCDS(line); - } else { - wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.newFromCDS(line); - } + public static SmsMessage newFromCMT(String[] lines) { + // received SMS in 3GPP format + SmsMessageBase wrappedMessage = + com.android.internal.telephony.gsm.SmsMessage.newFromCMT(lines); return new SmsMessage(wrappedMessage); } /** @hide */ public static SmsMessage newFromParcel(Parcel p) { - SmsMessageBase wrappedMessage; - int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(); - - if (PHONE_TYPE_CDMA == activePhone) { - wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.newFromParcel(p); - } else { - wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.newFromParcel(p); - } + // received SMS in 3GPP2 format + SmsMessageBase wrappedMessage = + com.android.internal.telephony.cdma.SmsMessage.newFromParcel(p); return new SmsMessage(wrappedMessage); } @@ -227,6 +217,9 @@ public class SmsMessage { /** * Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the * length in bytes (not hex chars) less the SMSC header + * + * FIXME: This method is only used by a CTS test case that isn't run on CDMA devices. + * We should probably deprecate it and remove the obsolete test case. */ public static int getTPLayerLengthForPDU(String pdu) { int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(); @@ -373,34 +366,6 @@ public class SmsMessage { * otherwise useful apps. */ - /** - * Get an SMS-SUBMIT PDU for a destination address and a message. - * This method will not attempt to use any GSM national language 7 bit encodings. - * - * @param scAddress Service Centre address. Null means use default. - * @return a SubmitPdu containing the encoded SC - * address, if applicable, and the encoded message. - * Returns null on encode error. - * @hide - */ - public static SubmitPdu getSubmitPdu(String scAddress, - String destinationAddress, String message, - boolean statusReportRequested, byte[] header) { - SubmitPduBase spb; - int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(); - - if (PHONE_TYPE_CDMA == activePhone) { - spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress, - destinationAddress, message, statusReportRequested, - SmsHeader.fromByteArray(header)); - } else { - spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress, - destinationAddress, message, statusReportRequested, header); - } - - return new SubmitPdu(spb); - } - /** * Get an SMS-SUBMIT PDU for a destination address and a message. * This method will not attempt to use any GSM national language 7 bit encodings. @@ -602,15 +567,6 @@ public class SmsMessage { return mWrappedSmsMessage.getUserData(); } - /** - * Return the user data header (UDH). - * - * @hide - */ - public SmsHeader getUserDataHeader() { - return mWrappedSmsMessage.getUserDataHeader(); - } - /** * Returns the raw PDU for the message. * @@ -646,7 +602,6 @@ public class SmsMessage { * SmsManager.STATUS_ON_ICC_UNSENT */ public int getStatusOnIcc() { - return mWrappedSmsMessage.getStatusOnIcc(); } @@ -666,7 +621,6 @@ public class SmsMessage { * SmsMessage was not created from a ICC SMS EF record. */ public int getIndexOnIcc() { - return mWrappedSmsMessage.getIndexOnIcc(); } @@ -704,19 +658,4 @@ public class SmsMessage { public boolean isReplyPathPresent() { return mWrappedSmsMessage.isReplyPathPresent(); } - - /** This method returns the reference to a specific - * SmsMessage object, which is used for accessing its static methods. - * @return Specific SmsMessage. - * - * @hide - */ - private static final SmsMessageBase getSmsFacility(){ - int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(); - if (PHONE_TYPE_CDMA == activePhone) { - return new com.android.internal.telephony.cdma.SmsMessage(); - } else { - return new com.android.internal.telephony.gsm.SmsMessage(); - } - } } diff --git a/telephony/java/android/telephony/gsm/SmsMessage.java b/telephony/java/android/telephony/gsm/SmsMessage.java index 4af99a646cf61..8d86ec296854c 100644 --- a/telephony/java/android/telephony/gsm/SmsMessage.java +++ b/telephony/java/android/telephony/gsm/SmsMessage.java @@ -165,104 +165,6 @@ public class SmsMessage { return new SmsMessage(wrappedMessage); } - /** - * TS 27.005 3.4.1 lines[0] and lines[1] are the two lines read from the - * +CMT unsolicited response (PDU mode, of course) - * +CMT: [<alpha>], - * - * Only public for debugging and for RIL - * @deprecated Use android.telephony.SmsMessage. - * {@hide} - */ - @Deprecated - public static SmsMessage newFromCMT(String[] lines){ - SmsMessageBase wrappedMessage; - int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(); - - if (PHONE_TYPE_CDMA == activePhone) { - wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.newFromCMT(lines); - } else { - wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.newFromCMT(lines); - } - - return new SmsMessage(wrappedMessage); - } - - /** @deprecated Use android.telephony.SmsMessage. - * @hide */ - @Deprecated - protected static SmsMessage newFromCMTI(String line) { - SmsMessageBase wrappedMessage; - int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(); - - if (PHONE_TYPE_CDMA == activePhone) { - wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.newFromCMTI(line); - } else { - wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.newFromCMTI(line); - } - - return new SmsMessage(wrappedMessage); - } - - /** @deprecated Use android.telephony.SmsMessage. - * @hide */ - @Deprecated - public static SmsMessage newFromCDS(String line) { - SmsMessageBase wrappedMessage; - int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(); - - if (PHONE_TYPE_CDMA == activePhone) { - wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.newFromCDS(line); - } else { - wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.newFromCDS(line); - } - - return new SmsMessage(wrappedMessage); - } - - /** @deprecated Use android.telephony.SmsMessage. - * @hide */ - @Deprecated - public static SmsMessage newFromParcel(Parcel p) { - SmsMessageBase wrappedMessage; - int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(); - - if (PHONE_TYPE_CDMA == activePhone) { - wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.newFromParcel(p); - } else { - wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.newFromParcel(p); - } - - return new SmsMessage(wrappedMessage); - } - - /** - * Create an SmsMessage from an SMS EF record. - * - * @param index Index of SMS record. This should be index in ArrayList - * returned by SmsManager.getAllMessagesFromSim + 1. - * @param data Record data. - * @return An SmsMessage representing the record. - * - * @deprecated Use android.telephony.SmsMessage. - * @hide - */ - @Deprecated - public static SmsMessage createFromEfRecord(int index, byte[] data) { - SmsMessageBase wrappedMessage; - int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(); - - if (PHONE_TYPE_CDMA == activePhone) { - wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromEfRecord( - index, data); - } else { - wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromEfRecord( - index, data); - } - - return new SmsMessage(wrappedMessage); - } - /** * Get the TP-Layer-Length for the given SMS-SUBMIT PDU Basically, the * length in bytes (not hex chars) less the SMSC header diff --git a/telephony/java/com/android/internal/telephony/BaseCommands.java b/telephony/java/com/android/internal/telephony/BaseCommands.java index f0d2fbaf018f6..f111dd6341fe0 100644 --- a/telephony/java/com/android/internal/telephony/BaseCommands.java +++ b/telephony/java/com/android/internal/telephony/BaseCommands.java @@ -79,7 +79,8 @@ public abstract class BaseCommands implements CommandsInterface { protected RegistrantList mRilConnectedRegistrants = new RegistrantList(); protected RegistrantList mIccRefreshRegistrants = new RegistrantList(); - protected Registrant mSMSRegistrant; + protected Registrant mGsmSmsRegistrant; + protected Registrant mCdmaSmsRegistrant; protected Registrant mNITZTimeRegistrant; protected Registrant mSignalStrengthRegistrant; protected Registrant mUSSDRegistrant; @@ -358,12 +359,20 @@ public abstract class BaseCommands implements CommandsInterface { mIccStatusChangedRegistrants.remove(h); } - public void setOnNewSMS(Handler h, int what, Object obj) { - mSMSRegistrant = new Registrant (h, what, obj); + public void setOnNewGsmSms(Handler h, int what, Object obj) { + mGsmSmsRegistrant = new Registrant (h, what, obj); } - public void unSetOnNewSMS(Handler h) { - mSMSRegistrant.clear(); + public void unSetOnNewGsmSms(Handler h) { + mGsmSmsRegistrant.clear(); + } + + public void setOnNewCdmaSms(Handler h, int what, Object obj) { + mCdmaSmsRegistrant = new Registrant (h, what, obj); + } + + public void unSetOnNewCdmaSms(Handler h) { + mCdmaSmsRegistrant.clear(); } public void setOnNewGsmBroadcastSms(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 1caea7010b444..33eed389b6150 100644 --- a/telephony/java/com/android/internal/telephony/CommandsInterface.java +++ b/telephony/java/com/android/internal/telephony/CommandsInterface.java @@ -20,8 +20,6 @@ import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo; import android.os.Message; import android.os.Handler; -import android.os.SystemProperties; - /** * {@hide} @@ -267,14 +265,32 @@ public interface CommandsInterface { void unregisterForRUIMReady(Handler h); /** - * unlike the register* methods, there's only one new SMS handler + * unlike the register* methods, there's only one new 3GPP format SMS handler. * if you need to unregister, you should also tell the radio to stop * sending SMS's to you (via AT+CNMI) * * AsyncResult.result is a String containing the SMS PDU */ - void setOnNewSMS(Handler h, int what, Object obj); - void unSetOnNewSMS(Handler h); + void setOnNewGsmSms(Handler h, int what, Object obj); + void unSetOnNewGsmSms(Handler h); + + /** + * unlike the register* methods, there's only one new 3GPP2 format SMS handler. + * if you need to unregister, you should also tell the radio to stop + * sending SMS's to you (via AT+CNMI) + * + * AsyncResult.result is a String containing the SMS PDU + */ + void setOnNewCdmaSms(Handler h, int what, Object obj); + void unSetOnNewCdmaSms(Handler h); + + /** + * Set the handler for SMS Cell Broadcast messages. + * + * AsyncResult.result is a byte array containing the SMS-CB PDU + */ + void setOnNewGsmBroadcastSms(Handler h, int what, Object obj); + void unSetOnNewGsmBroadcastSms(Handler h); /** * Register for NEW_SMS_ON_SIM unsolicited message diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java index 444f0d29c76ba..ca04eb2daa955 100644 --- a/telephony/java/com/android/internal/telephony/Phone.java +++ b/telephony/java/com/android/internal/telephony/Phone.java @@ -1394,7 +1394,7 @@ public interface Phone { String getDeviceSvn(); /** - * Retrieves the unique sbuscriber ID, e.g., IMSI for GSM phones. + * Retrieves the unique subscriber ID, e.g., IMSI for GSM phones. */ String getSubscriberId(); @@ -1756,4 +1756,13 @@ public interface Phone { * @param response a callback message with the String response in the obj field */ void requestIsimAuthentication(String nonce, Message response); + + /** + * Sets the SIM voice message waiting indicator records. + * @param line GSM Subscriber Profile Number, one-based. Only '1' is supported + * @param countWaiting The number of messages waiting, if known. Use + * -1 to indicate that an unknown number of + * messages are waiting + */ + void setVoiceMessageWaiting(int line, int countWaiting); } diff --git a/telephony/java/com/android/internal/telephony/PhoneBase.java b/telephony/java/com/android/internal/telephony/PhoneBase.java index 82f3955c9f2ef..a7a4908074b2f 100644 --- a/telephony/java/com/android/internal/telephony/PhoneBase.java +++ b/telephony/java/com/android/internal/telephony/PhoneBase.java @@ -112,15 +112,17 @@ public abstract class PhoneBase extends Handler implements Phone { /* Instance Variables */ public CommandsInterface mCM; protected IccFileHandler mIccFileHandler; - boolean mDnsCheckDisabled = false; + boolean mDnsCheckDisabled; public DataConnectionTracker mDataConnectionTracker; boolean mDoesRilSendMultipleCallRing; - int mCallRingContinueToken = 0; + int mCallRingContinueToken; int mCallRingDelay; public boolean mIsTheCurrentActivePhone = true; boolean mIsVoiceCapable = true; public IccRecords mIccRecords; public IccCard mIccCard; + public SmsStorageMonitor mSmsStorageMonitor; + public SmsUsageMonitor mSmsUsageMonitor; public SMSDispatcher mSMS; /** @@ -164,7 +166,7 @@ public abstract class PhoneBase extends Handler implements Phone { protected Looper mLooper; /* to insure registrants are in correct thread*/ - protected Context mContext; + protected final Context mContext; /** * PhoneNotifier is an abstraction for all system-wide @@ -238,6 +240,10 @@ public abstract class PhoneBase extends Handler implements Phone { mCallRingDelay = SystemProperties.getInt( TelephonyProperties.PROPERTY_CALL_RING_DELAY, 3000); Log.d(LOG_TAG, "mCallRingDelay=" + mCallRingDelay); + + // Initialize device storage and outgoing SMS usage monitors for SMSDispatchers. + mSmsStorageMonitor = new SmsStorageMonitor(this); + mSmsUsageMonitor = new SmsUsageMonitor(context.getContentResolver()); } public void dispose() { @@ -246,9 +252,17 @@ public abstract class PhoneBase extends Handler implements Phone { // Must cleanup all connectionS and needs to use sendMessage! mDataConnectionTracker.cleanUpAllConnections(null); mIsTheCurrentActivePhone = false; + // Dispose the SMS usage and storage monitors + mSmsStorageMonitor.dispose(); + mSmsUsageMonitor.dispose(); } } + public void removeReferences() { + mSmsStorageMonitor = null; + mSmsUsageMonitor = null; + } + /** * When overridden the derived class needs to call * super.handleMessage(msg) so this method has a @@ -1036,37 +1050,6 @@ public abstract class PhoneBase extends Handler implements Phone { (mDataConnectionTracker.isDataPossible(apnType))); } - /** - * simulateDataConnection - * - * simulates various data connection states. This messes with - * DataConnectionTracker's internal states, but doesn't actually change - * the underlying radio connection states. - * - * @param state Phone.DataState enum. - */ - public void simulateDataConnection(Phone.DataState state) { - DataConnectionTracker.State dcState; - - switch (state) { - case CONNECTED: - dcState = DataConnectionTracker.State.CONNECTED; - break; - case SUSPENDED: - dcState = DataConnectionTracker.State.CONNECTED; - break; - case DISCONNECTED: - dcState = DataConnectionTracker.State.FAILED; - break; - default: - dcState = DataConnectionTracker.State.CONNECTING; - break; - } - - mDataConnectionTracker.setState(dcState); - notifyDataConnection(null, Phone.APN_TYPE_DEFAULT); - } - /** * Notify registrants of a new ringing Connection. * Subclasses of Phone probably want to replace this with a @@ -1132,7 +1115,7 @@ public abstract class PhoneBase extends Handler implements Phone { /** * Common error logger method for unexpected calls to CDMA-only methods. */ - private void logUnexpectedCdmaMethodCall(String name) + private static void logUnexpectedCdmaMethodCall(String name) { Log.e(LOG_TAG, "Error! " + name + "() in PhoneBase should not be " + "called, CDMAPhone inactive."); @@ -1145,7 +1128,7 @@ public abstract class PhoneBase extends Handler implements Phone { /** * Common error logger method for unexpected calls to GSM/WCDMA-only methods. */ - private void logUnexpectedGsmMethodCall(String name) { + private static void logUnexpectedGsmMethodCall(String name) { Log.e(LOG_TAG, "Error! " + name + "() in PhoneBase should not be " + "called, GSMPhone inactive."); } @@ -1167,4 +1150,16 @@ public abstract class PhoneBase extends Handler implements Phone { public int getLteOnCdmaMode() { return mCM.getLteOnCdmaMode(); } + + /** + * Sets the SIM voice message waiting indicator records. + * @param line GSM Subscriber Profile Number, one-based. Only '1' is supported + * @param countWaiting The number of messages waiting, if known. Use + * -1 to indicate that an unknown number of + * messages are waiting + */ + @Override + public void setVoiceMessageWaiting(int line, int countWaiting) { + mIccRecords.setVoiceMessageWaiting(line, countWaiting); + } } diff --git a/telephony/java/com/android/internal/telephony/PhoneProxy.java b/telephony/java/com/android/internal/telephony/PhoneProxy.java index e0e8d49156d80..b497ec8faf434 100644 --- a/telephony/java/com/android/internal/telephony/PhoneProxy.java +++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java @@ -72,7 +72,7 @@ public class PhoneProxy extends Handler implements Phone { switch(msg.what) { case EVENT_RADIO_TECHNOLOGY_CHANGED: //switch Phone from CDMA to GSM or vice versa - mOutgoingPhone = ((PhoneBase)mActivePhone).getPhoneName(); + mOutgoingPhone = mActivePhone.getPhoneName(); logd("Switching phone from " + mOutgoingPhone + "Phone to " + (mOutgoingPhone.equals("GSM") ? "CDMAPhone" : "GSMPhone") ); boolean oldPowerState = false; // old power state to off @@ -144,23 +144,10 @@ public class PhoneProxy extends Handler implements Phone { super.handleMessage(msg); } - private void logv(String msg) { - Log.v(LOG_TAG, "[PhoneProxy] " + msg); - } - - private void logd(String msg) { + private static void logd(String msg) { Log.d(LOG_TAG, "[PhoneProxy] " + msg); } - private void logw(String msg) { - Log.w(LOG_TAG, "[PhoneProxy] " + msg); - } - - private void loge(String msg) { - Log.e(LOG_TAG, "[PhoneProxy] " + msg); - } - - public ServiceState getServiceState() { return mActivePhone.getServiceState(); } @@ -739,19 +726,19 @@ public class PhoneProxy extends Handler implements Phone { } public int getCdmaEriIconIndex() { - return mActivePhone.getCdmaEriIconIndex(); + return mActivePhone.getCdmaEriIconIndex(); } - public String getCdmaEriText() { - return mActivePhone.getCdmaEriText(); - } + public String getCdmaEriText() { + return mActivePhone.getCdmaEriText(); + } public int getCdmaEriIconMode() { - return mActivePhone.getCdmaEriIconMode(); + return mActivePhone.getCdmaEriIconMode(); } public Phone getActivePhone() { - return mActivePhone; + return mActivePhone; } public void sendBurstDtmf(String dtmfString, int on, int off, Message onComplete){ @@ -861,4 +848,9 @@ public class PhoneProxy extends Handler implements Phone { public int getLteOnCdmaMode() { return mActivePhone.getLteOnCdmaMode(); } + + @Override + public void setVoiceMessageWaiting(int line, int countWaiting) { + mActivePhone.setVoiceMessageWaiting(line, countWaiting); + } } diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java index bd35058f61465..8aae0d4281307 100644 --- a/telephony/java/com/android/internal/telephony/RIL.java +++ b/telephony/java/com/android/internal/telephony/RIL.java @@ -2434,8 +2434,8 @@ public final class RIL extends BaseCommands implements CommandsInterface { SmsMessage sms; sms = SmsMessage.newFromCMT(a); - if (mSMSRegistrant != null) { - mSMSRegistrant + if (mGsmSmsRegistrant != null) { + mGsmSmsRegistrant .notifyRegistrant(new AsyncResult(null, sms, null)); } break; @@ -2607,8 +2607,8 @@ public final class RIL extends BaseCommands implements CommandsInterface { SmsMessage sms = (SmsMessage) ret; - if (mSMSRegistrant != null) { - mSMSRegistrant + if (mCdmaSmsRegistrant != null) { + mCdmaSmsRegistrant .notifyRegistrant(new AsyncResult(null, sms, null)); } break; diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java index 76e719c3c8954..e4c60284dc4fc 100644 --- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java @@ -44,10 +44,12 @@ import android.telephony.ServiceState; import android.util.Log; import android.view.WindowManager; +import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails; import com.android.internal.util.HexDump; import java.io.ByteArrayOutputStream; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.Random; @@ -60,68 +62,66 @@ import static android.telephony.SmsManager.RESULT_ERROR_RADIO_OFF; import static android.telephony.SmsManager.RESULT_ERROR_LIMIT_EXCEEDED; import static android.telephony.SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE; - public abstract class SMSDispatcher extends Handler { - private static final String TAG = "SMS"; + static final String TAG = "SMS"; // accessed from inner class private static final String SEND_NEXT_MSG_EXTRA = "SendNextMsg"; - /** Default checking period for SMS sent without user permit */ - private static final int DEFAULT_SMS_CHECK_PERIOD = 3600000; - - /** Default number of SMS sent in checking period without user permit */ - private static final int DEFAULT_SMS_MAX_COUNT = 100; - /** Default timeout for SMS sent query */ private static final int DEFAULT_SMS_TIMEOUT = 6000; - protected static final String[] RAW_PROJECTION = new String[] { - "pdu", - "sequence", - "destination_port", + /** Permission required to receive SMS and SMS-CB messages. */ + public static final String RECEIVE_SMS_PERMISSION = "android.permission.RECEIVE_SMS"; + + /** Permission required to receive ETWS and CMAS emergency broadcasts. */ + public static final String RECEIVE_EMERGENCY_BROADCAST_PERMISSION = + "android.permission.RECEIVE_EMERGENCY_BROADCAST"; + + /** Query projection for checking for duplicate message segments. */ + private static final String[] PDU_PROJECTION = new String[] { + "pdu" }; - static final protected int EVENT_NEW_SMS = 1; + /** Query projection for combining concatenated message segments. */ + private static final String[] PDU_SEQUENCE_PORT_PROJECTION = new String[] { + "pdu", + "sequence", + "destination_port" + }; - static final protected int EVENT_SEND_SMS_COMPLETE = 2; + private static final int PDU_COLUMN = 0; + private static final int SEQUENCE_COLUMN = 1; + private static final int DESTINATION_PORT_COLUMN = 2; + + /** New SMS received. */ + protected static final int EVENT_NEW_SMS = 1; + + /** SMS send complete. */ + protected static final int EVENT_SEND_SMS_COMPLETE = 2; /** Retry sending a previously failed SMS message */ - static final protected int EVENT_SEND_RETRY = 3; - - /** Status report received */ - static final protected int EVENT_NEW_SMS_STATUS_REPORT = 5; - - /** SIM/RUIM storage is full */ - static final protected int EVENT_ICC_FULL = 6; + private static final int EVENT_SEND_RETRY = 3; /** SMS confirm required */ - static final protected int EVENT_POST_ALERT = 7; + private static final int EVENT_POST_ALERT = 4; /** Send the user confirmed SMS */ - static final protected int EVENT_SEND_CONFIRMED_SMS = 8; + static final int EVENT_SEND_CONFIRMED_SMS = 5; // accessed from inner class /** Alert is timeout */ - static final protected int EVENT_ALERT_TIMEOUT = 9; + private static final int EVENT_ALERT_TIMEOUT = 6; /** Stop the sending */ - static final protected int EVENT_STOP_SENDING = 10; + static final int EVENT_STOP_SENDING = 7; // accessed from inner class - /** Memory status reporting is acknowledged by RIL */ - static final protected int EVENT_REPORT_MEMORY_STATUS_DONE = 11; - - /** 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; - protected CommandsInterface mCm; + protected final Phone mPhone; + protected final Context mContext; + protected final ContentResolver mResolver; + protected final CommandsInterface mCm; + protected final SmsStorageMonitor mStorageMonitor; protected final WapPushOverSms mWapPush; - protected final Uri mRawUri = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw"); + protected static final Uri mRawUri = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw"); /** Maximum number of times to retry sending a failed SMS. */ private static final int MAX_SEND_RETRIES = 3; @@ -136,12 +136,14 @@ public abstract class SMSDispatcher extends Handler { * Message reference for a CONCATENATED_8_BIT_REFERENCE or * CONCATENATED_16_BIT_REFERENCE message set. Should be * incremented for each set of concatenated messages. + * Static field shared by all dispatcher objects. */ - private static int sConcatenatedRef; + private static int sConcatenatedRef = new Random().nextInt(256); - private SmsCounter mCounter; + /** Outgoing message counter. Shared by all dispatchers. */ + private final SmsUsageMonitor mUsageMonitor; - private ArrayList mSTrackers = new ArrayList(MO_MSG_QUEUE_LIMIT); + private final ArrayList mSTrackers = new ArrayList(MO_MSG_QUEUE_LIMIT); /** Wake lock to ensure device stays awake while dispatching the SMS intent. */ private PowerManager.WakeLock mWakeLock; @@ -150,17 +152,14 @@ public abstract class SMSDispatcher extends Handler { * Hold the wake lock for 5 seconds, which should be enough time for * any receiver(s) to grab its own wake lock. */ - private final int WAKE_LOCK_TIMEOUT = 5000; - - protected boolean mStorageAvailable = true; - protected boolean mReportMemoryStatusPending = false; + private static final int WAKE_LOCK_TIMEOUT = 5000; /* Flags indicating whether the current device allows sms service */ protected boolean mSmsCapable = true; protected boolean mSmsReceiveDisabled; protected boolean mSmsSendDisabled; - protected static int mRemainingMessages = -1; + protected int mRemainingMessages = -1; protected static int getNextConcatenatedRef() { sConcatenatedRef += 1; @@ -168,111 +167,52 @@ public abstract class SMSDispatcher extends Handler { } /** - * Implement the per-application based SMS control, which only allows - * a limit on the number of SMS/MMS messages an app can send in checking - * period. + * Create a new SMS dispatcher. + * @param phone the Phone to use + * @param storageMonitor the SmsStorageMonitor to use + * @param usageMonitor the SmsUsageMonitor to use */ - private class SmsCounter { - private int mCheckPeriod; - private int mMaxAllowed; - private HashMap> mSmsStamp; - - /** - * Create SmsCounter - * @param mMax is the number of SMS allowed without user permit - * @param mPeriod is the checking period - */ - SmsCounter(int mMax, int mPeriod) { - mMaxAllowed = mMax; - mCheckPeriod = mPeriod; - mSmsStamp = new HashMap> (); - } - - /** - * Check to see if an application allow to send new SMS messages - * - * @param appName is the application sending sms - * @param smsWaiting is the number of new sms wants to be sent - * @return true if application is allowed to send the requested number - * of new sms messages - */ - boolean check(String appName, int smsWaiting) { - if (!mSmsStamp.containsKey(appName)) { - mSmsStamp.put(appName, new ArrayList()); - } - - return isUnderLimit(mSmsStamp.get(appName), smsWaiting); - } - - private boolean isUnderLimit(ArrayList sent, int smsWaiting) { - Long ct = System.currentTimeMillis(); - - Log.d(TAG, "SMS send size=" + sent.size() + "time=" + ct); - - while (sent.size() > 0 && (ct - sent.get(0)) > mCheckPeriod ) { - sent.remove(0); - } - - - if ( (sent.size() + smsWaiting) <= mMaxAllowed) { - for (int i = 0; i < smsWaiting; i++ ) { - sent.add(ct); - } - return true; - } - return false; - } - } - - protected SMSDispatcher(PhoneBase phone) { + protected SMSDispatcher(PhoneBase phone, SmsStorageMonitor storageMonitor, + SmsUsageMonitor usageMonitor) { mPhone = phone; mWapPush = new WapPushOverSms(phone, this); mContext = phone.getContext(); mResolver = mContext.getContentResolver(); mCm = phone.mCM; + mStorageMonitor = storageMonitor; + mUsageMonitor = usageMonitor; createWakelock(); - int check_period = Settings.Secure.getInt(mResolver, - Settings.Secure.SMS_OUTGOING_CHECK_INTERVAL_MS, - DEFAULT_SMS_CHECK_PERIOD); - int max_count = Settings.Secure.getInt(mResolver, - Settings.Secure.SMS_OUTGOING_CHECK_MAX_COUNT, - DEFAULT_SMS_MAX_COUNT); - mCounter = new SmsCounter(max_count, check_period); - - mCm.setOnNewSMS(this, EVENT_NEW_SMS, null); - mCm.setOnSmsStatus(this, EVENT_NEW_SMS_STATUS_REPORT, null); - mCm.setOnIccSmsFull(this, EVENT_ICC_FULL, null); - mCm.registerForOn(this, EVENT_RADIO_ON, null); - - // Don't always start message ref at 0. - sConcatenatedRef = new Random().nextInt(256); - - // Register for device storage intents. Use these to notify the RIL - // that storage for SMS is or is not available. - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_DEVICE_STORAGE_FULL); - filter.addAction(Intent.ACTION_DEVICE_STORAGE_NOT_FULL); - mContext.registerReceiver(mResultReceiver, filter); - mSmsCapable = mContext.getResources().getBoolean( com.android.internal.R.bool.config_sms_capable); mSmsReceiveDisabled = !SystemProperties.getBoolean( TelephonyProperties.PROPERTY_SMS_RECEIVE, mSmsCapable); mSmsSendDisabled = !SystemProperties.getBoolean( TelephonyProperties.PROPERTY_SMS_SEND, mSmsCapable); - Log.d(TAG, "SMSDispatcher: ctor mSmsCapable=" + mSmsCapable + Log.d(TAG, "SMSDispatcher: ctor mSmsCapable=" + mSmsCapable + " format=" + getFormat() + " mSmsReceiveDisabled=" + mSmsReceiveDisabled + " mSmsSendDisabled=" + mSmsSendDisabled); } - public void dispose() { - mCm.unSetOnNewSMS(this); - mCm.unSetOnSmsStatus(this); - mCm.unSetOnIccSmsFull(this); - mCm.unregisterForOn(this); - } + /** Unregister for incoming SMS events. */ + public abstract void dispose(); + + /** + * The format of the message PDU in the associated broadcast intent. + * This will be either "3gpp" for GSM/UMTS/LTE messages in 3GPP format + * or "3gpp2" for CDMA/LTE messages in 3GPP2 format. + * + * Note: All applications which handle incoming SMS messages by processing the + * SMS_RECEIVED_ACTION broadcast intent MUST pass the "format" extra from the intent + * into the new methods in {@link android.telephony.SmsMessage} which take an + * extra format parameter. This is required in order to correctly decode the PDU on + * devices which require support for both 3GPP and 3GPP2 formats at the same time, + * such as CDMA/LTE devices and GSM/CDMA world phones. + * + * @return the format of the message PDU + */ + protected abstract String getFormat(); @Override protected void finalize() { @@ -338,14 +278,6 @@ public abstract class SMSDispatcher extends Handler { sendSms((SmsTracker) msg.obj); break; - case EVENT_NEW_SMS_STATUS_REPORT: - handleStatusReport((AsyncResult)msg.obj); - break; - - case EVENT_ICC_FULL: - handleIccFull(); - break; - case EVENT_POST_ALERT: handleReachSentLimit((SmsTracker)(msg.obj)); break; @@ -369,7 +301,7 @@ public abstract class SMSDispatcher extends Handler { case EVENT_SEND_CONFIRMED_SMS: if (mSTrackers.isEmpty() == false) { SmsTracker sTracker = mSTrackers.remove(mSTrackers.size() - 1); - if (isMultipartTracker(sTracker)) { + if (sTracker.isMultipart()) { sendMultipartSms(sTracker); } else { sendSms(sTracker); @@ -390,30 +322,6 @@ public abstract class SMSDispatcher extends Handler { removeMessages(EVENT_ALERT_TIMEOUT, msg.obj); } break; - - case EVENT_REPORT_MEMORY_STATUS_DONE: - ar = (AsyncResult)msg.obj; - if (ar.exception != null) { - mReportMemoryStatusPending = true; - Log.v(TAG, "Memory status report to modem pending : mStorageAvailable = " - + mStorageAvailable); - } else { - mReportMemoryStatusPending = false; - } - break; - - case EVENT_RADIO_ON: - if (mReportMemoryStatusPending) { - Log.v(TAG, "Sending pending memory status report : mStorageAvailable = " - + mStorageAvailable); - mCm.reportSmsMemoryStatus(mStorageAvailable, - obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE)); - } - break; - - case EVENT_NEW_BROADCAST_SMS: - handleBroadcastSms((AsyncResult)msg.obj); - break; } } @@ -439,26 +347,6 @@ public abstract class SMSDispatcher extends Handler { this, Activity.RESULT_OK, null, null); } - /** - * Called when SIM_FULL message is received from the RIL. Notifies interested - * parties that SIM storage for SMS messages is full. - */ - private void handleIccFull(){ - // broadcast SIM_FULL intent - Intent intent = new Intent(Intents.SIM_FULL_ACTION); - mWakeLock.acquire(WAKE_LOCK_TIMEOUT); - mContext.sendBroadcast(intent, "android.permission.RECEIVE_SMS"); - } - - /** - * Called when a status report is received. This should correspond to - * a previously successful SEND. - * - * @param ar AsyncResult passed into the message handler. ar.result should - * be a String representing the status report PDU, as ASCII hex. - */ - protected abstract void handleStatusReport(AsyncResult ar); - /** * Called when SMS send completes. Broadcasts a sentIntent on success. * On failure, either sets up retries or broadcasts a sentIntent with @@ -559,7 +447,7 @@ public abstract class SMSDispatcher extends Handler { * POWER_OFF * @param tracker An SmsTracker for the current message. */ - protected void handleNotInService(int ss, SmsTracker tracker) { + protected static void handleNotInService(int ss, SmsTracker tracker) { if (tracker.mSentIntent != null) { try { if (ss == ServiceState.STATE_POWER_OFF) { @@ -581,86 +469,171 @@ public abstract class SMSDispatcher extends Handler { */ public abstract int dispatchMessage(SmsMessageBase sms); + /** + * Dispatch a normal incoming SMS. This is called from the format-specific + * {@link #dispatchMessage(SmsMessageBase)} if no format-specific handling is required. + * + * @param sms + * @return + */ + protected int dispatchNormalMessage(SmsMessageBase sms) { + SmsHeader smsHeader = sms.getUserDataHeader(); + + // See if message is partial or port addressed. + if ((smsHeader == null) || (smsHeader.concatRef == null)) { + // Message is not partial (not part of concatenated sequence). + byte[][] pdus = new byte[1][]; + pdus[0] = sms.getPdu(); + + if (smsHeader != null && smsHeader.portAddrs != null) { + if (smsHeader.portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) { + // GSM-style WAP indication + return mWapPush.dispatchWapPdu(sms.getUserData()); + } else { + // The message was sent to a port, so concoct a URI for it. + dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort); + } + } else { + // Normal short and non-port-addressed message, dispatch it. + dispatchPdus(pdus); + } + return Activity.RESULT_OK; + } else { + // Process the message part. + SmsHeader.ConcatRef concatRef = smsHeader.concatRef; + SmsHeader.PortAddrs portAddrs = smsHeader.portAddrs; + return processMessagePart(sms.getPdu(), sms.getOriginatingAddress(), + concatRef.refNumber, concatRef.seqNumber, concatRef.msgCount, + sms.getTimestampMillis(), (portAddrs != null ? portAddrs.destPort : -1), false); + } + } /** * If this is the last part send the parts out to the application, otherwise - * the part is stored for later processing. + * the part is stored for later processing. Handles both 3GPP concatenated messages + * as well as 3GPP2 format WAP push messages processed by + * {@link com.android.internal.telephony.cdma.CdmaSMSDispatcher#processCdmaWapPdu}. + * + * @param pdu the message PDU, or the datagram portion of a CDMA WDP datagram segment + * @param address the originating address + * @param referenceNumber distinguishes concatenated messages from the same sender + * @param sequenceNumber the order of this segment in the message + * @param messageCount the number of segments in the message + * @param timestamp the service center timestamp in millis + * @param destPort the destination port for the message, or -1 for no destination port + * @param isCdmaWapPush true if pdu is a CDMA WDP datagram segment and not an SM PDU * - * NOTE: concatRef (naturally) needs to be non-null, but portAddrs can be null. * @return a result code from {@link Telephony.Sms.Intents}, or * {@link Activity#RESULT_OK} if the message has been broadcast * to applications */ - protected int processMessagePart(SmsMessageBase sms, - SmsHeader.ConcatRef concatRef, SmsHeader.PortAddrs portAddrs) { - - // Lookup all other related parts - StringBuilder where = new StringBuilder("reference_number ="); - where.append(concatRef.refNumber); - where.append(" AND address = ?"); - String[] whereArgs = new String[] {sms.getOriginatingAddress()}; - + protected int processMessagePart(byte[] pdu, String address, int referenceNumber, + int sequenceNumber, int messageCount, long timestamp, int destPort, + boolean isCdmaWapPush) { byte[][] pdus = null; Cursor cursor = null; try { - cursor = mResolver.query(mRawUri, RAW_PROJECTION, where.toString(), whereArgs, null); + // used by several query selection arguments + String refNumber = Integer.toString(referenceNumber); + String seqNumber = Integer.toString(sequenceNumber); + + // Check for duplicate message segment + cursor = mResolver.query(mRawUri, PDU_PROJECTION, + "address=? AND reference_number=? AND sequence=?", + new String[] {address, refNumber, seqNumber}, null); + + // moveToNext() returns false if no duplicates were found + if (cursor.moveToNext()) { + Log.w(TAG, "Discarding duplicate message segment from address=" + address + + " refNumber=" + refNumber + " seqNumber=" + seqNumber); + String oldPduString = cursor.getString(PDU_COLUMN); + byte[] oldPdu = HexDump.hexStringToByteArray(oldPduString); + if (!Arrays.equals(oldPdu, pdu)) { + Log.e(TAG, "Warning: dup message segment PDU of length " + pdu.length + + " is different from existing PDU of length " + oldPdu.length); + } + return Intents.RESULT_SMS_HANDLED; + } + cursor.close(); + + // not a dup, query for all other segments of this concatenated message + String where = "address=? AND reference_number=?"; + String[] whereArgs = new String[] {address, refNumber}; + cursor = mResolver.query(mRawUri, PDU_SEQUENCE_PORT_PROJECTION, where, whereArgs, null); + int cursorCount = cursor.getCount(); - if (cursorCount != concatRef.msgCount - 1) { + if (cursorCount != messageCount - 1) { // We don't have all the parts yet, store this one away ContentValues values = new ContentValues(); - values.put("date", new Long(sms.getTimestampMillis())); - values.put("pdu", HexDump.toHexString(sms.getPdu())); - values.put("address", sms.getOriginatingAddress()); - values.put("reference_number", concatRef.refNumber); - values.put("count", concatRef.msgCount); - values.put("sequence", concatRef.seqNumber); - if (portAddrs != null) { - values.put("destination_port", portAddrs.destPort); + values.put("date", timestamp); + values.put("pdu", HexDump.toHexString(pdu)); + values.put("address", address); + values.put("reference_number", referenceNumber); + values.put("count", messageCount); + values.put("sequence", sequenceNumber); + if (destPort != -1) { + values.put("destination_port", destPort); } mResolver.insert(mRawUri, values); return Intents.RESULT_SMS_HANDLED; } // All the parts are in place, deal with them - int pduColumn = cursor.getColumnIndex("pdu"); - int sequenceColumn = cursor.getColumnIndex("sequence"); - - pdus = new byte[concatRef.msgCount][]; + pdus = new byte[messageCount][]; for (int i = 0; i < cursorCount; i++) { cursor.moveToNext(); - int cursorSequence = (int)cursor.getLong(sequenceColumn); + int cursorSequence = cursor.getInt(SEQUENCE_COLUMN); pdus[cursorSequence - 1] = HexDump.hexStringToByteArray( - cursor.getString(pduColumn)); + cursor.getString(PDU_COLUMN)); + + // Read the destination port from the first segment (needed for CDMA WAP PDU). + // It's not a bad idea to prefer the port from the first segment for 3GPP as well. + if (cursorSequence == 0 && !cursor.isNull(DESTINATION_PORT_COLUMN)) { + destPort = cursor.getInt(DESTINATION_PORT_COLUMN); + } } // This one isn't in the DB, so add it - pdus[concatRef.seqNumber - 1] = sms.getPdu(); + pdus[sequenceNumber - 1] = pdu; // Remove the parts from the database - mResolver.delete(mRawUri, where.toString(), whereArgs); + mResolver.delete(mRawUri, where, whereArgs); } catch (SQLException e) { Log.e(TAG, "Can't access multipart SMS database", e); - // TODO: Would OUT_OF_MEMORY be more appropriate? return Intents.RESULT_SMS_GENERIC_ERROR; } finally { if (cursor != null) cursor.close(); } - /** - * TODO(cleanup): The following code has duplicated logic with - * the radio-specific dispatchMessage code, which is fragile, - * in addition to being redundant. Instead, if this method - * maybe returned the reassembled message (or just contents), - * the following code (which is not really related to - * reconstruction) could be better consolidated. - */ + // Special handling for CDMA WDP datagrams + if (isCdmaWapPush) { + // Build up the data stream + ByteArrayOutputStream output = new ByteArrayOutputStream(); + for (int i = 0; i < messageCount; i++) { + // reassemble the (WSP-)pdu + output.write(pdus[i], 0, pdus[i].length); + } + byte[] datagram = output.toByteArray(); + + // Dispatch the PDU to applications + if (destPort == SmsHeader.PORT_WAP_PUSH) { + // Handle the PUSH + return mWapPush.dispatchWapPdu(datagram); + } else { + pdus = new byte[1][]; + pdus[0] = datagram; + // The messages were sent to any other WAP port + dispatchPortAddressedPdus(pdus, destPort); + return Activity.RESULT_OK; + } + } // Dispatch the PDUs to applications - if (portAddrs != null) { - if (portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) { + if (destPort != -1) { + if (destPort == SmsHeader.PORT_WAP_PUSH) { // Build up the data stream ByteArrayOutputStream output = new ByteArrayOutputStream(); - for (int i = 0; i < concatRef.msgCount; i++) { - SmsMessage msg = SmsMessage.createFromPdu(pdus[i]); + for (int i = 0; i < messageCount; i++) { + SmsMessage msg = SmsMessage.createFromPdu(pdus[i], getFormat()); byte[] data = msg.getUserData(); output.write(data, 0, data.length); } @@ -668,7 +641,7 @@ public abstract class SMSDispatcher extends Handler { return mWapPush.dispatchWapPdu(output.toByteArray()); } else { // The messages were sent to a port, so concoct a URI for it - dispatchPortAddressedPdus(pdus, portAddrs.destPort); + dispatchPortAddressedPdus(pdus, destPort); } } else { // The messages were not sent to a port @@ -685,7 +658,8 @@ public abstract class SMSDispatcher extends Handler { protected void dispatchPdus(byte[][] pdus) { Intent intent = new Intent(Intents.SMS_RECEIVED_ACTION); intent.putExtra("pdus", pdus); - dispatch(intent, "android.permission.RECEIVE_SMS"); + intent.putExtra("format", getFormat()); + dispatch(intent, RECEIVE_SMS_PERMISSION); } /** @@ -698,7 +672,8 @@ public abstract class SMSDispatcher extends Handler { Uri uri = Uri.parse("sms://localhost:" + port); Intent intent = new Intent(Intents.DATA_SMS_RECEIVED_ACTION, uri); intent.putExtra("pdus", pdus); - dispatch(intent, "android.permission.RECEIVE_SMS"); + intent.putExtra("format", getFormat()); + dispatch(intent, RECEIVE_SMS_PERMISSION); } /** @@ -758,6 +733,16 @@ public abstract class SMSDispatcher extends Handler { protected abstract void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent); + /** + * Calculate the number of septets needed to encode the message. + * + * @param messageBody the message to encode + * @param use7bitOnly ignore (but still count) illegal characters if true + * @return TextEncodingDetails + */ + protected abstract TextEncodingDetails calculateLength(CharSequence messageBody, + boolean use7bitOnly); + /** * Send a multi-part text based SMS. * @@ -784,9 +769,70 @@ public abstract class SMSDispatcher extends Handler { * to the recipient. The raw pdu of the status report is in the * extended data ("pdu"). */ - protected abstract void sendMultipartText(String destAddr, String scAddr, + protected void sendMultipartText(String destAddr, String scAddr, ArrayList parts, ArrayList sentIntents, - ArrayList deliveryIntents); + ArrayList deliveryIntents) { + + int refNumber = getNextConcatenatedRef() & 0x00FF; + int msgCount = parts.size(); + int encoding = android.telephony.SmsMessage.ENCODING_UNKNOWN; + + mRemainingMessages = msgCount; + + TextEncodingDetails[] encodingForParts = new TextEncodingDetails[msgCount]; + for (int i = 0; i < msgCount; i++) { + TextEncodingDetails details = calculateLength(parts.get(i), false); + if (encoding != details.codeUnitSize + && (encoding == android.telephony.SmsMessage.ENCODING_UNKNOWN + || encoding == android.telephony.SmsMessage.ENCODING_7BIT)) { + encoding = details.codeUnitSize; + } + encodingForParts[i] = details; + } + + for (int i = 0; i < msgCount; i++) { + SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef(); + concatRef.refNumber = refNumber; + concatRef.seqNumber = i + 1; // 1-based sequence + concatRef.msgCount = msgCount; + // TODO: We currently set this to true since our messaging app will never + // send more than 255 parts (it converts the message to MMS well before that). + // However, we should support 3rd party messaging apps that might need 16-bit + // references + // Note: It's not sufficient to just flip this bit to true; it will have + // ripple effects (several calculations assume 8-bit ref). + concatRef.isEightBits = true; + SmsHeader smsHeader = new SmsHeader(); + smsHeader.concatRef = concatRef; + + // Set the national language tables for 3GPP 7-bit encoding, if enabled. + if (encoding == android.telephony.SmsMessage.ENCODING_7BIT) { + smsHeader.languageTable = encodingForParts[i].languageTable; + smsHeader.languageShiftTable = encodingForParts[i].languageShiftTable; + } + + PendingIntent sentIntent = null; + if (sentIntents != null && sentIntents.size() > i) { + sentIntent = sentIntents.get(i); + } + + PendingIntent deliveryIntent = null; + if (deliveryIntents != null && deliveryIntents.size() > i) { + deliveryIntent = deliveryIntents.get(i); + } + + sendNewSubmitPdu(destAddr, scAddr, parts.get(i), smsHeader, encoding, + sentIntent, deliveryIntent, (i == (msgCount - 1))); + } + + } + + /** + * Create a new SubmitPdu and send it. + */ + protected abstract void sendNewSubmitPdu(String destinationAddress, String scAddress, + String message, SmsHeader smsHeader, int encoding, + PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart); /** * Send a SMS @@ -842,7 +888,7 @@ public abstract class SMSDispatcher extends Handler { handleNotInService(ss, tracker); } else { String appName = getAppNameByIntent(sentIntent); - if (mCounter.check(appName, SINGLE_PART_SMS)) { + if (mUsageMonitor.check(appName, SINGLE_PART_SMS)) { sendSms(tracker); } else { sendMessage(obtainMessage(EVENT_POST_ALERT, tracker)); @@ -885,7 +931,7 @@ public abstract class SMSDispatcher extends Handler { DEFAULT_SMS_TIMEOUT); } - protected String getAppNameByIntent(PendingIntent intent) { + protected static String getAppNameByIntent(PendingIntent intent) { Resources r = Resources.getSystem(); return (intent != null) ? intent.getTargetPackage() : r.getString(R.string.sms_control_default_app_name); @@ -903,7 +949,35 @@ public abstract class SMSDispatcher extends Handler { * * @param tracker holds the multipart Sms tracker ready to be sent */ - protected abstract void sendMultipartSms (SmsTracker tracker); + private void sendMultipartSms(SmsTracker tracker) { + ArrayList parts; + ArrayList sentIntents; + ArrayList deliveryIntents; + + HashMap map = tracker.mData; + + String destinationAddress = (String) map.get("destination"); + String scAddress = (String) map.get("scaddress"); + + parts = (ArrayList) map.get("parts"); + sentIntents = (ArrayList) map.get("sentIntents"); + deliveryIntents = (ArrayList) map.get("deliveryIntents"); + + // check if in service + int ss = mPhone.getServiceState().getState(); + if (ss != ServiceState.STATE_IN_SERVICE) { + for (int i = 0, count = parts.size(); i < count; i++) { + PendingIntent sentIntent = null; + if (sentIntents != null && sentIntents.size() > i) { + sentIntent = sentIntents.get(i); + } + handleNotInService(ss, new SmsTracker(null, sentIntent, null)); + } + return; + } + + sendMultipartText(destinationAddress, scAddress, parts, sentIntents, deliveryIntents); + } /** * Send an acknowledge message. @@ -933,67 +1007,39 @@ public abstract class SMSDispatcher extends Handler { acknowledgeLastIncomingSms(success, result, response); } - /** - * Check if a SmsTracker holds multi-part Sms - * - * @param tracker a SmsTracker could hold a multi-part Sms - * @return true for tracker holds Multi-parts Sms - */ - private boolean isMultipartTracker (SmsTracker tracker) { - HashMap map = tracker.mData; - return ( map.get("parts") != null); - } - /** * Keeps track of an SMS that has been sent to the RIL, until it has * successfully been sent, or we're done trying. * */ - static protected class SmsTracker { + protected static final class SmsTracker { // fields need to be public for derived SmsDispatchers - public HashMap mData; + public final HashMap mData; public int mRetryCount; public int mMessageRef; - public PendingIntent mSentIntent; - public PendingIntent mDeliveryIntent; + public final PendingIntent mSentIntent; + public final PendingIntent mDeliveryIntent; - SmsTracker(HashMap data, PendingIntent sentIntent, + public SmsTracker(HashMap data, PendingIntent sentIntent, PendingIntent deliveryIntent) { mData = data; mSentIntent = sentIntent; mDeliveryIntent = deliveryIntent; mRetryCount = 0; } + + /** + * Returns whether this tracker holds a multi-part SMS. + * @return true if the tracker holds a multi-part SMS; false otherwise + */ + protected boolean isMultipart() { + HashMap map = mData; + return map.containsKey("parts"); + } } - protected SmsTracker SmsTrackerFactory(HashMap data, PendingIntent sentIntent, - PendingIntent deliveryIntent) { - return new SmsTracker(data, sentIntent, deliveryIntent); - } - - public void initSipStack(boolean isObg) { - // This function should be overridden by the classes that support - // switching modes such as the CdmaSMSDispatcher. - // Not implemented in GsmSMSDispatcher. - Log.e(TAG, "Error! This function should never be executed."); - } - - public void switchToCdma() { - // This function should be overridden by the classes that support - // switching modes such as the CdmaSMSDispatcher. - // Not implemented in GsmSMSDispatcher. - Log.e(TAG, "Error! This function should never be executed."); - } - - public void switchToGsm() { - // This function should be overridden by the classes that support - // switching modes such as the CdmaSMSDispatcher. - // Not implemented in GsmSMSDispatcher. - Log.e(TAG, "Error! This function should never be executed."); - } - - private DialogInterface.OnClickListener mListener = + private final DialogInterface.OnClickListener mListener = new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { @@ -1007,42 +1053,32 @@ public abstract class SMSDispatcher extends Handler { } }; - private BroadcastReceiver mResultReceiver = new BroadcastReceiver() { + private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_FULL)) { - mStorageAvailable = false; - mCm.reportSmsMemoryStatus(false, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE)); - } else if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_NOT_FULL)) { - mStorageAvailable = true; - mCm.reportSmsMemoryStatus(true, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE)); - } else { - // Assume the intent is one of the SMS receive intents that - // was sent as an ordered broadcast. Check result and ACK. - int rc = getResultCode(); - boolean success = (rc == Activity.RESULT_OK) - || (rc == Intents.RESULT_SMS_HANDLED); + // Assume the intent is one of the SMS receive intents that + // was sent as an ordered broadcast. Check result and ACK. + int rc = getResultCode(); + boolean success = (rc == Activity.RESULT_OK) + || (rc == Intents.RESULT_SMS_HANDLED); - // For a multi-part message, this only ACKs the last part. - // Previous parts were ACK'd as they were received. - acknowledgeLastIncomingSms(success, rc, null); - } + // For a multi-part message, this only ACKs the last part. + // Previous parts were ACK'd as they were received. + acknowledgeLastIncomingSms(success, rc, null); } }; - protected abstract void handleBroadcastSms(AsyncResult ar); - protected void dispatchBroadcastPdus(byte[][] pdus, boolean isEmergencyMessage) { if (isEmergencyMessage) { Intent intent = new Intent(Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION); intent.putExtra("pdus", pdus); Log.d(TAG, "Dispatching " + pdus.length + " emergency SMS CB pdus"); - dispatch(intent, "android.permission.RECEIVE_EMERGENCY_BROADCAST"); + dispatch(intent, RECEIVE_EMERGENCY_BROADCAST_PERMISSION); } else { Intent intent = new Intent(Intents.SMS_CB_RECEIVED_ACTION); intent.putExtra("pdus", pdus); Log.d(TAG, "Dispatching " + pdus.length + " SMS CB pdus"); - dispatch(intent, "android.permission.RECEIVE_SMS"); + dispatch(intent, RECEIVE_SMS_PERMISSION); } } } diff --git a/telephony/java/com/android/internal/telephony/SmsStorageMonitor.java b/telephony/java/com/android/internal/telephony/SmsStorageMonitor.java new file mode 100644 index 0000000000000..0c06ffc87e39d --- /dev/null +++ b/telephony/java/com/android/internal/telephony/SmsStorageMonitor.java @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.AsyncResult; +import android.os.Handler; +import android.os.Message; +import android.os.PowerManager; +import android.provider.Telephony.Sms.Intents; +import android.util.Log; + +/** + * Monitors the device and ICC storage, and sends the appropriate events. + * + * This code was formerly part of {@link SMSDispatcher}, and has been moved + * into a separate class to support instantiation of multiple SMSDispatchers on + * dual-mode devices that require support for both 3GPP and 3GPP2 format messages. + */ +public final class SmsStorageMonitor extends Handler { + private static final String TAG = "SmsStorageMonitor"; + + /** SIM/RUIM storage is full */ + private static final int EVENT_ICC_FULL = 1; + + /** Memory status reporting is acknowledged by RIL */ + private static final int EVENT_REPORT_MEMORY_STATUS_DONE = 2; + + /** Radio is ON */ + private static final int EVENT_RADIO_ON = 3; + + /** Context from phone object passed to constructor. */ + private final Context mContext; + + /** Wake lock to ensure device stays awake while dispatching the SMS intent. */ + private PowerManager.WakeLock mWakeLock; + + private boolean mReportMemoryStatusPending; + + final CommandsInterface mCm; // accessed from inner class + boolean mStorageAvailable = true; // accessed from inner class + + /** + * Hold the wake lock for 5 seconds, which should be enough time for + * any receiver(s) to grab its own wake lock. + */ + private static final int WAKE_LOCK_TIMEOUT = 5000; + + /** + * Creates an SmsStorageMonitor and registers for events. + * @param phone the Phone to use + */ + public SmsStorageMonitor(PhoneBase phone) { + mContext = phone.getContext(); + mCm = phone.mCM; + + createWakelock(); + + mCm.setOnIccSmsFull(this, EVENT_ICC_FULL, null); + mCm.registerForOn(this, EVENT_RADIO_ON, null); + + // Register for device storage intents. Use these to notify the RIL + // that storage for SMS is or is not available. + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_DEVICE_STORAGE_FULL); + filter.addAction(Intent.ACTION_DEVICE_STORAGE_NOT_FULL); + mContext.registerReceiver(mResultReceiver, filter); + } + + public void dispose() { + mCm.unSetOnIccSmsFull(this); + mCm.unregisterForOn(this); + mContext.unregisterReceiver(mResultReceiver); + } + + /** + * Handles events coming from the phone stack. Overridden from handler. + * @param msg the message to handle + */ + @Override + public void handleMessage(Message msg) { + AsyncResult ar; + + switch (msg.what) { + case EVENT_ICC_FULL: + handleIccFull(); + break; + + case EVENT_REPORT_MEMORY_STATUS_DONE: + ar = (AsyncResult) msg.obj; + if (ar.exception != null) { + mReportMemoryStatusPending = true; + Log.v(TAG, "Memory status report to modem pending : mStorageAvailable = " + + mStorageAvailable); + } else { + mReportMemoryStatusPending = false; + } + break; + + case EVENT_RADIO_ON: + if (mReportMemoryStatusPending) { + Log.v(TAG, "Sending pending memory status report : mStorageAvailable = " + + mStorageAvailable); + mCm.reportSmsMemoryStatus(mStorageAvailable, + obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE)); + } + break; + } + } + + private void createWakelock() { + PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); + mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SmsStorageMonitor"); + mWakeLock.setReferenceCounted(true); + } + + /** + * Called when SIM_FULL message is received from the RIL. Notifies interested + * parties that SIM storage for SMS messages is full. + */ + private void handleIccFull() { + // broadcast SIM_FULL intent + Intent intent = new Intent(Intents.SIM_FULL_ACTION); + mWakeLock.acquire(WAKE_LOCK_TIMEOUT); + mContext.sendBroadcast(intent, SMSDispatcher.RECEIVE_SMS_PERMISSION); + } + + /** Returns whether or not there is storage available for an incoming SMS. */ + public boolean isStorageAvailable() { + return mStorageAvailable; + } + + private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_FULL)) { + mStorageAvailable = false; + mCm.reportSmsMemoryStatus(false, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE)); + } else if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_NOT_FULL)) { + mStorageAvailable = true; + mCm.reportSmsMemoryStatus(true, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE)); + } + } + }; +} diff --git a/telephony/java/com/android/internal/telephony/SmsUsageMonitor.java b/telephony/java/com/android/internal/telephony/SmsUsageMonitor.java new file mode 100644 index 0000000000000..bd2ae8b423e42 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/SmsUsageMonitor.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony; + +import android.content.ContentResolver; +import android.provider.Settings; +import android.util.Log; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +/** + * Implement the per-application based SMS control, which limits the number of + * SMS/MMS messages an app can send in the checking period. + * + * This code was formerly part of {@link SMSDispatcher}, and has been moved + * into a separate class to support instantiation of multiple SMSDispatchers on + * dual-mode devices that require support for both 3GPP and 3GPP2 format messages. + */ +public class SmsUsageMonitor { + private static final String TAG = "SmsStorageMonitor"; + + /** Default checking period for SMS sent without user permission. */ + private static final int DEFAULT_SMS_CHECK_PERIOD = 3600000; + + /** Default number of SMS sent in checking period without user permission. */ + private static final int DEFAULT_SMS_MAX_COUNT = 100; + + private final int mCheckPeriod; + private final int mMaxAllowed; + private final HashMap> mSmsStamp = + new HashMap>(); + + /** + * Create SMS usage monitor. + * @param resolver the ContentResolver to use to load from secure settings + */ + public SmsUsageMonitor(ContentResolver resolver) { + mMaxAllowed = Settings.Secure.getInt(resolver, + Settings.Secure.SMS_OUTGOING_CHECK_MAX_COUNT, + DEFAULT_SMS_MAX_COUNT); + + mCheckPeriod = Settings.Secure.getInt(resolver, + Settings.Secure.SMS_OUTGOING_CHECK_INTERVAL_MS, + DEFAULT_SMS_CHECK_PERIOD); + } + + /** Clear the SMS application list for disposal. */ + void dispose() { + mSmsStamp.clear(); + } + + /** + * Check to see if an application is allowed to send new SMS messages. + * + * @param appName the application sending sms + * @param smsWaiting the number of new messages desired to send + * @return true if application is allowed to send the requested number + * of new sms messages + */ + public boolean check(String appName, int smsWaiting) { + synchronized (mSmsStamp) { + removeExpiredTimestamps(); + + ArrayList sentList = mSmsStamp.get(appName); + if (sentList == null) { + sentList = new ArrayList(); + mSmsStamp.put(appName, sentList); + } + + return isUnderLimit(sentList, smsWaiting); + } + } + + /** + * Remove keys containing only old timestamps. This can happen if an SMS app is used + * to send messages and then uninstalled. + */ + private void removeExpiredTimestamps() { + long beginCheckPeriod = System.currentTimeMillis() - mCheckPeriod; + + synchronized (mSmsStamp) { + Iterator>> iter = mSmsStamp.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry> entry = iter.next(); + ArrayList oldList = entry.getValue(); + if (oldList.isEmpty() || oldList.get(oldList.size() - 1) < beginCheckPeriod) { + iter.remove(); + } + } + } + } + + private boolean isUnderLimit(ArrayList sent, int smsWaiting) { + Long ct = System.currentTimeMillis(); + long beginCheckPeriod = ct - mCheckPeriod; + + Log.d(TAG, "SMS send size=" + sent.size() + " time=" + ct); + + while (!sent.isEmpty() && sent.get(0) < beginCheckPeriod) { + sent.remove(0); + } + + if ((sent.size() + smsWaiting) <= mMaxAllowed) { + for (int i = 0; i < smsWaiting; i++ ) { + sent.add(ct); + } + return true; + } + return false; + } +} diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java index 6903025f9dd9f..c2b9e4f3b22e5 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java +++ b/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java @@ -27,6 +27,9 @@ import android.util.Log; import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneNotifier; +import com.android.internal.telephony.PhoneProxy; +import com.android.internal.telephony.SMSDispatcher; +import com.android.internal.telephony.gsm.GsmSMSDispatcher; import com.android.internal.telephony.gsm.SimCard; import com.android.internal.telephony.ims.IsimRecords; @@ -35,14 +38,13 @@ public class CDMALTEPhone extends CDMAPhone { private static final boolean DBG = true; + /** Secondary SMSDispatcher for 3GPP format messages. */ + SMSDispatcher m3gppSMS; + // Constructors public CDMALTEPhone(Context context, CommandsInterface ci, PhoneNotifier notifier) { - this(context, ci, notifier, false); - } - - public CDMALTEPhone(Context context, CommandsInterface ci, PhoneNotifier notifier, - boolean unitTestMode) { super(context, ci, notifier, false); + m3gppSMS = new GsmSMSDispatcher(this, mSmsStorageMonitor, mSmsUsageMonitor); } @Override @@ -53,6 +55,20 @@ public class CDMALTEPhone extends CDMAPhone { mIccFileHandler = new CdmaLteUiccFileHandler(this); } + @Override + public void dispose() { + synchronized(PhoneProxy.lockForRadioTechnologyChange) { + super.dispose(); + m3gppSMS.dispose(); + } + } + + @Override + public void removeReferences() { + super.removeReferences(); + m3gppSMS = null; + } + @Override public DataState getDataConnectionState(String apnType) { DataState ret = DataState.DISCONNECTED; @@ -92,13 +108,15 @@ public class CDMALTEPhone extends CDMAPhone { return ret; } + @Override public boolean updateCurrentCarrierInProvider() { if (mIccRecords != null) { try { Uri uri = Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current"); ContentValues map = new ContentValues(); - map.put(Telephony.Carriers.NUMERIC, mIccRecords.getOperatorNumeric()); - log("updateCurrentCarrierInProvider insert uri=" + uri); + String operatorNumeric = mIccRecords.getOperatorNumeric(); + map.put(Telephony.Carriers.NUMERIC, operatorNumeric); + log("updateCurrentCarrierInProvider from UICC: numeric=" + operatorNumeric); mContext.getContentResolver().insert(uri, map); return true; } catch (SQLException e) { diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java index 286515eb6c4d7..09ee28c9a448f 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java +++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java @@ -17,10 +17,9 @@ package com.android.internal.telephony.cdma; import android.app.ActivityManagerNative; -import android.content.Context; import android.content.ContentValues; +import android.content.Context; import android.content.Intent; -import android.content.res.Configuration; import android.content.SharedPreferences; import android.database.SQLException; import android.net.Uri; @@ -31,7 +30,6 @@ import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.os.Registrant; import android.os.RegistrantList; -import android.os.RemoteException; import android.os.SystemProperties; import android.preference.PreferenceManager; import android.provider.Telephony; @@ -42,20 +40,17 @@ 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.CallTracker; import com.android.internal.telephony.CommandException; import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.Connection; -import com.android.internal.telephony.DataConnection; -import com.android.internal.telephony.IccRecords; -import com.android.internal.telephony.MccTable; -import com.android.internal.telephony.IccCard; import com.android.internal.telephony.IccException; import com.android.internal.telephony.IccFileHandler; import com.android.internal.telephony.IccPhoneBookInterfaceManager; import com.android.internal.telephony.IccSmsInterfaceManager; +import com.android.internal.telephony.MccTable; import com.android.internal.telephony.MmiCode; import com.android.internal.telephony.OperatorInfo; import com.android.internal.telephony.Phone; @@ -67,19 +62,17 @@ import com.android.internal.telephony.ServiceStateTracker; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.telephony.TelephonyProperties; import com.android.internal.telephony.UUSInfo; -import com.android.internal.telephony.CallTracker; - -import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA; -import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC; -import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY; +import com.android.internal.telephony.cat.CatService; import java.util.ArrayList; import java.util.List; - - import java.util.regex.Matcher; import java.util.regex.Pattern; +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA; +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY; +import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC; + /** * {@hide} */ @@ -109,13 +102,13 @@ public class CDMAPhone extends PhoneBase { CatService mCcatService; // mNvLoadedRegistrants are informed after the EVENT_NV_READY - private RegistrantList mNvLoadedRegistrants = new RegistrantList(); + private final RegistrantList mNvLoadedRegistrants = new RegistrantList(); // mEriFileLoadedRegistrants are informed after the ERI text has been loaded - private RegistrantList mEriFileLoadedRegistrants = new RegistrantList(); + private final RegistrantList mEriFileLoadedRegistrants = new RegistrantList(); // mEcmTimerResetRegistrants are informed after Ecm timer is canceled or re-started - private RegistrantList mEcmTimerResetRegistrants = new RegistrantList(); + private final RegistrantList mEcmTimerResetRegistrants = new RegistrantList(); // mEcmExitRespRegistrant is informed after the phone has been exited //the emergency callback mode @@ -131,6 +124,7 @@ public class CDMAPhone extends PhoneBase { // A runnable which is used to automatically exit from Ecm after a period of time. private Runnable mExitEcmRunnable = new Runnable() { + @Override public void run() { exitEmergencyCallbackMode(); } @@ -164,7 +158,7 @@ public class CDMAPhone extends PhoneBase { protected void init(Context context, PhoneNotifier notifier) { mCM.setPhoneType(Phone.PHONE_TYPE_CDMA); mCT = new CdmaCallTracker(this); - mSMS = new CdmaSMSDispatcher(this); + mSMS = new CdmaSMSDispatcher(this, mSmsStorageMonitor, mSmsUsageMonitor); mDataConnectionTracker = new CdmaDataConnectionTracker (this); mRuimPhoneBookInterfaceManager = new RuimPhoneBookInterfaceManager(this); mRuimSmsInterfaceManager = new RuimSmsInterfaceManager(this, mSMS); @@ -188,7 +182,7 @@ public class CDMAPhone extends PhoneBase { //Change the system setting SystemProperties.set(TelephonyProperties.CURRENT_ACTIVE_PHONE, - new Integer(Phone.PHONE_TYPE_CDMA).toString()); + Integer.toString(Phone.PHONE_TYPE_CDMA)); // This is needed to handle phone process crashes String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false"); @@ -220,6 +214,7 @@ public class CDMAPhone extends PhoneBase { notifier.notifyMessageWaitingChanged(this); } + @Override public void dispose() { synchronized(PhoneProxy.lockForRadioTechnologyChange) { super.dispose(); @@ -253,23 +248,26 @@ public class CDMAPhone extends PhoneBase { } } + @Override public void removeReferences() { - log("removeReferences"); - this.mRuimPhoneBookInterfaceManager = null; - this.mRuimSmsInterfaceManager = null; - this.mSMS = null; - this.mSubInfo = null; - this.mIccRecords = null; - this.mIccFileHandler = null; - this.mIccCard = null; - this.mDataConnectionTracker = null; - this.mCT = null; - this.mSST = null; - this.mEriManager = null; - this.mCcatService = null; - this.mExitEcmRunnable = null; + log("removeReferences"); + super.removeReferences(); + mRuimPhoneBookInterfaceManager = null; + mRuimSmsInterfaceManager = null; + mSMS = null; + mSubInfo = null; + mIccRecords = null; + mIccFileHandler = null; + mIccCard = null; + mDataConnectionTracker = null; + mCT = null; + mSST = null; + mEriManager = null; + mCcatService = null; + mExitEcmRunnable = null; } + @Override protected void finalize() { if(DBG) Log.d(LOG_TAG, "CDMAPhone finalized"); if (mWakeLock.isHeld()) { @@ -813,7 +811,7 @@ public class CDMAPhone extends PhoneBase { return null; } - /** + /** * Notify any interested party of a Phone state change {@link Phone.State} */ /*package*/ void notifyPhoneStateChanged() { @@ -858,18 +856,6 @@ public class CDMAPhone extends PhoneBase { if (DBG) Log.d(LOG_TAG, "sendEmergencyCallbackModeChange"); } - /*package*/ void - updateMessageWaitingIndicator(boolean mwi) { - // this also calls notifyMessageWaitingIndicator() - mIccRecords.setVoiceMessageWaiting(1, mwi ? -1 : 0); - } - - /* This function is overloaded to send number of voicemails instead of sending true/false */ - /*package*/ void - updateMessageWaitingIndicator(int mwi) { - mIccRecords.setVoiceMessageWaiting(1, mwi); - } - @Override public void exitEmergencyCallbackMode() { if (mWakeLock.isHeld()) { @@ -1013,6 +999,7 @@ public class CDMAPhone extends PhoneBase { case EVENT_RUIM_RECORDS_LOADED:{ Log.d(LOG_TAG, "Event EVENT_RUIM_RECORDS_LOADED Received"); + updateCurrentCarrierInProvider(); } break; @@ -1172,7 +1159,7 @@ public class CDMAPhone extends PhoneBase { private static final int IS683_CONST_1900MHZ_F_BLOCK = 7; private static final int INVALID_SYSTEM_SELECTION_CODE = -1; - private boolean isIs683OtaSpDialStr(String dialStr) { + private static boolean isIs683OtaSpDialStr(String dialStr) { int sysSelCodeInt; boolean isOtaspDialString = false; int dialStrLen = dialStr.length(); @@ -1203,7 +1190,7 @@ public class CDMAPhone extends PhoneBase { /** * This function extracts the system selection code from the dial string. */ - private int extractSelCodeFromOtaSpNum(String dialStr) { + private static int extractSelCodeFromOtaSpNum(String dialStr) { int dialStrLen = dialStr.length(); int sysSelCodeInt = INVALID_SYSTEM_SELECTION_CODE; @@ -1226,7 +1213,7 @@ public class CDMAPhone extends PhoneBase { * the dial string "sysSelCodeInt' is the system selection code specified * in the carrier ota sp number schema "sch". */ - private boolean + private static boolean checkOtaSpNumBasedOnSysSelCode (int sysSelCodeInt, String sch[]) { boolean isOtaSpNum = false; try { @@ -1414,7 +1401,7 @@ public class CDMAPhone extends PhoneBase { Uri uri = Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current"); ContentValues map = new ContentValues(); map.put(Telephony.Carriers.NUMERIC, operatorNumeric); - log("updateCurrentCarrierInProvider insert uri=" + uri); + log("updateCurrentCarrierInProvider from system: numeric=" + operatorNumeric); getContext().getContentResolver().insert(uri, map); // Updates MCC MNC device configuration information @@ -1428,6 +1415,16 @@ public class CDMAPhone extends PhoneBase { return false; } + /** + * Sets the "current" field in the telephony provider according to the SIM's operator. + * Implemented in {@link CDMALTEPhone} for CDMA/LTE devices. + * + * @return true for success; false otherwise. + */ + boolean updateCurrentCarrierInProvider() { + return true; + } + public void prepareEri() { mEriManager.loadEriFile(); if(mEriManager.isEriFileLoaded()) { diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java index 0617fee741f13..47c638f62c1e9 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java @@ -26,6 +26,7 @@ import com.android.internal.telephony.IccFileHandler; import com.android.internal.telephony.IccUtils; import com.android.internal.telephony.MccTable; import com.android.internal.telephony.PhoneBase; +import com.android.internal.telephony.SmsMessageBase; import com.android.internal.telephony.cdma.sms.UserData; import com.android.internal.telephony.gsm.SIMRecords; import com.android.internal.telephony.ims.IsimRecords; @@ -438,4 +439,13 @@ public final class CdmaLteUiccRecords extends SIMRecords { } return true; } + + /** + * Dispatch 3GPP format message. For CDMA/LTE phones, + * send the message to the secondary 3GPP format SMS dispatcher. + */ + @Override + protected int dispatchGsmMessage(SmsMessageBase message) { + return ((CDMALTEPhone) phone).m3gppSMS.dispatchMessage(message); + } } diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java index 07b0f4f993c26..dded39e1a6a37 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java @@ -25,7 +25,6 @@ import android.content.Intent; import android.content.SharedPreferences; import android.database.Cursor; import android.database.SQLException; -import android.os.AsyncResult; import android.os.Message; import android.os.SystemProperties; import android.preference.PreferenceManager; @@ -40,6 +39,8 @@ 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.SmsStorageMonitor; +import com.android.internal.telephony.SmsUsageMonitor; import com.android.internal.telephony.TelephonyProperties; import com.android.internal.telephony.WspTypeDecoder; import com.android.internal.telephony.cdma.sms.SmsEnvelope; @@ -47,7 +48,6 @@ import com.android.internal.telephony.cdma.sms.UserData; import com.android.internal.util.HexDump; import java.io.ByteArrayOutputStream; -import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -60,24 +60,23 @@ final class CdmaSMSDispatcher extends SMSDispatcher { private byte[] mLastDispatchedSmsFingerprint; private byte[] mLastAcknowledgedSmsFingerprint; - private boolean mCheckForDuplicatePortsInOmadmWapPush = Resources.getSystem().getBoolean( + private final boolean mCheckForDuplicatePortsInOmadmWapPush = Resources.getSystem().getBoolean( com.android.internal.R.bool.config_duplicate_port_omadm_wappush); - CdmaSMSDispatcher(CDMAPhone phone) { - super(phone); + CdmaSMSDispatcher(CDMAPhone phone, SmsStorageMonitor storageMonitor, + SmsUsageMonitor usageMonitor) { + super(phone, storageMonitor, usageMonitor); + mCm.setOnNewCdmaSms(this, EVENT_NEW_SMS, null); } - /** - * Called when a status report is received. This should correspond to - * a previously successful SEND. - * Is a special GSM function, should never be called in CDMA!! - * - * @param ar AsyncResult passed into the message handler. ar.result should - * be a String representing the status report PDU, as ASCII hex. - */ @Override - protected void handleStatusReport(AsyncResult ar) { - Log.d(TAG, "handleStatusReport is a special GSM function, should never be called in CDMA!"); + public void dispose() { + mCm.unSetOnNewCdmaSms(this); + } + + @Override + protected String getFormat() { + return android.telephony.SmsMessage.FORMAT_3GPP2; } private void handleCdmaStatusReport(SmsMessage sms) { @@ -138,11 +137,11 @@ final class CdmaSMSDispatcher extends SMSDispatcher { Log.d(TAG, "Voicemail count=" + voicemailCount); // Store the voicemail count in preferences. SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences( - mPhone.getContext()); + mContext); SharedPreferences.Editor editor = sp.edit(); editor.putInt(CDMAPhone.VM_COUNT_CDMA, voicemailCount); editor.apply(); - ((CDMAPhone) mPhone).updateMessageWaitingIndicator(voicemailCount); + mPhone.setVoiceMessageWaiting(1, voicemailCount); handled = true; } else if (((SmsEnvelope.TELESERVICE_WMT == teleService) || (SmsEnvelope.TELESERVICE_WEMT == teleService)) && @@ -160,7 +159,8 @@ final class CdmaSMSDispatcher extends SMSDispatcher { return Intents.RESULT_SMS_HANDLED; } - if (!mStorageAvailable && (sms.getMessageClass() != MessageClass.CLASS_0)) { + if (!mStorageMonitor.isStorageAvailable() && + sms.getMessageClass() != MessageClass.CLASS_0) { // It's a storable message and there's no storage available. Bail. // (See C.S0015-B v2.0 for a description of "Immediate Display" // messages, which we represent as CLASS_0.) @@ -181,48 +181,7 @@ final class CdmaSMSDispatcher extends SMSDispatcher { return Intents.RESULT_SMS_UNSUPPORTED; } - /* - * TODO(cleanup): Why are we using a getter method for this - * (and for so many other sms fields)? Trivial getters and - * setters like this are direct violations of the style guide. - * If the purpose is to protect against writes (by not - * providing a setter) then any protection is illusory (and - * hence bad) for cases where the values are not primitives, - * such as this call for the header. Since this is an issue - * with the public API it cannot be changed easily, but maybe - * something can be done eventually. - */ - SmsHeader smsHeader = sms.getUserDataHeader(); - - /* - * TODO(cleanup): Since both CDMA and GSM use the same header - * format, this dispatch processing is naturally identical, - * and code should probably not be replicated explicitly. - */ - - // See if message is partial or port addressed. - if ((smsHeader == null) || (smsHeader.concatRef == null)) { - // Message is not partial (not part of concatenated sequence). - byte[][] pdus = new byte[1][]; - pdus[0] = sms.getPdu(); - - if (smsHeader != null && smsHeader.portAddrs != null) { - if (smsHeader.portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) { - // GSM-style WAP indication - return mWapPush.dispatchWapPdu(sms.getUserData()); - } else { - // The message was sent to a port, so concoct a URI for it. - dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort); - } - } else { - // Normal short and non-port-addressed message, dispatch it. - dispatchPdus(pdus); - } - return Activity.RESULT_OK; - } else { - // Process the message part. - return processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs); - } + return dispatchNormalMessage(smsb); } /** @@ -236,23 +195,19 @@ final class CdmaSMSDispatcher extends SMSDispatcher { * to applications */ protected int processCdmaWapPdu(byte[] pdu, int referenceNumber, String address) { - int segment; - int totalSegments; int index = 0; - int msgType; - int sourcePort = 0; - int destinationPort = 0; - - msgType = pdu[index++]; - if (msgType != 0){ + int msgType = pdu[index++]; + if (msgType != 0) { Log.w(TAG, "Received a WAP SMS which is not WDP. Discard."); return Intents.RESULT_SMS_HANDLED; } - totalSegments = pdu[index++]; // >=1 - segment = pdu[index++]; // >=0 + int totalSegments = pdu[index++]; // >= 1 + int segment = pdu[index++]; // >= 0 // Only the first segment contains sourcePort and destination Port + int sourcePort = 0; + int destinationPort = 0; if (segment == 0) { //process WDP segment sourcePort = (0xFF & pdu[index++]) << 8; @@ -269,90 +224,16 @@ final class CdmaSMSDispatcher extends SMSDispatcher { } // Lookup all other related parts - StringBuilder where = new StringBuilder("reference_number ="); - where.append(referenceNumber); - where.append(" AND address = ?"); - String[] whereArgs = new String[] {address}; - Log.i(TAG, "Received WAP PDU. Type = " + msgType + ", originator = " + address + ", src-port = " + sourcePort + ", dst-port = " + destinationPort - + ", ID = " + referenceNumber + ", segment# = " + segment + "/" + totalSegments); + + ", ID = " + referenceNumber + ", segment# = " + segment + '/' + totalSegments); - byte[][] pdus = null; - Cursor cursor = null; - try { - cursor = mResolver.query(mRawUri, RAW_PROJECTION, where.toString(), whereArgs, null); - int cursorCount = cursor.getCount(); - if (cursorCount != totalSegments - 1) { - // We don't have all the parts yet, store this one away - ContentValues values = new ContentValues(); - values.put("date", (long) 0); - values.put("pdu", HexDump.toHexString(pdu, index, pdu.length - index)); - values.put("address", address); - values.put("reference_number", referenceNumber); - values.put("count", totalSegments); - values.put("sequence", segment); - values.put("destination_port", destinationPort); + // pass the user data portion of the PDU to the shared handler in SMSDispatcher + byte[] userData = new byte[pdu.length - index]; + System.arraycopy(pdu, index, userData, 0, pdu.length - index); - mResolver.insert(mRawUri, values); - - return Intents.RESULT_SMS_HANDLED; - } - - // All the parts are in place, deal with them - int pduColumn = cursor.getColumnIndex("pdu"); - int sequenceColumn = cursor.getColumnIndex("sequence"); - - pdus = new byte[totalSegments][]; - for (int i = 0; i < cursorCount; i++) { - cursor.moveToNext(); - int cursorSequence = (int)cursor.getLong(sequenceColumn); - // Read the destination port from the first segment - if (cursorSequence == 0) { - int destinationPortColumn = cursor.getColumnIndex("destination_port"); - destinationPort = (int)cursor.getLong(destinationPortColumn); - } - pdus[cursorSequence] = HexDump.hexStringToByteArray( - cursor.getString(pduColumn)); - } - // The last part will be added later - - // Remove the parts from the database - mResolver.delete(mRawUri, where.toString(), whereArgs); - } catch (SQLException e) { - Log.e(TAG, "Can't access multipart SMS database", e); - return Intents.RESULT_SMS_GENERIC_ERROR; - } finally { - if (cursor != null) cursor.close(); - } - - // Build up the data stream - ByteArrayOutputStream output = new ByteArrayOutputStream(); - for (int i = 0; i < totalSegments; i++) { - // reassemble the (WSP-)pdu - if (i == segment) { - // This one isn't in the DB, so add it - output.write(pdu, index, pdu.length - index); - } else { - output.write(pdus[i], 0, pdus[i].length); - } - } - - byte[] datagram = output.toByteArray(); - // Dispatch the PDU to applications - switch (destinationPort) { - case SmsHeader.PORT_WAP_PUSH: - // Handle the PUSH - return mWapPush.dispatchWapPdu(datagram); - - default:{ - pdus = new byte[1][]; - pdus[0] = datagram; - // The messages were sent to any other WAP port - dispatchPortAddressedPdus(pdus, destinationPort); - return Activity.RESULT_OK; - } - } + return processMessagePart(userData, address, referenceNumber, segment, totalSegments, + 0L, destinationPort, true); } /** {@inheritDoc} */ @@ -375,68 +256,34 @@ final class CdmaSMSDispatcher extends SMSDispatcher { /** {@inheritDoc} */ @Override - protected void sendMultipartText(String destAddr, String scAddr, - ArrayList parts, ArrayList sentIntents, - ArrayList deliveryIntents) { + protected TextEncodingDetails calculateLength(CharSequence messageBody, + boolean use7bitOnly) { + return SmsMessage.calculateLength(messageBody, use7bitOnly); + } - /** - * TODO(cleanup): There is no real code difference between - * this and the GSM version, and hence it should be moved to - * the base class or consolidated somehow, provided calling - * the proper submit pdu stuff can be arranged. - */ - - int refNumber = getNextConcatenatedRef() & 0x00FF; - int msgCount = parts.size(); - int encoding = android.telephony.SmsMessage.ENCODING_UNKNOWN; - - for (int i = 0; i < msgCount; i++) { - TextEncodingDetails details = SmsMessage.calculateLength(parts.get(i), false); - if (encoding != details.codeUnitSize - && (encoding == android.telephony.SmsMessage.ENCODING_UNKNOWN - || encoding == android.telephony.SmsMessage.ENCODING_7BIT)) { - encoding = details.codeUnitSize; - } + /** {@inheritDoc} */ + @Override + protected void sendNewSubmitPdu(String destinationAddress, String scAddress, + String message, SmsHeader smsHeader, int encoding, + PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart) { + UserData uData = new UserData(); + uData.payloadStr = message; + uData.userDataHeader = smsHeader; + if (encoding == android.telephony.SmsMessage.ENCODING_7BIT) { + uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET; + } else { // assume UTF-16 + uData.msgEncoding = UserData.ENCODING_UNICODE_16; } + uData.msgEncodingSet = true; - for (int i = 0; i < msgCount; i++) { - SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef(); - concatRef.refNumber = refNumber; - concatRef.seqNumber = i + 1; // 1-based sequence - concatRef.msgCount = msgCount; - concatRef.isEightBits = true; - SmsHeader smsHeader = new SmsHeader(); - smsHeader.concatRef = concatRef; + /* By setting the statusReportRequested bit only for the + * last message fragment, this will result in only one + * callback to the sender when that last fragment delivery + * has been acknowledged. */ + SmsMessage.SubmitPdu submitPdu = SmsMessage.getSubmitPdu(destinationAddress, + uData, (deliveryIntent != null) && lastPart); - PendingIntent sentIntent = null; - if (sentIntents != null && sentIntents.size() > i) { - sentIntent = sentIntents.get(i); - } - - PendingIntent deliveryIntent = null; - if (deliveryIntents != null && deliveryIntents.size() > i) { - deliveryIntent = deliveryIntents.get(i); - } - - UserData uData = new UserData(); - uData.payloadStr = parts.get(i); - uData.userDataHeader = smsHeader; - if (encoding == android.telephony.SmsMessage.ENCODING_7BIT) { - uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET; - } else { // assume UTF-16 - uData.msgEncoding = UserData.ENCODING_UNICODE_16; - } - uData.msgEncodingSet = true; - - /* By setting the statusReportRequested bit only for the - * last message fragment, this will result in only one - * callback to the sender when that last fragment delivery - * has been acknowledged. */ - SmsMessage.SubmitPdu submitPdu = SmsMessage.getSubmitPdu(destAddr, - uData, (deliveryIntent != null) && (i == (msgCount - 1))); - - sendSubmitPdu(submitPdu, sentIntent, deliveryIntent); - } + sendSubmitPdu(submitPdu, sentIntent, deliveryIntent); } protected void sendSubmitPdu(SmsMessage.SubmitPdu pdu, @@ -464,43 +311,27 @@ final class CdmaSMSDispatcher extends SMSDispatcher { byte pdu[] = (byte[]) map.get("pdu"); Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker); - mCm.sendCdmaSms(pdu, reply); } - /** {@inheritDoc} */ - @Override - protected void sendMultipartSms (SmsTracker tracker) { - Log.d(TAG, "TODO: CdmaSMSDispatcher.sendMultipartSms not implemented"); - } - /** {@inheritDoc} */ @Override - protected void acknowledgeLastIncomingSms(boolean success, int result, Message response){ - // FIXME unit test leaves cm == null. this should change - + protected void acknowledgeLastIncomingSms(boolean success, int result, Message response) { String inEcm=SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE, "false"); if (inEcm.equals("true")) { return; } - if (mCm != null) { - int causeCode = resultToCause(result); - mCm.acknowledgeLastIncomingCdmaSms(success, causeCode, response); + int causeCode = resultToCause(result); + mCm.acknowledgeLastIncomingCdmaSms(success, causeCode, response); - if (causeCode == 0) { - mLastAcknowledgedSmsFingerprint = mLastDispatchedSmsFingerprint; - } - mLastDispatchedSmsFingerprint = null; + if (causeCode == 0) { + mLastAcknowledgedSmsFingerprint = mLastDispatchedSmsFingerprint; } + mLastDispatchedSmsFingerprint = null; } - protected void handleBroadcastSms(AsyncResult ar) { - // Not supported - Log.e(TAG, "Error! Not implemented for CDMA."); - } - - private int resultToCause(int rc) { + private static int resultToCause(int rc) { switch (rc) { case Activity.RESULT_OK: case Intents.RESULT_SMS_HANDLED: @@ -527,7 +358,7 @@ final class CdmaSMSDispatcher extends SMSDispatcher { * @return True if OrigPdu is OmaDM Push Message which has duplicate ports. * False if OrigPdu is NOT OmaDM Push Message which has duplicate ports. */ - private boolean checkDuplicatePortOmadmWappush(byte[] origPdu, int index) { + private static boolean checkDuplicatePortOmadmWappush(byte[] origPdu, int index) { index += 4; byte[] omaPdu = new byte[origPdu.length - index]; System.arraycopy(origPdu, index, omaPdu, 0, omaPdu.length); diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java index be5c61600cb4a..1409cab2aa2a2 100644 --- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java @@ -113,30 +113,6 @@ public class SmsMessage extends SmsMessageBase { } } - /** - * Note: This function is a GSM specific functionality which is not supported in CDMA mode. - */ - public static SmsMessage newFromCMT(String[] lines) { - Log.w(LOG_TAG, "newFromCMT: is not supported in CDMA mode."); - return null; - } - - /** - * Note: This function is a GSM specific functionality which is not supported in CDMA mode. - */ - public static SmsMessage newFromCMTI(String line) { - Log.w(LOG_TAG, "newFromCMTI: is not supported in CDMA mode."); - return null; - } - - /** - * Note: This function is a GSM specific functionality which is not supported in CDMA mode. - */ - public static SmsMessage newFromCDS(String line) { - Log.w(LOG_TAG, "newFromCDS: is not supported in CDMA mode."); - return null; - } - /** * Create a "raw" CDMA SmsMessage from a Parcel that was forged in ril.cpp. * Note: Only primitive fields are set. diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java index d325aaa5e6e2a..e1f4c4bd28b01 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java +++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java @@ -56,9 +56,6 @@ import com.android.internal.telephony.CallForwardInfo; import com.android.internal.telephony.CallStateException; import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.Connection; -import com.android.internal.telephony.DataConnection; -import com.android.internal.telephony.DataConnectionTracker; -import com.android.internal.telephony.IccCard; import com.android.internal.telephony.IccFileHandler; import com.android.internal.telephony.IccPhoneBookInterfaceManager; import com.android.internal.telephony.IccSmsInterfaceManager; @@ -140,7 +137,7 @@ public class GSMPhone extends PhoneBase { mCM.setPhoneType(Phone.PHONE_TYPE_GSM); mCT = new GsmCallTracker(this); mSST = new GsmServiceStateTracker (this); - mSMS = new GsmSMSDispatcher(this); + mSMS = new GsmSMSDispatcher(this, mSmsStorageMonitor, mSmsUsageMonitor); mIccFileHandler = new SIMFileHandler(this); mIccRecords = new SIMRecords(this); mDataConnectionTracker = new GsmDataConnectionTracker (this); @@ -199,6 +196,7 @@ public class GSMPhone extends PhoneBase { new Integer(Phone.PHONE_TYPE_GSM).toString()); } + @Override public void dispose() { synchronized(PhoneProxy.lockForRadioTechnologyChange) { super.dispose(); @@ -228,19 +226,22 @@ public class GSMPhone extends PhoneBase { } } + @Override public void removeReferences() { - this.mSimulatedRadioControl = null; - this.mStkService = null; - this.mSimPhoneBookIntManager = null; - this.mSimSmsIntManager = null; - this.mSMS = null; - this.mSubInfo = null; - this.mIccRecords = null; - this.mIccFileHandler = null; - this.mIccCard = null; - this.mDataConnectionTracker = null; - this.mCT = null; - this.mSST = null; + Log.d(LOG_TAG, "removeReferences"); + super.removeReferences(); + mSimulatedRadioControl = null; + mStkService = null; + mSimPhoneBookIntManager = null; + mSimSmsIntManager = null; + mSMS = null; + mSubInfo = null; + mIccRecords = null; + mIccFileHandler = null; + mIccCard = null; + mDataConnectionTracker = null; + mCT = null; + mSST = null; } protected void finalize() { @@ -405,17 +406,6 @@ public class GSMPhone extends PhoneBase { mNotifier.notifySignalStrength(this); } - public void - notifyDataConnectionFailed(String reason, String apnType) { - mNotifier.notifyDataConnectionFailed(this, reason, apnType); - } - - /*package*/ void - updateMessageWaitingIndicator(boolean mwi) { - // this also calls notifyMessageWaitingIndicator() - mIccRecords.setVoiceMessageWaiting(1, mwi ? -1 : 0); - } - public void notifyCallForwardingIndicator() { mNotifier.notifyCallForwardingChanged(this); diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java index 52ca453047140..4e1cc9afd13f6 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java @@ -30,13 +30,15 @@ import android.telephony.SmsCbMessage; import android.telephony.gsm.GsmCellLocation; 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.PhoneBase; 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.SmsStorageMonitor; +import com.android.internal.telephony.SmsUsageMonitor; import com.android.internal.telephony.TelephonyProperties; import java.util.ArrayList; @@ -45,16 +47,55 @@ import java.util.Iterator; import static android.telephony.SmsMessage.MessageClass; -final class GsmSMSDispatcher extends SMSDispatcher { +public final class GsmSMSDispatcher extends SMSDispatcher { private static final String TAG = "GSM"; - private GSMPhone mGsmPhone; + /** Status report received */ + private static final int EVENT_NEW_SMS_STATUS_REPORT = 100; - GsmSMSDispatcher(GSMPhone phone) { - super(phone); - mGsmPhone = phone; + /** New broadcast SMS */ + private static final int EVENT_NEW_BROADCAST_SMS = 101; - ((BaseCommands)mCm).setOnNewGsmBroadcastSms(this, EVENT_NEW_BROADCAST_SMS, null); + public GsmSMSDispatcher(PhoneBase phone, SmsStorageMonitor storageMonitor, + SmsUsageMonitor usageMonitor) { + super(phone, storageMonitor, usageMonitor); + mCm.setOnNewGsmSms(this, EVENT_NEW_SMS, null); + mCm.setOnSmsStatus(this, EVENT_NEW_SMS_STATUS_REPORT, null); + mCm.setOnNewGsmBroadcastSms(this, EVENT_NEW_BROADCAST_SMS, null); + } + + @Override + public void dispose() { + mCm.unSetOnNewGsmSms(this); + mCm.unSetOnSmsStatus(this); + mCm.unSetOnNewGsmBroadcastSms(this); + } + + @Override + protected String getFormat() { + return android.telephony.SmsMessage.FORMAT_3GPP; + } + + /** + * Handles 3GPP format-specific events coming from the phone stack. + * Other events are handled by {@link SMSDispatcher#handleMessage}. + * + * @param msg the message to handle + */ + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case EVENT_NEW_SMS_STATUS_REPORT: + handleStatusReport((AsyncResult) msg.obj); + break; + + case EVENT_NEW_BROADCAST_SMS: + handleBroadcastSms((AsyncResult)msg.obj); + break; + + default: + super.handleMessage(msg); + } } /** @@ -64,8 +105,7 @@ final class GsmSMSDispatcher extends SMSDispatcher { * @param ar AsyncResult passed into the message handler. ar.result should * be a String representing the status report PDU, as ASCII hex. */ - @Override - protected void handleStatusReport(AsyncResult ar) { + private void handleStatusReport(AsyncResult ar) { String pduString = (String) ar.result; SmsMessage sms = SmsMessage.newFromCDS(pduString); @@ -94,17 +134,17 @@ final class GsmSMSDispatcher extends SMSDispatcher { acknowledgeLastIncomingSms(true, Intents.RESULT_SMS_HANDLED, null); } - /** {@inheritDoc} */ @Override public int dispatchMessage(SmsMessageBase smsb) { // If sms is null, means there was a parsing error. if (smsb == null) { + Log.e(TAG, "dispatchMessage: message is null"); return Intents.RESULT_SMS_GENERIC_ERROR; } + SmsMessage sms = (SmsMessage) smsb; - boolean handled = false; if (sms.isTypeZero()) { // As per 3GPP TS 23.040 9.2.3.9, Type Zero messages should not be @@ -121,14 +161,15 @@ final class GsmSMSDispatcher extends SMSDispatcher { } // Special case the message waiting indicator messages + boolean handled = false; if (sms.isMWISetMessage()) { - mGsmPhone.updateMessageWaitingIndicator(true); + mPhone.setVoiceMessageWaiting(1, -1); // line 1: unknown number of msgs waiting handled = sms.isMwiDontStore(); if (false) { Log.d(TAG, "Received voice mail indicator set SMS shouldStore=" + !handled); } } else if (sms.isMWIClearMessage()) { - mGsmPhone.updateMessageWaitingIndicator(false); + mPhone.setVoiceMessageWaiting(1, 0); // line 1: no msgs waiting handled = sms.isMwiDontStore(); if (false) { Log.d(TAG, "Received voice mail indicator clear SMS shouldStore=" + !handled); @@ -139,35 +180,14 @@ final class GsmSMSDispatcher extends SMSDispatcher { return Intents.RESULT_SMS_HANDLED; } - if (!mStorageAvailable && (sms.getMessageClass() != MessageClass.CLASS_0)) { + if (!mStorageMonitor.isStorageAvailable() && + sms.getMessageClass() != MessageClass.CLASS_0) { // It's a storable message and there's no storage available. Bail. // (See TS 23.038 for a description of class 0 messages.) return Intents.RESULT_SMS_OUT_OF_MEMORY; } - SmsHeader smsHeader = sms.getUserDataHeader(); - // See if message is partial or port addressed. - if ((smsHeader == null) || (smsHeader.concatRef == null)) { - // Message is not partial (not part of concatenated sequence). - byte[][] pdus = new byte[1][]; - pdus[0] = sms.getPdu(); - - if (smsHeader != null && smsHeader.portAddrs != null) { - if (smsHeader.portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) { - return mWapPush.dispatchWapPdu(sms.getUserData()); - } else { - // The message was sent to a port, so concoct a URI for it. - dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort); - } - } else { - // Normal short and non-port-addressed message, dispatch it. - dispatchPdus(pdus); - } - return Activity.RESULT_OK; - } else { - // Process the message part. - return processMessagePart(sms, smsHeader.concatRef, smsHeader.portAddrs); - } + return dispatchNormalMessage(smsb); } /** {@inheritDoc} */ @@ -190,158 +210,20 @@ final class GsmSMSDispatcher extends SMSDispatcher { /** {@inheritDoc} */ @Override - protected void sendMultipartText(String destinationAddress, String scAddress, - ArrayList parts, ArrayList sentIntents, - ArrayList deliveryIntents) { - - int refNumber = getNextConcatenatedRef() & 0x00FF; - int msgCount = parts.size(); - int encoding = android.telephony.SmsMessage.ENCODING_UNKNOWN; - - mRemainingMessages = msgCount; - - TextEncodingDetails[] encodingForParts = new TextEncodingDetails[msgCount]; - for (int i = 0; i < msgCount; i++) { - TextEncodingDetails details = SmsMessage.calculateLength(parts.get(i), false); - if (encoding != details.codeUnitSize - && (encoding == android.telephony.SmsMessage.ENCODING_UNKNOWN - || encoding == android.telephony.SmsMessage.ENCODING_7BIT)) { - encoding = details.codeUnitSize; - } - encodingForParts[i] = details; - } - - for (int i = 0; i < msgCount; i++) { - SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef(); - concatRef.refNumber = refNumber; - concatRef.seqNumber = i + 1; // 1-based sequence - concatRef.msgCount = msgCount; - // TODO: We currently set this to true since our messaging app will never - // send more than 255 parts (it converts the message to MMS well before that). - // However, we should support 3rd party messaging apps that might need 16-bit - // references - // Note: It's not sufficient to just flip this bit to true; it will have - // ripple effects (several calculations assume 8-bit ref). - concatRef.isEightBits = true; - SmsHeader smsHeader = new SmsHeader(); - smsHeader.concatRef = concatRef; - if (encoding == android.telephony.SmsMessage.ENCODING_7BIT) { - smsHeader.languageTable = encodingForParts[i].languageTable; - smsHeader.languageShiftTable = encodingForParts[i].languageShiftTable; - } - - PendingIntent sentIntent = null; - if (sentIntents != null && sentIntents.size() > i) { - sentIntent = sentIntents.get(i); - } - - PendingIntent deliveryIntent = null; - if (deliveryIntents != null && deliveryIntents.size() > i) { - deliveryIntent = deliveryIntents.get(i); - } - - SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(scAddress, destinationAddress, - parts.get(i), deliveryIntent != null, SmsHeader.toByteArray(smsHeader), - encoding, smsHeader.languageTable, smsHeader.languageShiftTable); - - sendRawPdu(pdus.encodedScAddress, pdus.encodedMessage, sentIntent, deliveryIntent); - } + protected TextEncodingDetails calculateLength(CharSequence messageBody, + boolean use7bitOnly) { + return SmsMessage.calculateLength(messageBody, use7bitOnly); } - /** - * Send a multi-part text based SMS which already passed SMS control check. - * - * It is the working function for sendMultipartText(). - * - * @param destinationAddress the address to send the message to - * @param scAddress is the service center address or null to use - * the current default SMSC - * @param parts an ArrayList of strings that, in order, - * comprise the original message - * @param sentIntents if not null, an ArrayList of - * PendingIntents (one for each message part) that is - * broadcast when the corresponding message part has been sent. - * The result code will be Activity.RESULT_OK for success, - * or one of these errors: - * RESULT_ERROR_GENERIC_FAILURE - * RESULT_ERROR_RADIO_OFF - * RESULT_ERROR_NULL_PDU. - * @param deliveryIntents if not null, an ArrayList of - * PendingIntents (one for each message part) that is - * broadcast when the corresponding message part has been delivered - * to the recipient. The raw pdu of the status report is in the - * extended data ("pdu"). - */ - private void sendMultipartTextWithPermit(String destinationAddress, - String scAddress, ArrayList parts, - ArrayList sentIntents, - ArrayList deliveryIntents) { - - // check if in service - int ss = mPhone.getServiceState().getState(); - if (ss != ServiceState.STATE_IN_SERVICE) { - for (int i = 0, count = parts.size(); i < count; i++) { - PendingIntent sentIntent = null; - if (sentIntents != null && sentIntents.size() > i) { - sentIntent = sentIntents.get(i); - } - SmsTracker tracker = SmsTrackerFactory(null, sentIntent, null); - handleNotInService(ss, tracker); - } - return; - } - - int refNumber = getNextConcatenatedRef() & 0x00FF; - int msgCount = parts.size(); - int encoding = android.telephony.SmsMessage.ENCODING_UNKNOWN; - - mRemainingMessages = msgCount; - - TextEncodingDetails[] encodingForParts = new TextEncodingDetails[msgCount]; - for (int i = 0; i < msgCount; i++) { - TextEncodingDetails details = SmsMessage.calculateLength(parts.get(i), false); - if (encoding != details.codeUnitSize - && (encoding == android.telephony.SmsMessage.ENCODING_UNKNOWN - || encoding == android.telephony.SmsMessage.ENCODING_7BIT)) { - encoding = details.codeUnitSize; - } - encodingForParts[i] = details; - } - - for (int i = 0; i < msgCount; i++) { - SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef(); - concatRef.refNumber = refNumber; - concatRef.seqNumber = i + 1; // 1-based sequence - concatRef.msgCount = msgCount; - concatRef.isEightBits = false; - SmsHeader smsHeader = new SmsHeader(); - smsHeader.concatRef = concatRef; - if (encoding == android.telephony.SmsMessage.ENCODING_7BIT) { - smsHeader.languageTable = encodingForParts[i].languageTable; - smsHeader.languageShiftTable = encodingForParts[i].languageShiftTable; - } - - PendingIntent sentIntent = null; - if (sentIntents != null && sentIntents.size() > i) { - sentIntent = sentIntents.get(i); - } - - PendingIntent deliveryIntent = null; - if (deliveryIntents != null && deliveryIntents.size() > i) { - deliveryIntent = deliveryIntents.get(i); - } - - SmsMessage.SubmitPdu pdus = SmsMessage.getSubmitPdu(scAddress, destinationAddress, - parts.get(i), deliveryIntent != null, SmsHeader.toByteArray(smsHeader), - encoding, smsHeader.languageTable, smsHeader.languageShiftTable); - - HashMap map = new HashMap(); - map.put("smsc", pdus.encodedScAddress); - map.put("pdu", pdus.encodedMessage); - - SmsTracker tracker = SmsTrackerFactory(map, sentIntent, deliveryIntent); - sendSms(tracker); - } + /** {@inheritDoc} */ + @Override + protected void sendNewSubmitPdu(String destinationAddress, String scAddress, + String message, SmsHeader smsHeader, int encoding, + PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart) { + SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(scAddress, destinationAddress, + message, deliveryIntent != null, SmsHeader.toByteArray(smsHeader), + encoding, smsHeader.languageTable, smsHeader.languageShiftTable); + sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent); } /** {@inheritDoc} */ @@ -353,45 +235,16 @@ final class GsmSMSDispatcher extends SMSDispatcher { byte pdu[] = (byte[]) map.get("pdu"); Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker); - mCm.sendSMS(IccUtils.bytesToHexString(smsc), - IccUtils.bytesToHexString(pdu), reply); - } - - /** - * Send the multi-part SMS based on multipart Sms tracker - * - * @param tracker holds the multipart Sms tracker ready to be sent - */ - @Override - protected void sendMultipartSms (SmsTracker tracker) { - ArrayList parts; - ArrayList sentIntents; - ArrayList deliveryIntents; - - HashMap map = tracker.mData; - - String destinationAddress = (String) map.get("destination"); - String scAddress = (String) map.get("scaddress"); - - parts = (ArrayList) map.get("parts"); - sentIntents = (ArrayList) map.get("sentIntents"); - deliveryIntents = (ArrayList) map.get("deliveryIntents"); - - sendMultipartTextWithPermit(destinationAddress, - scAddress, parts, sentIntents, deliveryIntents); - + mCm.sendSMS(IccUtils.bytesToHexString(smsc), IccUtils.bytesToHexString(pdu), reply); } /** {@inheritDoc} */ @Override - protected void acknowledgeLastIncomingSms(boolean success, int result, Message response){ - // FIXME unit test leaves cm == null. this should change - if (mCm != null) { - mCm.acknowledgeLastIncomingGsmSms(success, resultToCause(result), response); - } + protected void acknowledgeLastIncomingSms(boolean success, int result, Message response) { + mCm.acknowledgeLastIncomingGsmSms(success, resultToCause(result), response); } - private int resultToCause(int rc) { + private static int resultToCause(int rc) { switch (rc) { case Activity.RESULT_OK: case Intents.RESULT_SMS_HANDLED: @@ -485,10 +338,12 @@ final class GsmSMSDispatcher extends SMSDispatcher { private final HashMap mSmsCbPageMap = new HashMap(); - @Override - protected void handleBroadcastSms(AsyncResult ar) { + /** + * Handle 3GPP format SMS-CB message. + * @param ar the AsyncResult containing the received PDUs + */ + private void handleBroadcastSms(AsyncResult ar) { try { - byte[][] pdus = null; byte[] receivedPdu = (byte[])ar.result; if (false) { @@ -507,10 +362,11 @@ final class GsmSMSDispatcher extends SMSDispatcher { SmsCbHeader header = new SmsCbHeader(receivedPdu); String plmn = SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC); - GsmCellLocation cellLocation = (GsmCellLocation)mGsmPhone.getCellLocation(); + GsmCellLocation cellLocation = (GsmCellLocation) mPhone.getCellLocation(); int lac = cellLocation.getLac(); int cid = cellLocation.getCid(); + byte[][] pdus; if (header.nrOfPages > 1) { // Multi-page message SmsCbConcatInfo concatInfo = new SmsCbConcatInfo(header, plmn, lac, cid); @@ -563,5 +419,4 @@ final class GsmSMSDispatcher extends SMSDispatcher { Log.e(TAG, "Error in decoding SMS CB pdu", e); } } - } diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java index 73c319c7a3270..5d6f18113c8d2 100755 --- a/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java +++ b/telephony/java/com/android/internal/telephony/gsm/SIMRecords.java @@ -38,6 +38,7 @@ import com.android.internal.telephony.IccVmNotSupportedException; import com.android.internal.telephony.MccTable; import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneBase; +import com.android.internal.telephony.SmsMessageBase; import java.util.ArrayList; @@ -1160,6 +1161,15 @@ public class SIMRecords extends IccRecords { } } + /** + * Dispatch 3GPP format message. Overridden for CDMA/LTE phones by + * {@link com.android.internal.telephony.cdma.CdmaLteUiccRecords} + * to send messages to the secondary 3GPP format SMS dispatcher. + */ + protected int dispatchGsmMessage(SmsMessageBase message) { + return phone.mSMS.dispatchMessage(message); + } + private void handleSms(byte[] ba) { if (ba[0] != 0) Log.d("ENF", "status : " + ba[0]); @@ -1175,7 +1185,7 @@ public class SIMRecords extends IccRecords { System.arraycopy(ba, 1, pdu, 0, n - 1); SmsMessage message = SmsMessage.createFromPdu(pdu); - phone.mSMS.dispatchMessage(message); + dispatchGsmMessage(message); } } @@ -1201,7 +1211,7 @@ public class SIMRecords extends IccRecords { System.arraycopy(ba, 1, pdu, 0, n - 1); SmsMessage message = SmsMessage.createFromPdu(pdu); - phone.mSMS.dispatchMessage(message); + dispatchGsmMessage(message); // 3GPP TS 51.011 v5.0.0 (20011-12) 10.5.3 // 1 == "received by MS from network; message read" diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java index 3784e7ccea21c..ea030e6bb0476 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java @@ -136,14 +136,6 @@ public class SmsMessage extends SmsMessageBase { } } - /** @hide */ - public static SmsMessage newFromCMTI(String line) { - // the thinking here is not to read the message immediately - // FTA test case - Log.e(LOG_TAG, "newFromCMTI: not yet supported"); - return null; - } - /** @hide */ public static SmsMessage newFromCDS(String line) { try { @@ -156,15 +148,6 @@ public class SmsMessage extends SmsMessageBase { } } - /** - * Note: This functionality is currently not supported in GSM mode. - * @hide - */ - public static SmsMessageBase newFromParcel(Parcel p){ - Log.w(LOG_TAG, "newFromParcel: is not supported in GSM mode."); - return null; - } - /** * Create an SmsMessage from an SMS EF record. *