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
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 237a892..79995d0 100755
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -666,6 +666,7 @@
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 @@
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 5bdc146..44bdaeb 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -325,7 +325,7 @@
*
* {@hide}
*/
- public ArrayList<SmsMessage> getAllMessagesFromIcc() {
+ public static ArrayList<SmsMessage> getAllMessagesFromIcc() {
List<SmsRawData> records = null;
try {
@@ -470,7 +470,7 @@
* <code>getAllMessagesFromIcc</code>
* @return <code>ArrayList</code> of <code>SmsMessage</code> objects.
*/
- private ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) {
+ private static ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) {
ArrayList<SmsMessage> messages = new ArrayList<SmsMessage>();
if (records != null) {
int count = records.size();
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index e75d96d..fc8a145 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -36,7 +36,6 @@
* 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 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 @@
}
- /**
- * Constructor
- *
- * @hide
- */
- public SmsMessage() {
- this(getSmsFacility());
- }
-
private SmsMessage(SmsMessageBase smb) {
mWrappedSmsMessage = smb;
}
/**
* Create an SmsMessage from a raw PDU.
+ *
+ * <p><b>This method will soon be deprecated</b> and all applications which handle
+ * incoming SMS messages by processing the {@code SMS_RECEIVED_ACTION} broadcast
+ * intent <b>must</b> 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 @@
*
* {@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 @@
/**
* 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();
@@ -381,34 +374,6 @@
* @return a <code>SubmitPdu</code> 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.
- *
- * @param scAddress Service Centre address. Null means use default.
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
*/
public static SubmitPdu getSubmitPdu(String scAddress,
String destinationAddress, String message, boolean statusReportRequested) {
@@ -603,15 +568,6 @@
}
/**
- * Return the user data header (UDH).
- *
- * @hide
- */
- public SmsHeader getUserDataHeader() {
- return mWrappedSmsMessage.getUserDataHeader();
- }
-
- /**
* Returns the raw PDU for the message.
*
* @return the raw PDU for the message.
@@ -646,7 +602,6 @@
* SmsManager.STATUS_ON_ICC_UNSENT
*/
public int getStatusOnIcc() {
-
return mWrappedSmsMessage.getStatusOnIcc();
}
@@ -666,7 +621,6 @@
* SmsMessage was not created from a ICC SMS EF record.
*/
public int getIndexOnIcc() {
-
return mWrappedSmsMessage.getIndexOnIcc();
}
@@ -704,19 +658,4 @@
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 4af99a6..8d86ec2 100644
--- a/telephony/java/android/telephony/gsm/SmsMessage.java
+++ b/telephony/java/android/telephony/gsm/SmsMessage.java
@@ -166,104 +166,6 @@
}
/**
- * 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>],<length><CR><LF><pdu>
- *
- * 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
* @deprecated Use android.telephony.SmsMessage.
diff --git a/telephony/java/com/android/internal/telephony/BaseCommands.java b/telephony/java/com/android/internal/telephony/BaseCommands.java
index f0d2fba..f111dd6 100644
--- a/telephony/java/com/android/internal/telephony/BaseCommands.java
+++ b/telephony/java/com/android/internal/telephony/BaseCommands.java
@@ -79,7 +79,8 @@
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 @@
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 1caea70..33eed38 100644
--- a/telephony/java/com/android/internal/telephony/CommandsInterface.java
+++ b/telephony/java/com/android/internal/telephony/CommandsInterface.java
@@ -20,8 +20,6 @@
import android.os.Message;
import android.os.Handler;
-import android.os.SystemProperties;
-
/**
* {@hide}
@@ -267,14 +265,32 @@
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 444f0d2..ca04eb2 100644
--- a/telephony/java/com/android/internal/telephony/Phone.java
+++ b/telephony/java/com/android/internal/telephony/Phone.java
@@ -1394,7 +1394,7 @@
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 @@
* @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 82f3955..a7a4908 100644
--- a/telephony/java/com/android/internal/telephony/PhoneBase.java
+++ b/telephony/java/com/android/internal/telephony/PhoneBase.java
@@ -112,15 +112,17 @@
/* 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 @@
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 @@
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 @@
// 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
@@ -1037,37 +1051,6 @@
}
/**
- * 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
* version scoped to their packages
@@ -1132,7 +1115,7 @@
/**
* 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 @@
/**
* 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 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 e0e8d49..b497ec8 100644
--- a/telephony/java/com/android/internal/telephony/PhoneProxy.java
+++ b/telephony/java/com/android/internal/telephony/PhoneProxy.java
@@ -72,7 +72,7 @@
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 @@
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 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 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 bd35058..8aae0d4 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -2434,8 +2434,8 @@
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 @@
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 76e719c..e4c6028 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.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_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 @@
* 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<SmsTracker> mSTrackers = new ArrayList<SmsTracker>(MO_MSG_QUEUE_LIMIT);
+ private final ArrayList<SmsTracker> mSTrackers = new ArrayList<SmsTracker>(MO_MSG_QUEUE_LIMIT);
/** Wake lock to ensure device stays awake while dispatching the SMS intent. */
private PowerManager.WakeLock mWakeLock;
@@ -150,17 +152,14 @@
* 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 @@
}
/**
- * 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<String, ArrayList<Long>> 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<String, ArrayList<Long>> ();
- }
-
- /**
- * 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<Long>());
- }
-
- return isUnderLimit(mSmsStamp.get(appName), smsWaiting);
- }
-
- private boolean isUnderLimit(ArrayList<Long> 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 @@
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 @@
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 @@
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;
}
}
@@ -440,26 +348,6 @@
}
/**
- * 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
* the failure in the result code.
@@ -559,7 +447,7 @@
* 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 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}.
*
- * NOTE: concatRef (naturally) needs to be non-null, but portAddrs can be null.
+ * @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
+ *
* @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 @@
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 @@
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 @@
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);
}
/**
@@ -759,6 +734,16 @@
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.
*
* @param destAddr the address to send the message to
@@ -784,9 +769,70 @@
* 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<String> parts, ArrayList<PendingIntent> sentIntents,
- ArrayList<PendingIntent> deliveryIntents);
+ ArrayList<PendingIntent> 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 @@
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 @@
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 @@
*
* @param tracker holds the multipart Sms tracker ready to be sent
*/
- protected abstract void sendMultipartSms (SmsTracker tracker);
+ private void sendMultipartSms(SmsTracker tracker) {
+ ArrayList<String> parts;
+ ArrayList<PendingIntent> sentIntents;
+ ArrayList<PendingIntent> deliveryIntents;
+
+ HashMap<String, Object> map = tracker.mData;
+
+ String destinationAddress = (String) map.get("destination");
+ String scAddress = (String) map.get("scaddress");
+
+ parts = (ArrayList<String>) map.get("parts");
+ sentIntents = (ArrayList<PendingIntent>) map.get("sentIntents");
+ deliveryIntents = (ArrayList<PendingIntent>) 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.
@@ -934,66 +1008,38 @@
}
/**
- * 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<String, Object> mData;
+ public final HashMap<String, Object> mData;
public int mRetryCount;
public int mMessageRef;
- public PendingIntent mSentIntent;
- public PendingIntent mDeliveryIntent;
+ public final PendingIntent mSentIntent;
+ public final PendingIntent mDeliveryIntent;
- SmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
+ public SmsTracker(HashMap<String, Object> 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<String, Object> 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 @@
}
};
- 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 0000000..0c06ffc
--- /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 0000000..bd2ae8b
--- /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<String, ArrayList<Long>> mSmsStamp =
+ new HashMap<String, ArrayList<Long>>();
+
+ /**
+ * 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<Long> sentList = mSmsStamp.get(appName);
+ if (sentList == null) {
+ sentList = new ArrayList<Long>();
+ 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<Map.Entry<String, ArrayList<Long>>> iter = mSmsStamp.entrySet().iterator();
+ while (iter.hasNext()) {
+ Map.Entry<String, ArrayList<Long>> entry = iter.next();
+ ArrayList<Long> oldList = entry.getValue();
+ if (oldList.isEmpty() || oldList.get(oldList.size() - 1) < beginCheckPeriod) {
+ iter.remove();
+ }
+ }
+ }
+ }
+
+ private boolean isUnderLimit(ArrayList<Long> 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 6903025..c2b9e4f 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 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 @@
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
@@ -54,6 +56,20 @@
}
@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 @@
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 286515e..09ee28c 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.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.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.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 @@
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 @@
// 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 @@
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 @@
//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 @@
notifier.notifyMessageWaitingChanged(this);
}
+ @Override
public void dispose() {
synchronized(PhoneProxy.lockForRadioTechnologyChange) {
super.dispose();
@@ -253,23 +248,26 @@
}
}
+ @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 @@
return null;
}
- /**
+ /**
* Notify any interested party of a Phone state change {@link Phone.State}
*/
/*package*/ void notifyPhoneStateChanged() {
@@ -858,18 +856,6 @@
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 @@
case EVENT_RUIM_RECORDS_LOADED:{
Log.d(LOG_TAG, "Event EVENT_RUIM_RECORDS_LOADED Received");
+ updateCurrentCarrierInProvider();
}
break;
@@ -1172,7 +1159,7 @@
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 @@
/**
* 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 @@
* 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 @@
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 @@
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 0617fee..47c638f 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.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 @@
}
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 07b0f4f..dded39e 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.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.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.util.HexDump;
import java.io.ByteArrayOutputStream;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -60,24 +60,23 @@
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 @@
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 @@
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 @@
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 @@
* 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 @@
}
// 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 @@
/** {@inheritDoc} */
@Override
- protected void sendMultipartText(String destAddr, String scAddr,
- ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
- ArrayList<PendingIntent> 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 @@
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 @@
* @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 be5c616..1409cab 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -114,30 +114,6 @@
}
/**
- * 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 d325aaa..e1f4c4b 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.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 @@
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 @@
new Integer(Phone.PHONE_TYPE_GSM).toString());
}
+ @Override
public void dispose() {
synchronized(PhoneProxy.lockForRadioTechnologyChange) {
super.dispose();
@@ -228,19 +226,22 @@
}
}
+ @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() {
@@ -406,17 +407,6 @@
}
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 52ca453..4e1cc9a 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.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 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 @@
* @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 @@
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 @@
}
// 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 @@
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 @@
/** {@inheritDoc} */
@Override
- protected void sendMultipartText(String destinationAddress, String scAddress,
- ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
- ArrayList<PendingIntent> 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 <code>ArrayList</code> of strings that, in order,
- * comprise the original message
- * @param sentIntents if not null, an <code>ArrayList</code> of
- * <code>PendingIntent</code>s (one for each message part) that is
- * broadcast when the corresponding message part has been sent.
- * The result code will be <code>Activity.RESULT_OK<code> for success,
- * or one of these errors:
- * <code>RESULT_ERROR_GENERIC_FAILURE</code>
- * <code>RESULT_ERROR_RADIO_OFF</code>
- * <code>RESULT_ERROR_NULL_PDU</code>.
- * @param deliveryIntents if not null, an <code>ArrayList</code> of
- * <code>PendingIntent</code>s (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<String> parts,
- ArrayList<PendingIntent> sentIntents,
- ArrayList<PendingIntent> 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<String, Object> map = new HashMap<String, Object>();
- 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 @@
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<String> parts;
- ArrayList<PendingIntent> sentIntents;
- ArrayList<PendingIntent> deliveryIntents;
-
- HashMap<String, Object> map = tracker.mData;
-
- String destinationAddress = (String) map.get("destination");
- String scAddress = (String) map.get("scaddress");
-
- parts = (ArrayList<String>) map.get("parts");
- sentIntents = (ArrayList<PendingIntent>) map.get("sentIntents");
- deliveryIntents = (ArrayList<PendingIntent>) 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 @@
private final HashMap<SmsCbConcatInfo, byte[][]> mSmsCbPageMap =
new HashMap<SmsCbConcatInfo, byte[][]>();
- @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 @@
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 @@
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 73c319c..5d6f181 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.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 @@
}
}
+ /**
+ * 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 @@
System.arraycopy(ba, 1, pdu, 0, n - 1);
SmsMessage message = SmsMessage.createFromPdu(pdu);
- phone.mSMS.dispatchMessage(message);
+ dispatchGsmMessage(message);
}
}
@@ -1201,7 +1211,7 @@
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 3784e7c..ea030e6 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -137,14 +137,6 @@
}
/** @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 {
SmsMessage msg = new SmsMessage();
@@ -157,15 +149,6 @@
}
/**
- * 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.
*
* @param index Index of SMS record. This should be index in ArrayList