Send outbound messages via the default carrier app. Initially, the
message will go into a pending state. There is a new API call to
update the status of a message. If the carrier app cannot send the
message, it will fall back to the default GSM/CDMA network.
This is the same cl as 476721 which has already been approved by jsh@
Change-Id: I51d732b9cc40b371f77fd26d28e0836a466afc71
diff --git a/src/java/android/provider/Telephony.java b/src/java/android/provider/Telephony.java
index 82ad215..2666d65 100644
--- a/src/java/android/provider/Telephony.java
+++ b/src/java/android/provider/Telephony.java
@@ -879,6 +879,42 @@
"android.provider.Telephony.SMS_REJECTED";
/**
+ * Broadcast Action: A new SMS PDU needs to be sent from
+ * the device. This intent will only be delivered to the default
+ * carrier app. That app is responsible for sending the PDU.
+ * The intent will have the following extra values:</p>
+ *
+ * <ul>
+ * <li><em>"pdu"</em> - (byte[]) The PDU to send.</li>
+ * <li><em>"smsc"</em> - (byte[]) The service center address (for GSM PDU only).</li>
+ * <li><em>"format"</em> - (String) The format of the PDU. Either 3gpp or 3gpp2. </li>
+ * </ul>
+ *
+ * <p>If a BroadcastReceiver is trying to send the message,
+ * it should set the result code to {@link android.app.Activity#RESULT_OK} and set
+ * the following in the result extra values:</p>
+ *
+ * <ul>
+ * <li><em>"messageref"</em> - (int) The new message reference number which will be
+ * later used in the updateSmsSendStatus call.</li>
+ * </ul>
+ *
+ * <p>If a BroadcastReceiver cannot send the message, it should not set the result
+ * code and the platform will send it via the normal pathway.
+ * </p>
+ *
+ * <p class="note"><strong>Note:</strong>
+ * The broadcast receiver that filters for this intent must declare
+ * {@link android.Manifest.permission#BROADCAST_SMS} as a required permission in
+ * the <a href="{@docRoot}guide/topics/manifest/receiver-element.html">{@code
+ * <receiver>}</a> tag.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String SMS_SEND_ACTION =
+ "android.provider.Telephony.SMS_SEND";
+
+ /**
* Read the PDUs out of an {@link #SMS_RECEIVED_ACTION} or a
* {@link #DATA_SMS_RECEIVED_ACTION} intent.
*
diff --git a/src/java/android/telephony/SmsManager.java b/src/java/android/telephony/SmsManager.java
index 3af756e..aff0bb2 100644
--- a/src/java/android/telephony/SmsManager.java
+++ b/src/java/android/telephony/SmsManager.java
@@ -105,6 +105,27 @@
}
/**
+ * Update the status of a pending (send-by-IP) SMS message and resend by PSTN if necessary.
+ * This outbound message was handled by the carrier app. If the carrier app fails to send
+ * this message, it would be resent by PSTN.
+ *
+ * @param messageRef the reference number of the SMS message.
+ * @param success True if and only if the message was sent successfully. If its value is
+ * false, this message should be resent via PSTN.
+ * {@hide}
+ */
+ public void updateSmsSendStatus(int messageRef, boolean success) {
+ try {
+ ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+ if (iccISms != null) {
+ iccISms.updateSmsSendStatus(messageRef, success);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+ }
+
+ /**
* Divide a message text into several fragments, none bigger than
* the maximum SMS message size.
*
diff --git a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
index 938fd38..ce5b493 100644
--- a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
@@ -394,6 +394,21 @@
}
/**
+ * Update the status of a pending (send-by-IP) SMS message and resend by PSTN if necessary.
+ * This outbound message was handled by the carrier app. If the carrier app fails to send
+ * this message, it would be resent by PSTN.
+ *
+ * @param messageRef the reference number of the SMS message.
+ * @param success True if and only if the message was sent successfully. If its value is
+ * false, this message should be resent via PSTN.
+ * {@hide}
+ */
+ @Override
+ public void updateSmsSendStatus(int messageRef, boolean success) {
+ mDispatcher.updateSmsSendStatus(messageRef, success);
+ }
+
+ /**
* Send a multi-part text based SMS.
*
* @param destAddr the address to send the message to
diff --git a/src/java/com/android/internal/telephony/ImsSMSDispatcher.java b/src/java/com/android/internal/telephony/ImsSMSDispatcher.java
index 07bce94..2bc0c16 100644
--- a/src/java/com/android/internal/telephony/ImsSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/ImsSMSDispatcher.java
@@ -20,6 +20,7 @@
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
@@ -189,6 +190,66 @@
}
@Override
+ protected void sendSmsByPstn(SmsTracker tracker) {
+ // This function should be defined in Gsm/CdmaDispatcher.
+ Rlog.e(TAG, "sendSmsByPstn should never be called from here!");
+ }
+
+ @Override
+ protected void updateSmsSendStatus(int messageRef, boolean success) {
+ if (isCdmaMo()) {
+ updateSmsSendStatusHelper(messageRef, mCdmaDispatcher.sendPendingList,
+ mCdmaDispatcher, success);
+ updateSmsSendStatusHelper(messageRef, mGsmDispatcher.sendPendingList,
+ null, success);
+ } else {
+ updateSmsSendStatusHelper(messageRef, mGsmDispatcher.sendPendingList,
+ mGsmDispatcher, success);
+ updateSmsSendStatusHelper(messageRef, mCdmaDispatcher.sendPendingList,
+ null, success);
+ }
+ }
+
+ /**
+ * Find a tracker in a list to update its status. If the status is successful,
+ * send an EVENT_SEND_SMS_COMPLETE message. Otherwise, resend the message by PSTN if
+ * feasible.
+ *
+ * @param messageRef the reference number of the tracker.
+ * @param sendPendingList the list of trackers to look into.
+ * @param smsDispatcher the dispatcher for resending the message by PSTN.
+ * @param success true iff the message was sent successfully.
+ */
+ private void updateSmsSendStatusHelper(int messageRef,
+ List<SmsTracker> sendPendingList,
+ SMSDispatcher smsDispatcher,
+ boolean success) {
+ synchronized (sendPendingList) {
+ for (int i = 0, count = sendPendingList.size(); i < count; i++) {
+ SmsTracker tracker = sendPendingList.get(i);
+ if (tracker.mMessageRef == messageRef) {
+ // Found it. Remove from list and broadcast.
+ sendPendingList.remove(i);
+ if (success) {
+ Rlog.d(TAG, "Sending SMS by IP succeeded.");
+ sendMessage(obtainMessage(EVENT_SEND_SMS_COMPLETE,
+ new AsyncResult(tracker, null, null)));
+ } else {
+ Rlog.d(TAG, "Sending SMS by IP failed.");
+ if (smsDispatcher != null) {
+ smsDispatcher.sendSmsByPstn(tracker);
+ } else {
+ Rlog.e(TAG, "No feasible way to send this SMS.");
+ }
+ }
+ // Only expect to see one tracker matching this messageref.
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
protected void sendText(String destAddr, String scAddr, String text,
PendingIntent sentIntent, PendingIntent deliveryIntent) {
Rlog.d(TAG, "sendText");
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
index 9cd5b70..5eb9162 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -20,6 +20,7 @@
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
+import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
@@ -34,12 +35,14 @@
import android.net.Uri;
import android.os.AsyncResult;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemProperties;
import android.provider.Settings;
import android.provider.Telephony;
import android.provider.Telephony.Sms;
+import android.provider.Telephony.Sms.Intents;
import android.telephony.PhoneNumberUtils;
import android.telephony.Rlog;
import android.telephony.ServiceState;
@@ -61,7 +64,9 @@
import com.android.internal.telephony.ImsSMSDispatcher;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
@@ -256,6 +261,10 @@
/** Sent messages awaiting a delivery status report. */
protected final ArrayList<SmsTracker> deliveryPendingList = new ArrayList<SmsTracker>();
+ /** Outgoing messages being handled by the carrier app. */
+ protected final List<SmsTracker> sendPendingList =
+ Collections.synchronizedList(new ArrayList<SmsTracker>());
+
/**
* Handles events coming from the phone stack. Overridden from handler.
*
@@ -522,6 +531,55 @@
boolean use7bitOnly);
/**
+ * Update the status of a pending (send-by-IP) SMS message and resend by PSTN if necessary.
+ * This outbound message was handled by the carrier app. If the carrier app fails to send
+ * this message, it would be resent by PSTN.
+ *
+ * @param messageRef the reference number of the SMS message.
+ * @param success True if and only if the message was sent successfully. If its value is
+ * false, this message should be resent via PSTN.
+ */
+ protected abstract void updateSmsSendStatus(int messageRef, boolean success);
+
+ /**
+ * Handler for a {@link GsmSMSDispatcher} or {@link CdmaSMSDispatcher} broadcast.
+ * If SMS sending is successfuly, sends EVENT_SEND_SMS_COMPLETE message. Otherwise,
+ * send the message via the GSM/CDMA network.
+ */
+ protected final class SMSDispatcherReceiver extends BroadcastReceiver {
+
+ private final SmsTracker mTracker;
+
+ public SMSDispatcherReceiver(SmsTracker tracker) {
+ mTracker = tracker;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(Intents.SMS_SEND_ACTION)) {
+ int rc = getResultCode();
+ if (rc == Activity.RESULT_OK) {
+ Rlog.d(TAG, "Sending SMS by IP pending.");
+ Bundle resultExtras = getResultExtras(false);
+ if (resultExtras != null && resultExtras.containsKey("messageref")) {
+ mTracker.mMessageRef = resultExtras.getInt("messageref");
+ Rlog.d(TAG, "messageref = " + mTracker.mMessageRef);
+ } else {
+ Rlog.e(TAG, "Can't find messageref in result extras.");
+ }
+ sendPendingList.add(mTracker);
+ } else {
+ Rlog.d(TAG, "Sending SMS by IP failed.");
+ sendSmsByPstn(mTracker);
+ }
+ } else {
+ Rlog.e(TAG, "unexpected BroadcastReceiver action: " + action);
+ }
+ }
+ }
+
+ /**
* Send a multi-part text based SMS.
*
* @param destAddr the address to send the message to
@@ -942,6 +1000,13 @@
protected abstract void sendSms(SmsTracker tracker);
/**
+ * Send the SMS via the PSTN network.
+ *
+ * @param tracker holds the Sms tracker ready to be sent
+ */
+ protected abstract void sendSmsByPstn(SmsTracker tracker);
+
+ /**
* Retry the message along to the radio.
*
* @param tracker holds the SMS message to send
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index f168180..0bfc245 100755
--- a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -17,12 +17,18 @@
package com.android.internal.telephony.cdma;
import android.app.Activity;
+import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
+import android.os.AsyncResult;
import android.os.Message;
import android.os.SystemProperties;
import android.provider.Telephony.Sms;
+import android.provider.Telephony.Sms.Intents;
import android.telephony.Rlog;
import android.telephony.SmsManager;
@@ -180,14 +186,43 @@
// byte[] smsc = (byte[]) map.get("smsc"); // unused for CDMA
byte[] pdu = (byte[]) map.get("pdu");
- Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
-
Rlog.d(TAG, "sendSms: "
- +" isIms()="+isIms()
- +" mRetryCount="+tracker.mRetryCount
- +" mImsRetry="+tracker.mImsRetry
- +" mMessageRef="+tracker.mMessageRef
- +" SS=" +mPhone.getServiceState().getState());
+ + " isIms()=" + isIms()
+ + " mRetryCount=" + tracker.mRetryCount
+ + " mImsRetry=" + tracker.mImsRetry
+ + " mMessageRef=" + tracker.mMessageRef
+ + " SS=" + mPhone.getServiceState().getState());
+
+ // FIX this once the carrier app and SIM restricted API is finalized.
+ // We should direct the intent to only the default carrier app.
+
+ // Send SMS via the carrier app.
+ BroadcastReceiver resultReceiver = new SMSDispatcherReceiver(tracker);
+
+ // Direct the intent to only the default carrier app.
+ Intent intent = new Intent(Intents.SMS_SEND_ACTION);
+ intent.putExtra("pdu", pdu);
+ intent.putExtra("format", getFormat());
+ intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
+ Rlog.d(TAG, "Sending SMS by carrier app.");
+
+ mContext.sendOrderedBroadcast(intent, android.Manifest.permission.RECEIVE_SMS,
+ AppOpsManager.OP_RECEIVE_SMS, resultReceiver,
+ null, Activity.RESULT_CANCELED, null, null);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void updateSmsSendStatus(int messageRef, boolean success) {
+ // This function should be defined in ImsDispatcher.
+ Rlog.e(TAG, "updateSmsSendStatus should never be called from here!");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void sendSmsByPstn(SmsTracker tracker) {
+ Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
+ byte[] pdu = (byte[]) tracker.mData.get("pdu");
// sms over cdma is used:
// if sms over IMS is not supported AND
diff --git a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index 345abba..26dfa00 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -17,8 +17,12 @@
package com.android.internal.telephony.gsm;
import android.app.Activity;
+import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.os.AsyncResult;
import android.os.Message;
@@ -211,11 +215,8 @@
protected void sendSms(SmsTracker tracker) {
HashMap<String, Object> map = tracker.mData;
- byte smsc[] = (byte[]) map.get("smsc");
byte pdu[] = (byte[]) map.get("pdu");
- Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
-
if (tracker.mRetryCount > 0) {
Rlog.d(TAG, "sendSms: "
+ " mRetryCount=" + tracker.mRetryCount
@@ -231,11 +232,38 @@
}
}
Rlog.d(TAG, "sendSms: "
- +" isIms()="+isIms()
- +" mRetryCount="+tracker.mRetryCount
- +" mImsRetry="+tracker.mImsRetry
- +" mMessageRef="+tracker.mMessageRef
- +" SS=" +mPhone.getServiceState().getState());
+ + " isIms()=" + isIms()
+ + " mRetryCount=" + tracker.mRetryCount
+ + " mImsRetry=" + tracker.mImsRetry
+ + " mMessageRef=" + tracker.mMessageRef
+ + " SS=" + mPhone.getServiceState().getState());
+
+ // FIX this once the carrier app and SIM restricted API is finalized.
+ // We should direct the intent to only the default carrier app.
+
+ // Send SMS via the carrier app.
+ BroadcastReceiver resultReceiver = new SMSDispatcherReceiver(tracker);
+
+ Intent intent = new Intent(Intents.SMS_SEND_ACTION);
+ intent.putExtra("pdu", pdu);
+ intent.putExtra("smsc", (byte[]) map.get("smsc"));
+ intent.putExtra("format", getFormat());
+ intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
+ Rlog.d(TAG, "Sending SMS by carrier app.");
+
+ mContext.sendOrderedBroadcast(intent, android.Manifest.permission.RECEIVE_SMS,
+ AppOpsManager.OP_RECEIVE_SMS, resultReceiver,
+ null, Activity.RESULT_CANCELED, null, null);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void sendSmsByPstn(SmsTracker tracker) {
+ HashMap<String, Object> map = tracker.mData;
+
+ byte smsc[] = (byte[]) map.get("smsc");
+ byte[] pdu = (byte[]) map.get("pdu");
+ Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
// sms over gsm is used:
// if sms over IMS is not supported AND
@@ -263,6 +291,13 @@
}
}
+ /** {@inheritDoc} */
+ @Override
+ protected void updateSmsSendStatus(int messageRef, boolean success) {
+ // This function should be defined in ImsDispatcher.
+ Rlog.e(TAG, "updateSmsSendStatus should never be called from here!");
+ }
+
protected UiccCardApplication getUiccCardApplication() {
return mUiccController.getUiccCardApplication(UiccController.APP_FAM_3GPP);
}