Support SMS over IMS
Sukanya Rajkhowa [Sat, 14 Sep 2013 02:06:47 +0000 (19:06 -0700)]
ImsSMSDispatcher is part of IccSmsInterfaceManager.
It always receives calls to send sms first and decides
whether to use ims call flow or gsm/cdma based on response
to REQUEST_IMS_REGISTRATION_STATE.

When ims is registered and sms is supported, the request also returns
sms format to use.

In case of sms over ims failure, RIL_REQUEST_IMS_SEND_SMS sets
messageRef from RIL_SMS_RESPONSE of corresponding failed MO SMS, and
sets retry field to non-zero. If voice is available, sends
RIL_REQUEST_IMS_SEND_SMS retries with data encoded based on voice tech
available.  If voice is not available, sets retry counter to max and
skips retries and sends failure to client.

Bug: 9626411

Change-Id: I4c63c8fc0eb2191847b509e66772e3de27d491ed
Signed-off-by: Ed Tam <etam@google.com>

Conflicts:
src/java/com/android/internal/telephony/gsm/GSMPhone.java

19 files changed:
src/java/android/telephony/SmsManager.java
src/java/android/telephony/SmsMessage.java
src/java/com/android/internal/telephony/BaseCommands.java
src/java/com/android/internal/telephony/CommandsInterface.java
src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
src/java/com/android/internal/telephony/ImsSMSDispatcher.java [new file with mode: 0644]
src/java/com/android/internal/telephony/PhoneBase.java
src/java/com/android/internal/telephony/RIL.java
src/java/com/android/internal/telephony/SMSDispatcher.java
src/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
src/java/com/android/internal/telephony/cdma/CDMAPhone.java
src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
src/java/com/android/internal/telephony/cdma/SmsMessage.java
src/java/com/android/internal/telephony/gsm/GSMPhone.java
src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
src/java/com/android/internal/telephony/gsm/SmsMessage.java
src/java/com/android/internal/telephony/sip/SipCommandInterface.java
src/java/com/android/internal/telephony/test/SimulatedCommands.java
tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java

index f3b4735..aa0e85b 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -518,6 +519,54 @@ public final class SmsManager {
         return messages;
     }
 
+    /**
+     * SMS over IMS is supported if IMS is registered and SMS is supported
+     * on IMS.
+     *
+     * @return true if SMS over IMS is supported, false otherwise
+     *
+     * @see #getImsSmsFormat()
+     *
+     * @hide
+     */
+    boolean isImsSmsSupported() {
+        boolean boSupported = false;
+        try {
+            ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+            if (iccISms != null) {
+                boSupported = iccISms.isImsSmsSupported();
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+        return boSupported;
+    }
+
+    /**
+     * Gets SMS format supported on IMS.  SMS over IMS format is
+     * either 3GPP or 3GPP2.
+     *
+     * @return SmsMessage.FORMAT_3GPP,
+     *         SmsMessage.FORMAT_3GPP2
+     *      or SmsMessage.FORMAT_UNKNOWN
+     *
+     * @see #isImsSmsSupported()
+     *
+     * @hide
+     */
+    String getImsSmsFormat() {
+        String format = com.android.internal.telephony.SmsConstants.FORMAT_UNKNOWN;
+        try {
+            ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+            if (iccISms != null) {
+                format = iccISms.getImsSmsFormat();
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+        return format;
+    }
+
     // see SmsMessage.getStatusOnIcc
 
     /** Free space (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
index e83d175..d9e85d2 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -131,13 +132,26 @@ public class SmsMessage {
      * 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.
+     * such as dual-mode GSM/CDMA and CDMA/LTE phones.  Guess format based on Voice
+     * technology first, if it fails use other format.
      */
     public static SmsMessage createFromPdu(byte[] pdu) {
+         SmsMessage message = null;
+
+        // cdma(3gpp2) vs gsm(3gpp) format info was not given,
+        // guess from active voice phone type
         int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
         String format = (PHONE_TYPE_CDMA == activePhone) ?
                 SmsConstants.FORMAT_3GPP2 : SmsConstants.FORMAT_3GPP;
-        return createFromPdu(pdu, format);
+        message = createFromPdu(pdu, format);
+
+        if (null == message || null == message.mWrappedSmsMessage) {
+            // decoding pdu failed based on activePhone type, must be other format
+            format = (PHONE_TYPE_CDMA == activePhone) ?
+                    SmsConstants.FORMAT_3GPP : SmsConstants.FORMAT_3GPP2;
+            message = createFromPdu(pdu, format);
+        }
+        return message;
     }
 
     /**
@@ -203,9 +217,8 @@ public class SmsMessage {
      */
     public static SmsMessage createFromEfRecord(int index, byte[] data) {
         SmsMessageBase wrappedMessage;
-        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
 
-        if (PHONE_TYPE_CDMA == activePhone) {
+        if (isCdmaVoice()) {
             wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromEfRecord(
                     index, data);
         } else {
@@ -224,9 +237,7 @@ public class SmsMessage {
      * We should probably deprecate it and remove the obsolete test case.
      */
     public static int getTPLayerLengthForPDU(String pdu) {
-        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
-
-        if (PHONE_TYPE_CDMA == activePhone) {
+        if (isCdmaVoice()) {
             return com.android.internal.telephony.cdma.SmsMessage.getTPLayerLengthForPDU(pdu);
         } else {
             return com.android.internal.telephony.gsm.SmsMessage.getTPLayerLengthForPDU(pdu);
@@ -259,8 +270,8 @@ public class SmsMessage {
      *         code unit size (see the ENCODING_* definitions in SmsConstants)
      */
     public static int[] calculateLength(CharSequence msgBody, boolean use7bitOnly) {
-        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
-        TextEncodingDetails ted = (PHONE_TYPE_CDMA == activePhone) ?
+        // this function is for MO SMS
+        TextEncodingDetails ted = (useCdmaFormatForMoSms()) ?
             com.android.internal.telephony.cdma.SmsMessage.calculateLength(msgBody, use7bitOnly) :
             com.android.internal.telephony.gsm.SmsMessage.calculateLength(msgBody, use7bitOnly);
         int ret[] = new int[4];
@@ -282,8 +293,8 @@ public class SmsMessage {
      * @hide
      */
     public static ArrayList<String> fragmentText(String text) {
-        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
-        TextEncodingDetails ted = (PHONE_TYPE_CDMA == activePhone) ?
+        // This function is for MO SMS
+        TextEncodingDetails ted = (useCdmaFormatForMoSms()) ?
             com.android.internal.telephony.cdma.SmsMessage.calculateLength(text, false) :
             com.android.internal.telephony.gsm.SmsMessage.calculateLength(text, false);
 
@@ -325,7 +336,7 @@ public class SmsMessage {
         while (pos < textLen) {
             int nextPos = 0;  // Counts code units.
             if (ted.codeUnitSize == SmsConstants.ENCODING_7BIT) {
-                if (activePhone == PHONE_TYPE_CDMA && ted.msgCount == 1) {
+                if (useCdmaFormatForMoSms() && ted.msgCount == 1) {
                     // For a singleton CDMA message, the encoding must be ASCII...
                     nextPos = pos + Math.min(limit, textLen - pos);
                 } else {
@@ -398,9 +409,8 @@ public class SmsMessage {
     public static SubmitPdu getSubmitPdu(String scAddress,
             String destinationAddress, String message, boolean statusReportRequested) {
         SubmitPduBase spb;
-        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
 
-        if (PHONE_TYPE_CDMA == activePhone) {
+        if (useCdmaFormatForMoSms()) {
             spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
                     destinationAddress, message, statusReportRequested, null);
         } else {
@@ -428,9 +438,8 @@ public class SmsMessage {
             String destinationAddress, short destinationPort, byte[] data,
             boolean statusReportRequested) {
         SubmitPduBase spb;
-        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
 
-        if (PHONE_TYPE_CDMA == activePhone) {
+        if (useCdmaFormatForMoSms()) {
             spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
                     destinationAddress, destinationPort, data, statusReportRequested);
         } else {
@@ -685,4 +694,30 @@ public class SmsMessage {
     public boolean isReplyPathPresent() {
         return mWrappedSmsMessage.isReplyPathPresent();
     }
+
+    /**
+     * Determines whether or not to use CDMA format for MO SMS.
+     * If SMS over IMS is supported, then format is based on IMS SMS format,
+     * otherwise format is based on current phone type.
+     *
+     * @return true if Cdma format should be used for MO SMS, false otherwise.
+     */
+    private static boolean useCdmaFormatForMoSms() {
+        if (!SmsManager.getDefault().isImsSmsSupported()) {
+            // use Voice technology to determine SMS format.
+            return isCdmaVoice();
+        }
+        // IMS is registered with SMS support, check the SMS format supported
+        return (SmsConstants.FORMAT_3GPP2.equals(SmsManager.getDefault().getImsSmsFormat()));
+    }
+
+    /**
+     * Determines whether or not to current phone type is cdma.
+     *
+     * @return true if current phone type is cdma, false otherwise.
+     */
+    private static boolean isCdmaVoice() {
+        int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
+        return (PHONE_TYPE_CDMA == activePhone);
+    }
 }
index 425f89a..c4536d0 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2006 The Android Open Source Project
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -42,6 +43,7 @@ public abstract class BaseCommands implements CommandsInterface {
     protected RegistrantList mVoiceNetworkStateRegistrants = new RegistrantList();
     protected RegistrantList mDataNetworkStateRegistrants = new RegistrantList();
     protected RegistrantList mVoiceRadioTechChangedRegistrants = new RegistrantList();
+    protected RegistrantList mImsNetworkStateChangedRegistrants = new RegistrantList();
     protected RegistrantList mIccStatusChangedRegistrants = new RegistrantList();
     protected RegistrantList mVoicePrivacyOnRegistrants = new RegistrantList();
     protected RegistrantList mVoicePrivacyOffRegistrants = new RegistrantList();
@@ -121,6 +123,15 @@ public abstract class BaseCommands implements CommandsInterface {
         }
     }
 
+    public void registerForImsNetworkStateChanged(Handler h, int what, Object obj) {
+        Registrant r = new Registrant (h, what, obj);
+        mImsNetworkStateChangedRegistrants.add(r);
+    }
+
+    public void unregisterForImsNetworkStateChanged(Handler h) {
+        mImsNetworkStateChangedRegistrants.remove(h);
+    }
+
     @Override
     public void registerForOn(Handler h, int what, Object obj) {
         Registrant r = new Registrant (h, what, obj);
index 310affa..670d976 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2006 The Android Open Source Project
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -110,6 +111,17 @@ public interface CommandsInterface {
     RadioState getRadioState();
 
     /**
+     * response.obj.result is an int[2]
+     *
+     * response.obj.result[0] is registration state
+     *                        0 - Not registered
+     *                        1 - Registered
+     * response.obj.result[1] is of type const RIL_IMS_SMS_Format,
+     *                        corresponds to sms format used for SMS over IMS.
+     */
+    void getImsRegistrationState(Message result);
+
+    /**
      * Fires on any RadioState transition
      * Always fires immediately as well
      *
@@ -122,6 +134,8 @@ public interface CommandsInterface {
 
     void registerForVoiceRadioTechChanged(Handler h, int what, Object obj);
     void unregisterForVoiceRadioTechChanged(Handler h);
+    void registerForImsNetworkStateChanged(Handler h, int what, Object obj);
+    void unregisterForImsNetworkStateChanged(Handler h);
 
     /**
      * Fires on any transition into RadioState.isOn()
@@ -1000,6 +1014,31 @@ public interface CommandsInterface {
     void sendCdmaSms(byte[] pdu, Message response);
 
     /**
+     * send SMS over IMS with 3GPP/GSM SMS format
+     * @param smscPDU is smsc address in PDU form GSM BCD format prefixed
+     *      by a length byte (as expected by TS 27.005) or NULL for default SMSC
+     * @param pdu is SMS in PDU format as an ASCII hex string
+     *      less the SMSC address
+     * @param retry indicates if this is a retry; 0 == not retry, nonzero = retry
+     * @param messageRef valid field if retry is set to nonzero.
+     *        Contains messageRef from RIL_SMS_Response corresponding to failed MO SMS
+     * @param response sent when operation completes
+     */
+    void sendImsGsmSms (String smscPDU, String pdu, int retry, int messageRef,
+            Message response);
+
+    /**
+     * send SMS over IMS with 3GPP2/CDMA SMS format
+     * @param pdu is CDMA-SMS in internal pseudo-PDU format
+     * @param response sent when operation completes
+     * @param retry indicates if this is a retry; 0 == not retry, nonzero = retry
+     * @param messageRef valid field if retry is set to nonzero.
+     *        Contains messageRef from RIL_SMS_Response corresponding to failed MO SMS
+     * @param response sent when operation completes
+     */
+    void sendImsCdmaSms(byte[] pdu, int retry, int messageRef, Message response);
+
+    /**
      * Deletes the specified SMS record from SIM memory (EF_SMS).
      *
      * @param index index of the SMS record to delete
index 82e20bb..88fbfc9 100644 (file)
@@ -120,6 +120,8 @@ public class IccSmsInterfaceManager extends ISms.Stub {
         mPhone = phone;
         mContext = phone.getContext();
         mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+        mDispatcher = new ImsSMSDispatcher(phone,
+                phone.mSmsStorageMonitor, phone.mSmsUsageMonitor);
         if (ServiceManager.getService("isms") == null) {
             ServiceManager.addService("isms", this);
         }
@@ -158,8 +160,13 @@ public class IccSmsInterfaceManager extends ISms.Stub {
         }
     }
 
+    public void dispose() {
+        mDispatcher.dispose();
+    }
+
     protected void updatePhoneObject(PhoneBase phone) {
         mPhone = phone;
+        mDispatcher.updatePhoneObject(phone);
     }
 
     protected void enforceReceiveAndSend(String message) {
@@ -792,4 +799,12 @@ public class IccSmsInterfaceManager extends ISms.Stub {
     protected void log(String msg) {
         Log.d(LOG_TAG, "[IccSmsInterfaceManager] " + msg);
     }
+
+    public boolean isImsSmsSupported() {
+        return mDispatcher.isIms();
+    }
+
+    public String getImsSmsFormat() {
+        return mDispatcher.getImsSmsFormat();
+    }
 }
diff --git a/src/java/com/android/internal/telephony/ImsSMSDispatcher.java b/src/java/com/android/internal/telephony/ImsSMSDispatcher.java
new file mode 100644 (file)
index 0000000..28919a0
--- /dev/null
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import static android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.os.AsyncResult;
+import android.os.Message;
+import android.provider.Telephony.Sms.Intents;
+import android.telephony.Rlog;
+
+import com.android.internal.telephony.cdma.CdmaSMSDispatcher;
+import com.android.internal.telephony.gsm.GsmSMSDispatcher;
+import com.android.internal.telephony.InboundSmsHandler;
+import com.android.internal.telephony.gsm.GsmInboundSmsHandler;
+import com.android.internal.telephony.cdma.CdmaInboundSmsHandler;
+import com.android.internal.telephony.SmsBroadcastUndelivered;
+
+public final class ImsSMSDispatcher extends SMSDispatcher {
+    private static final String TAG = "RIL_ImsSms";
+
+    private SMSDispatcher mCdmaDispatcher;
+    private SMSDispatcher mGsmDispatcher;
+
+    GsmInboundSmsHandler mGsmInboundSmsHandler;
+    CdmaInboundSmsHandler mCdmaInboundSmsHandler;
+
+    /** true if IMS is registered and sms is supported, false otherwise.*/
+    private boolean mIms = false;
+    private String mImsSmsFormat = SmsConstants.FORMAT_UNKNOWN;
+
+    public ImsSMSDispatcher(PhoneBase phone, SmsStorageMonitor storageMonitor,
+            SmsUsageMonitor usageMonitor) {
+        super(phone, usageMonitor);
+        Rlog.d(TAG, "ImsSMSDispatcher created");
+
+        // Create dispatchers, inbound SMS handlers and broadcast
+        // undelivered messages in raw table.
+        mCdmaDispatcher = new CdmaSMSDispatcher(phone,
+                storageMonitor, usageMonitor, this);
+        mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(phone.getContext(),
+                storageMonitor, phone);
+        mCdmaInboundSmsHandler = CdmaInboundSmsHandler.makeInboundSmsHandler(phone.getContext(),
+                storageMonitor, phone, (CdmaSMSDispatcher) mCdmaDispatcher);
+        mGsmDispatcher = new GsmSMSDispatcher(phone,
+                storageMonitor, usageMonitor, this, mGsmInboundSmsHandler);
+        Thread broadcastThread = new Thread(new SmsBroadcastUndelivered(phone.getContext(),
+                mGsmInboundSmsHandler, mCdmaInboundSmsHandler));
+        broadcastThread.start();
+
+        mCi.registerForOn(this, EVENT_RADIO_ON, null);
+        mCi.registerForImsNetworkStateChanged(this, EVENT_IMS_STATE_CHANGED, null);
+    }
+
+    /* Updates the phone object when there is a change */
+    @Override
+    protected void updatePhoneObject(PhoneBase phone) {
+        Rlog.d(TAG, "In IMS updatePhoneObject ");
+        super.updatePhoneObject(phone);
+        mCdmaDispatcher.updatePhoneObject(phone);
+        mGsmDispatcher.updatePhoneObject(phone);
+    }
+
+    public void dispose() {
+        mCi.unregisterForOn(this);
+        mCi.unregisterForImsNetworkStateChanged(this);
+        mGsmDispatcher.dispose();
+        mCdmaDispatcher.dispose();
+        mGsmInboundSmsHandler.dispose();
+        mCdmaInboundSmsHandler.dispose();
+    }
+
+    /**
+     * 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_RADIO_ON:
+        case EVENT_IMS_STATE_CHANGED: // received unsol
+            mCi.getImsRegistrationState(this.obtainMessage(EVENT_IMS_STATE_DONE));
+            break;
+
+        case EVENT_IMS_STATE_DONE:
+            ar = (AsyncResult) msg.obj;
+
+            if (ar.exception == null) {
+                updateImsInfo(ar);
+            } else {
+                Rlog.e(TAG, "IMS State query failed with exp "
+                        + ar.exception);
+            }
+            break;
+
+        default:
+            super.handleMessage(msg);
+        }
+    }
+
+    private void setImsSmsFormat(int format) {
+        // valid format?
+        switch (format) {
+            case PhoneConstants.PHONE_TYPE_GSM:
+                mImsSmsFormat = "3gpp";
+                break;
+            case PhoneConstants.PHONE_TYPE_CDMA:
+                mImsSmsFormat = "3gpp2";
+                break;
+            default:
+                mImsSmsFormat = "unknown";
+                break;
+        }
+    }
+
+    private void updateImsInfo(AsyncResult ar) {
+        int[] responseArray = (int[])ar.result;
+
+        mIms = false;
+        if (responseArray[0] == 1) {  // IMS is registered
+            Rlog.d(TAG, "IMS is registered!");
+            mIms = true;
+        } else {
+            Rlog.d(TAG, "IMS is NOT registered!");
+        }
+
+        setImsSmsFormat(responseArray[1]);
+
+        if (("unknown".equals(mImsSmsFormat))) {
+            Rlog.e(TAG, "IMS format was unknown!");
+            // failed to retrieve valid IMS SMS format info, set IMS to unregistered
+            mIms = false;
+        }
+    }
+
+    @Override
+    protected void sendData(String destAddr, String scAddr, int destPort,
+            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+        if (isCdmaMo()) {
+            mCdmaDispatcher.sendData(destAddr, scAddr, destPort,
+                    data, sentIntent, deliveryIntent);
+        } else {
+            mGsmDispatcher.sendData(destAddr, scAddr, destPort,
+                    data, sentIntent, deliveryIntent);
+        }
+    }
+
+    @Override
+    protected void sendMultipartText(String destAddr, String scAddr,
+            ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
+            ArrayList<PendingIntent> deliveryIntents) {
+        if (isCdmaMo()) {
+            mCdmaDispatcher.sendMultipartText(destAddr, scAddr,
+                    parts, sentIntents, deliveryIntents);
+        } else {
+            mGsmDispatcher.sendMultipartText(destAddr, scAddr,
+                    parts, sentIntents, deliveryIntents);
+        }
+    }
+
+    @Override
+    protected void sendSms(SmsTracker tracker) {
+        //  sendSms is a helper function to other send functions, sendText/Data...
+        //  it is not part of ISms.stub
+        Rlog.e(TAG, "sendSms should never be called from here!");
+    }
+
+    @Override
+    protected void sendText(String destAddr, String scAddr, String text,
+            PendingIntent sentIntent, PendingIntent deliveryIntent) {
+        Rlog.d(TAG, "sendText");
+        if (isCdmaMo()) {
+            mCdmaDispatcher.sendText(destAddr, scAddr,
+                    text, sentIntent, deliveryIntent);
+        } else {
+            mGsmDispatcher.sendText(destAddr, scAddr,
+                    text, sentIntent, deliveryIntent);
+        }
+    }
+
+    @Override
+    public void sendRetrySms(SmsTracker tracker) {
+        String oldFormat = tracker.mFormat;
+
+        // newFormat will be based on voice technology
+        String newFormat =
+            (PhoneConstants.PHONE_TYPE_CDMA == mPhone.getPhoneType()) ?
+                    mCdmaDispatcher.getFormat() :
+                        mGsmDispatcher.getFormat();
+
+        // was previously sent sms format match with voice tech?
+        if (oldFormat.equals(newFormat)) {
+            if (isCdmaFormat(newFormat)) {
+                Rlog.d(TAG, "old format matched new format (cdma)");
+                mCdmaDispatcher.sendSms(tracker);
+                return;
+            } else {
+                Rlog.d(TAG, "old format matched new format (gsm)");
+                mGsmDispatcher.sendSms(tracker);
+                return;
+            }
+        }
+
+        // format didn't match, need to re-encode.
+        HashMap map = tracker.mData;
+
+        // to re-encode, fields needed are:  scAddr, destAddr, and
+        //   text if originally sent as sendText or
+        //   data and destPort if originally sent as sendData.
+        if (!( map.containsKey("scAddr") && map.containsKey("destAddr") &&
+               ( map.containsKey("text") ||
+                       (map.containsKey("data") && map.containsKey("destPort"))))) {
+            // should never come here...
+            Rlog.e(TAG, "sendRetrySms failed to re-encode per missing fields!");
+            if (tracker.mSentIntent != null) {
+                int error = RESULT_ERROR_GENERIC_FAILURE;
+                // Done retrying; return an error to the app.
+                try {
+                    tracker.mSentIntent.send(mContext, error, null);
+                } catch (CanceledException ex) {}
+            }
+            return;
+        }
+        String scAddr = (String)map.get("scAddr");
+        String destAddr = (String)map.get("destAddr");
+
+        SmsMessageBase.SubmitPduBase pdu = null;
+        //    figure out from tracker if this was sendText/Data
+        if (map.containsKey("text")) {
+            Rlog.d(TAG, "sms failed was text");
+            String text = (String)map.get("text");
+
+            if (isCdmaFormat(newFormat)) {
+                Rlog.d(TAG, "old format (gsm) ==> new format (cdma)");
+                pdu = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(
+                        scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null);
+            } else {
+                Rlog.d(TAG, "old format (cdma) ==> new format (gsm)");
+                pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(
+                        scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null);
+            }
+        } else if (map.containsKey("data")) {
+            Rlog.d(TAG, "sms failed was data");
+            byte[] data = (byte[])map.get("data");
+            Integer destPort = (Integer)map.get("destPort");
+
+            if (isCdmaFormat(newFormat)) {
+                Rlog.d(TAG, "old format (gsm) ==> new format (cdma)");
+                pdu = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(
+                            scAddr, destAddr, destPort.intValue(), data,
+                            (tracker.mDeliveryIntent != null));
+            } else {
+                Rlog.d(TAG, "old format (cdma) ==> new format (gsm)");
+                pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(
+                            scAddr, destAddr, destPort.intValue(), data,
+                            (tracker.mDeliveryIntent != null));
+            }
+        }
+
+        // replace old smsc and pdu with newly encoded ones
+        map.put("smsc", pdu.encodedScAddress);
+        map.put("pdu", pdu.encodedMessage);
+
+        SMSDispatcher dispatcher = (isCdmaFormat(newFormat)) ?
+                mCdmaDispatcher : mGsmDispatcher;
+
+        tracker.mFormat = dispatcher.getFormat();
+        dispatcher.sendSms(tracker);
+    }
+
+    @Override
+    protected String getFormat() {
+        // this function should be defined in Gsm/CdmaDispatcher.
+        Rlog.e(TAG, "getFormat should never be called from here!");
+        return "unknown";
+    }
+
+    @Override
+    protected GsmAlphabet.TextEncodingDetails calculateLength(
+            CharSequence messageBody, boolean use7bitOnly) {
+        Rlog.e(TAG, "Error! Not implemented for IMS.");
+        return null;
+    }
+
+    @Override
+    protected void sendNewSubmitPdu(String destinationAddress, String scAddress, String message,
+            SmsHeader smsHeader, int format, PendingIntent sentIntent,
+            PendingIntent deliveryIntent, boolean lastPart) {
+        Rlog.e(TAG, "Error! Not implemented for IMS.");
+    }
+
+    @Override
+    public boolean isIms() {
+        return mIms;
+    }
+
+    @Override
+    public String getImsSmsFormat() {
+        return mImsSmsFormat;
+    }
+
+    /**
+     * Determines whether or not to use CDMA format for MO SMS.
+     * If SMS over IMS is supported, then format is based on IMS SMS format,
+     * otherwise format is based on current phone type.
+     *
+     * @return true if Cdma format should be used for MO SMS, false otherwise.
+     */
+    private boolean isCdmaMo() {
+        if (!isIms()) {
+            // IMS is not registered, use Voice technology to determine SMS format.
+            return (PhoneConstants.PHONE_TYPE_CDMA == mPhone.getPhoneType());
+        }
+        // IMS is registered with SMS support
+        return isCdmaFormat(mImsSmsFormat);
+    }
+
+    /**
+     * Determines whether or not format given is CDMA format.
+     *
+     * @param format
+     * @return true if format given is CDMA format, false otherwise.
+     */
+    private boolean isCdmaFormat(String format) {
+        return (mCdmaDispatcher.getFormat().equals(format));
+    }
+}
index 2a047c2..88ca348 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2007 The Android Open Source Project
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -111,9 +112,8 @@ public abstract class PhoneBase extends Handler implements Phone {
     protected static final int EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED = 27;
     // other
     protected static final int EVENT_SET_NETWORK_AUTOMATIC          = 28;
-    protected static final int EVENT_NEW_ICC_SMS                    = 29;
-    protected static final int EVENT_ICC_RECORD_EVENTS              = 30;
-    protected static final int EVENT_ICC_CHANGED                    = 31;
+    protected static final int EVENT_ICC_RECORD_EVENTS              = 29;
+    protected static final int EVENT_ICC_CHANGED                    = 30;
 
     // Key used to read/write current CLIR setting
     public static final String CLIR_KEY = "clir_key";
@@ -136,7 +136,6 @@ public abstract class PhoneBase extends Handler implements Phone {
     public SmsUsageMonitor mSmsUsageMonitor;
     protected AtomicReference<UiccCardApplication> mUiccApplication =
             new AtomicReference<UiccCardApplication>();
-    public SMSDispatcher mSMS;
 
     private TelephonyTester mTelephonyTester;
     private final String mName;
@@ -323,7 +322,6 @@ public abstract class PhoneBase extends Handler implements Phone {
     public void removeReferences() {
         mSmsStorageMonitor = null;
         mSmsUsageMonitor = null;
-        mSMS = null;
         mIccRecords.set(null);
         mUiccApplication.set(null);
         mDcTracker = null;
@@ -1387,7 +1385,6 @@ public abstract class PhoneBase extends Handler implements Phone {
         pw.println(" mUiccApplication=" + mUiccApplication.get());
         pw.println(" mSmsStorageMonitor=" + mSmsStorageMonitor);
         pw.println(" mSmsUsageMonitor=" + mSmsUsageMonitor);
-        pw.println(" mSMS=" + mSMS);
         pw.flush();
         pw.println(" mLooper=" + mLooper);
         pw.println(" mContext=" + mContext);
index 79a198a..a9b2c29 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2006 The Android Open Source Project
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -623,6 +624,15 @@ public final class RIL extends BaseCommands implements CommandsInterface {
     }
 
 
+    public void getImsRegistrationState(Message result) {
+        RILRequest rr = RILRequest.obtain(RIL_REQUEST_IMS_REGISTRATION_STATE, result);
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+        }
+        send(rr);
+    }
+
     @Override public void
     setOnNITZTime(Handler h, int what, Object obj) {
         super.setOnNITZTime(h, what, obj);
@@ -1183,33 +1193,33 @@ public final class RIL extends BaseCommands implements CommandsInterface {
         send(rr);
     }
 
-    @Override
+    private void
+    constructGsmSendSmsRilRequest (RILRequest rr, String smscPDU, String pdu) {
+        rr.mParcel.writeInt(2);
+        rr.mParcel.writeString(smscPDU);
+        rr.mParcel.writeString(pdu);
+    }
+
     public void
     sendSMS (String smscPDU, String pdu, Message result) {
         RILRequest rr
                 = RILRequest.obtain(RIL_REQUEST_SEND_SMS, result);
 
-        rr.mParcel.writeInt(2);
-        rr.mParcel.writeString(smscPDU);
-        rr.mParcel.writeString(pdu);
+        constructGsmSendSmsRilRequest(rr, smscPDU, pdu);
 
         if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
 
         send(rr);
     }
 
-    @Override
-    public void
-    sendCdmaSms(byte[] pdu, Message result) {
+    private void
+    constructCdmaSendSmsRilRequest(RILRequest rr, byte[] pdu) {
         int address_nbr_of_digits;
         int subaddr_nbr_of_digits;
         int bearerDataLength;
         ByteArrayInputStream bais = new ByteArrayInputStream(pdu);
         DataInputStream dis = new DataInputStream(bais);
 
-        RILRequest rr
-                = RILRequest.obtain(RIL_REQUEST_CDMA_SEND_SMS, result);
-
         try {
             rr.mParcel.writeInt(dis.readInt()); //teleServiceId
             rr.mParcel.writeByte((byte) dis.readInt()); //servicePresent
@@ -1240,6 +1250,45 @@ public final class RIL extends BaseCommands implements CommandsInterface {
             if (RILJ_LOGD) riljLog("sendSmsCdma: conversion from input stream to object failed: "
                     + ex);
         }
+    }
+
+    public void
+    sendCdmaSms(byte[] pdu, Message result) {
+        RILRequest rr
+                = RILRequest.obtain(RIL_REQUEST_CDMA_SEND_SMS, result);
+
+        constructCdmaSendSmsRilRequest(rr, pdu);
+
+        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+        send(rr);
+    }
+
+    public void
+    sendImsGsmSms (String smscPDU, String pdu, int retry, int messageRef,
+            Message result) {
+        RILRequest rr = RILRequest.obtain(RIL_REQUEST_IMS_SEND_SMS, result);
+
+        rr.mParcel.writeInt(1); //RIL_IMS_SMS_Format.FORMAT_3GPP
+        rr.mParcel.writeByte((byte)retry);
+        rr.mParcel.writeInt(messageRef);
+
+        constructGsmSendSmsRilRequest(rr, smscPDU, pdu);
+
+        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+
+        send(rr);
+    }
+
+    public void
+    sendImsCdmaSms(byte[] pdu, int retry, int messageRef, Message result) {
+        RILRequest rr = RILRequest.obtain(RIL_REQUEST_IMS_SEND_SMS, result);
+
+        rr.mParcel.writeInt(2); //RIL_IMS_SMS_Format.FORMAT_3GPP2
+        rr.mParcel.writeByte((byte)retry);
+        rr.mParcel.writeInt(messageRef);
+
+        constructCdmaSendSmsRilRequest(rr, pdu);
 
         if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
 
@@ -2385,6 +2434,8 @@ public final class RIL extends BaseCommands implements CommandsInterface {
             case RIL_REQUEST_GET_CELL_INFO_LIST: ret = responseCellInfoList(p); break;
             case RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE: ret = responseVoid(p); break;
             case RIL_REQUEST_SET_INITIAL_ATTACH_APN: ret = responseVoid(p); break;
+            case RIL_REQUEST_IMS_REGISTRATION_STATE: ret = responseInts(p); break;
+            case RIL_REQUEST_IMS_SEND_SMS: ret =  responseSMS(p); break;
             default:
                 throw new RuntimeException("Unrecognized solicited response: " + rr.mRequest);
             //break;
@@ -2564,6 +2615,7 @@ public final class RIL extends BaseCommands implements CommandsInterface {
             case RIL_UNSOL_RIL_CONNECTED: ret = responseInts(p); break;
             case RIL_UNSOL_VOICE_RADIO_TECH_CHANGED: ret =  responseInts(p); break;
             case RIL_UNSOL_CELL_INFO_LIST: ret = responseCellInfoList(p); break;
+            case RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED: ret =  responseVoid(p); break;
 
             default:
                 throw new RuntimeException("Unrecognized unsol response: " + response);
@@ -2582,6 +2634,12 @@ public final class RIL extends BaseCommands implements CommandsInterface {
 
                 switchToRadioState(newState);
             break;
+            case RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED:
+                if (RILJ_LOGD) unsljLog(response);
+
+                mImsNetworkStateChangedRegistrants
+                    .notifyRegistrants(new AsyncResult(null, null, null));
+            break;
             case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED:
                 if (RILJ_LOGD) unsljLog(response);
 
@@ -3681,6 +3739,8 @@ public final class RIL extends BaseCommands implements CommandsInterface {
             case RIL_REQUEST_GET_CELL_INFO_LIST: return "RIL_REQUEST_GET_CELL_INFO_LIST";
             case RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE: return "RIL_REQUEST_SET_CELL_INFO_LIST_RATE";
             case RIL_REQUEST_SET_INITIAL_ATTACH_APN: return "RIL_REQUEST_SET_INITIAL_ATTACH_APN";
+            case RIL_REQUEST_IMS_REGISTRATION_STATE: return "RIL_REQUEST_IMS_REGISTRATION_STATE";
+            case RIL_REQUEST_IMS_SEND_SMS: return "RIL_REQUEST_IMS_SEND_SMS";
             default: return "<unknown request>";
         }
     }
@@ -3731,7 +3791,9 @@ public final class RIL extends BaseCommands implements CommandsInterface {
             case RIL_UNSOL_RIL_CONNECTED: return "UNSOL_RIL_CONNECTED";
             case RIL_UNSOL_VOICE_RADIO_TECH_CHANGED: return "UNSOL_VOICE_RADIO_TECH_CHANGED";
             case RIL_UNSOL_CELL_INFO_LIST: return "UNSOL_CELL_INFO_LIST";
-            default: return "<unknown reponse>";
+            case RIL_UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED:
+                return "UNSOL_RESPONSE_IMS_NETWORK_STATE_CHANGED";
+            default: return "<unknown response>";
         }
     }
 
index 4c19abf..f11f91d 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2006 The Android Open Source Project
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -105,10 +106,24 @@ public abstract class SMSDispatcher extends Handler {
     /** Handle status report from {@code CdmaInboundSmsHandler}. */
     protected static final int EVENT_HANDLE_STATUS_REPORT = 10;
 
-    protected final Phone mPhone;
+    /** Radio is ON */
+    protected static final int EVENT_RADIO_ON = 11;
+
+    /** IMS registration/SMS format changed */
+    protected static final int EVENT_IMS_STATE_CHANGED = 12;
+
+    /** Callback from RIL_REQUEST_IMS_REGISTRATION_STATE */
+    protected static final int EVENT_IMS_STATE_DONE = 13;
+
+    // other
+    protected static final int EVENT_NEW_ICC_SMS = 14;
+    protected static final int EVENT_ICC_CHANGED = 15;
+
+    protected PhoneBase mPhone;
     protected final Context mContext;
     protected final ContentResolver mResolver;
     protected final CommandsInterface mCi;
+    protected SmsStorageMonitor mStorageMonitor;
     protected final TelephonyManager mTelephonyManager;
 
     /** Maximum number of times to retry sending a failed SMS. */
@@ -129,7 +144,7 @@ public abstract class SMSDispatcher extends Handler {
     private static int sConcatenatedRef = new Random().nextInt(256);
 
     /** Outgoing message counter. Shared by all dispatchers. */
-    private final SmsUsageMonitor mUsageMonitor;
+    private SmsUsageMonitor mUsageMonitor;
 
     /** Number of outgoing SmsTrackers waiting for user confirmation. */
     private int mPendingTrackerCount;
@@ -189,6 +204,13 @@ public abstract class SMSDispatcher extends Handler {
         }
     }
 
+    protected void updatePhoneObject(PhoneBase phone) {
+        mPhone = phone;
+        mStorageMonitor = phone.mSmsStorageMonitor;
+        mUsageMonitor = phone.mSmsUsageMonitor;
+        Rlog.d(TAG, "Active phone changed to " + mPhone.getPhoneName() );
+    }
+
     /** Unregister for incoming SMS events. */
     public void dispose() {
         mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
@@ -241,7 +263,8 @@ public abstract class SMSDispatcher extends Handler {
             break;
 
         case EVENT_SEND_RETRY:
-            sendSms((SmsTracker) msg.obj);
+            Rlog.d(TAG, "SMS retry..");
+            sendRetrySms((SmsTracker) msg.obj);
             break;
 
         case EVENT_SEND_LIMIT_REACHED_CONFIRMATION:
@@ -338,7 +361,22 @@ public abstract class SMSDispatcher extends Handler {
 
             int ss = mPhone.getServiceState().getState();
 
-            if (ss != ServiceState.STATE_IN_SERVICE) {
+            if ( tracker.mImsRetry > 0 && ss != ServiceState.STATE_IN_SERVICE) {
+                // This is retry after failure over IMS but voice is not available.
+                // Set retry to max allowed, so no retry is sent and
+                //   cause RESULT_ERROR_GENERIC_FAILURE to be returned to app.
+                tracker.mRetryCount = MAX_SEND_RETRIES;
+
+                Rlog.d(TAG, "handleSendComplete: Skipping retry: "
+                +" isIms()="+isIms()
+                +" mRetryCount="+tracker.mRetryCount
+                +" mImsRetry="+tracker.mImsRetry
+                +" mMessageRef="+tracker.mMessageRef
+                +" SS= "+mPhone.getServiceState().getState());
+            }
+
+            // if sms over IMS is not supported on data and voice is not available...
+            if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
                 handleNotInService(ss, tracker.mSentIntent);
             } else if ((((CommandException)(ar.exception)).getCommandError()
                     == CommandException.Error.SMS_FAIL_RETRY) &&
@@ -562,11 +600,11 @@ public abstract class SMSDispatcher extends Handler {
 
     /**
      * Send a SMS
-     *
-     * @param smsc the SMSC to send the message through, or NULL for the
+     * @param tracker will contain:
+     * -smsc the SMSC to send the message through, or NULL for the
      *  default SMSC
-     * @param pdu the raw PDU to send
-     * @param sentIntent if not NULL this <code>Intent</code> is
+     * -pdu the raw PDU to send
+     * -sentIntent if not NULL this <code>Intent</code> is
      *  broadcast when the message is successfully sent, or failed.
      *  The result code will be <code>Activity.RESULT_OK<code> for success,
      *  or one of these errors:
@@ -577,13 +615,16 @@ public abstract class SMSDispatcher extends Handler {
      *  The per-application based SMS control checks sentIntent. If sentIntent
      *  is NULL the caller will be checked against all unknown applications,
      *  which cause smaller number of SMS to be sent in checking period.
-     * @param deliveryIntent if not NULL this <code>Intent</code> is
+     * -deliveryIntent if not NULL this <code>Intent</code> is
      *  broadcast when the message is delivered to the recipient.  The
      *  raw pdu of the status report is in the extended data ("pdu").
-     * @param destAddr the destination phone number (for short code confirmation)
+     * -param destAddr the destination phone number (for short code confirmation)
      */
-    protected void sendRawPdu(byte[] smsc, byte[] pdu, PendingIntent sentIntent,
-            PendingIntent deliveryIntent, String destAddr) {
+    protected void sendRawPdu(SmsTracker tracker) {
+        HashMap map = tracker.mData;
+        byte pdu[] = (byte[]) map.get("pdu");
+
+        PendingIntent sentIntent = tracker.mSentIntent;
         if (mSmsSendDisabled) {
             if (sentIntent != null) {
                 try {
@@ -603,10 +644,6 @@ public abstract class SMSDispatcher extends Handler {
             return;
         }
 
-        HashMap<String, Object> map = new HashMap<String, Object>();
-        map.put("smsc", smsc);
-        map.put("pdu", pdu);
-
         // Get calling app package name via UID from Binder call
         PackageManager pm = mContext.getPackageManager();
         String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid());
@@ -641,11 +678,6 @@ public abstract class SMSDispatcher extends Handler {
             return;
         }
 
-        // Strip non-digits from destination phone number before checking for short codes
-        // and before displaying the number to the user if confirmation is required.
-        SmsTracker tracker = new SmsTracker(map, sentIntent, deliveryIntent, appInfo,
-                PhoneNumberUtils.extractNetworkPortion(destAddr));
-
         // checkDestination() returns true if the destination is not a premium short code or the
         // sending app is approved to send to short codes. Otherwise, a message is sent to our
         // handler with the SmsTracker to request user confirmation before sending.
@@ -658,7 +690,8 @@ public abstract class SMSDispatcher extends Handler {
 
             int ss = mPhone.getServiceState().getState();
 
-            if (ss != ServiceState.STATE_IN_SERVICE) {
+            // if sms over IMS is not supported on data and voice is not available...
+            if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
                 handleNotInService(ss, tracker.mSentIntent);
             } else {
                 sendSms(tracker);
@@ -895,6 +928,13 @@ public abstract class SMSDispatcher extends Handler {
     protected abstract void sendSms(SmsTracker tracker);
 
     /**
+     * Retry the message along to the radio.
+     *
+     * @param tracker holds the SMS message to send
+     */
+    public abstract void sendRetrySms(SmsTracker tracker);
+
+    /**
      * Send the multi-part SMS based on multipart Sms tracker
      *
      * @param tracker holds the multipart Sms tracker ready to be sent
@@ -915,7 +955,8 @@ public abstract class SMSDispatcher extends Handler {
 
         // check if in service
         int ss = mPhone.getServiceState().getState();
-        if (ss != ServiceState.STATE_IN_SERVICE) {
+        // if sms over IMS is not supported on data and voice is not available...
+        if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
             for (int i = 0, count = parts.size(); i < count; i++) {
                 PendingIntent sentIntent = null;
                 if (sentIntents != null && sentIntents.size() > i) {
@@ -937,7 +978,9 @@ public abstract class SMSDispatcher extends Handler {
         // fields need to be public for derived SmsDispatchers
         public final HashMap<String, Object> mData;
         public int mRetryCount;
+        public int mImsRetry; // nonzero indicates initial message was sent over Ims
         public int mMessageRef;
+        String mFormat;
 
         public final PendingIntent mSentIntent;
         public final PendingIntent mDeliveryIntent;
@@ -945,14 +988,17 @@ public abstract class SMSDispatcher extends Handler {
         public final PackageInfo mAppInfo;
         public final String mDestAddress;
 
-        public SmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
-                PendingIntent deliveryIntent, PackageInfo appInfo, String destAddr) {
+        private SmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
+                PendingIntent deliveryIntent, PackageInfo appInfo, String destAddr, String format) {
             mData = data;
             mSentIntent = sentIntent;
             mDeliveryIntent = deliveryIntent;
             mRetryCount = 0;
             mAppInfo = appInfo;
             mDestAddress = destAddr;
+            mFormat = format;
+            mImsRetry = 0;
+            mMessageRef = 0;
         }
 
         /**
@@ -965,6 +1011,51 @@ public abstract class SMSDispatcher extends Handler {
         }
     }
 
+    protected SmsTracker SmsTrackerFactory(HashMap<String, Object> data, PendingIntent sentIntent,
+            PendingIntent deliveryIntent, String format) {
+        // Get calling app package name via UID from Binder call
+        PackageManager pm = mContext.getPackageManager();
+        String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid());
+
+        // Get package info via packagemanager
+        PackageInfo appInfo = null;
+        if (packageNames != null && packageNames.length > 0) {
+            try {
+                // XXX this is lossy- apps can share a UID
+                appInfo = pm.getPackageInfo(packageNames[0], PackageManager.GET_SIGNATURES);
+            } catch (PackageManager.NameNotFoundException e) {
+                // error will be logged in sendRawPdu
+            }
+        }
+        // Strip non-digits from destination phone number before checking for short codes
+        // and before displaying the number to the user if confirmation is required.
+        String destAddr = PhoneNumberUtils.extractNetworkPortion((String) data.get("destAddr"));
+        return new SmsTracker(data, sentIntent, deliveryIntent, appInfo, destAddr, format);
+    }
+
+    protected HashMap SmsTrackerMapFactory(String destAddr, String scAddr,
+            String text, SmsMessageBase.SubmitPduBase pdu) {
+        HashMap<String, Object> map = new HashMap<String, Object>();
+        map.put("destAddr", destAddr);
+        map.put("scAddr", scAddr);
+        map.put("text", text);
+        map.put("smsc", pdu.encodedScAddress);
+        map.put("pdu", pdu.encodedMessage);
+        return map;
+    }
+
+    protected HashMap SmsTrackerMapFactory(String destAddr, String scAddr,
+            int destPort, byte[] data, SmsMessageBase.SubmitPduBase pdu) {
+        HashMap<String, Object> map = new HashMap<String, Object>();
+        map.put("destAddr", destAddr);
+        map.put("scAddr", scAddr);
+        map.put("destPort", Integer.valueOf(destPort));
+        map.put("data", data);
+        map.put("smsc", pdu.encodedScAddress);
+        map.put("pdu", pdu.encodedMessage);
+        return map;
+    }
+
     /**
      * Dialog listener for SMS confirmation dialog.
      */
@@ -1049,4 +1140,8 @@ public abstract class SMSDispatcher extends Handler {
             }
         }
     }
+
+    public abstract boolean isIms();
+
+    public abstract String getImsSmsFormat();
 }
index ae79e35..1f9368a 100644 (file)
@@ -1,5 +1,6 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
+/* 
+ * Copyrigh (C) 2011 The Android Open Source Project
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -28,14 +29,13 @@ import android.provider.Telephony;
 import android.telephony.Rlog;
 
 import com.android.internal.telephony.CommandsInterface;
-import com.android.internal.telephony.InboundSmsHandler;
+
 import com.android.internal.telephony.OperatorInfo;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.PhoneNotifier;
 import com.android.internal.telephony.PhoneProxy;
 import com.android.internal.telephony.SMSDispatcher;
 import com.android.internal.telephony.SmsBroadcastUndelivered;
-import com.android.internal.telephony.gsm.GsmInboundSmsHandler;
 import com.android.internal.telephony.gsm.GsmSMSDispatcher;
 import com.android.internal.telephony.gsm.SmsMessage;
 import com.android.internal.telephony.uicc.IsimRecords;
@@ -51,17 +51,12 @@ public class CDMALTEPhone extends CDMAPhone {
     static final String LOG_LTE_TAG = "CDMALTEPhone";
     private static final boolean DBG = true;
 
-    /** Secondary SMSDispatcher for 3GPP format messages. */
-    SMSDispatcher m3gppSMS;
-
     /** CdmaLtePhone in addition to RuimRecords available from
      * PhoneBase needs access to SIMRecords and IsimUiccRecords
      */
     private SIMRecords mSimRecords;
     private IsimUiccRecords mIsimUiccRecords;
 
-    private GsmInboundSmsHandler mGsmInboundSmsHandler;
-
     /**
      * Small container class used to hold information relevant to
      * the carrier selection process. operatorNumeric can be ""
@@ -87,11 +82,6 @@ public class CDMALTEPhone extends CDMAPhone {
                 handleSetSelectNetwork((AsyncResult) msg.obj);
                 break;
 
-            case EVENT_NEW_ICC_SMS:
-                // pass to InboundSmsHandler to process
-                mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, msg.obj);
-                break;
-
             default:
                 super.handleMessage(msg);
         }
@@ -102,35 +92,16 @@ public class CDMALTEPhone extends CDMAPhone {
         mSST = new CdmaLteServiceStateTracker(this);
     }
 
-    /**
-     * Create SMSDispatcher and InboundSmsHandler for 3GPP format messages.
-     * @return the new GsmInboundSmsHandler (to pass to {@link SmsBroadcastUndelivered})
-     */
-    @Override
-    protected GsmInboundSmsHandler initGsmSmsDispatcher() {
-        // Create 3GPP SMS dispatcher for outgoing messages.
-        m3gppSMS = new GsmSMSDispatcher(this, mSmsUsageMonitor);
-
-        // Create 3GPP inbound SMS handler.
-        mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(mContext,
-                mSmsStorageMonitor, this);
-
-        return mGsmInboundSmsHandler;
-    }
-
     @Override
     public void dispose() {
         synchronized(PhoneProxy.lockForRadioTechnologyChange) {
             super.dispose();
-            m3gppSMS.dispose();
-            mGsmInboundSmsHandler.dispose();
         }
     }
 
     @Override
     public void removeReferences() {
         super.removeReferences();
-        m3gppSMS = null;
     }
 
     @Override
@@ -313,13 +284,11 @@ public class CDMALTEPhone extends CDMAPhone {
         if (mSimRecords != newSimRecords) {
             if (mSimRecords != null) {
                 log("Removing stale SIMRecords object.");
-                mSimRecords.unregisterForNewSms(this);
                 mSimRecords = null;
             }
             if (newSimRecords != null) {
                 log("New SIMRecords found");
                 mSimRecords = newSimRecords;
-                mSimRecords.registerForNewSms(this, EVENT_NEW_ICC_SMS, null);
             }
         }
 
@@ -343,6 +312,5 @@ public class CDMALTEPhone extends CDMAPhone {
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("CDMALTEPhone extends:");
         super.dump(fd, pw, args);
-        pw.println(" m3gppSMS=" + m3gppSMS);
     }
 }
index b9b318a..aff5122 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2006 The Android Open Source Project
- * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
@@ -61,7 +62,6 @@ import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.TelephonyProperties;
 import com.android.internal.telephony.UUSInfo;
 import com.android.internal.telephony.dataconnection.DcTracker;
-import com.android.internal.telephony.gsm.GsmInboundSmsHandler;
 import com.android.internal.telephony.uicc.IccException;
 import com.android.internal.telephony.uicc.IccRecords;
 import com.android.internal.telephony.uicc.RuimRecords;
@@ -107,8 +107,6 @@ public class CDMAPhone extends PhoneBase {
     EriManager mEriManager;
     WakeLock mWakeLock;
 
-    protected CdmaInboundSmsHandler mCdmaInboundSmsHandler;
-
     // mEriFileLoadedRegistrants are informed after the ERI text has been loaded
     private final RegistrantList mEriFileLoadedRegistrants = new RegistrantList();
 
@@ -157,41 +155,11 @@ public class CDMAPhone extends PhoneBase {
         mSST = new CdmaServiceStateTracker(this);
     }
 
-    /**
-     * Implemented in {@link CDMALTEPhone} to create SMSDispatcher and InboundSmsHandler for 3GPP.
-     * @return the new GsmInboundSmsHandler (to pass to SmsBroadcastUndelivered), or null
-     */
-    protected GsmInboundSmsHandler initGsmSmsDispatcher() {
-        return null;
-    }
-
-    /**
-     * Create SMS dispatcher(s) and inbound SMS handler(s), and start worker thread for
-     * {@link SmsBroadcastUndelivered}.
-     */
-    protected void initSmsDispatcher() {
-        // Create 3GPP2 SMS dispatcher for outgoing messages.
-        mSMS = new CdmaSMSDispatcher(this, mSmsUsageMonitor);
-
-        // Create 3GPP2 inbound SMS handler.
-        mCdmaInboundSmsHandler = CdmaInboundSmsHandler.makeInboundSmsHandler(mContext,
-                mSmsStorageMonitor, this, (CdmaSMSDispatcher) mSMS);
-
-        // Create 3GPP dispatcher and handler for CDMA/LTE devices.
-        GsmInboundSmsHandler gsmInboundSmsHandler = initGsmSmsDispatcher();
-
-        // Find undelivered messages in raw table and send to inbound SMS handler to broadcast.
-        Thread broadcastThread = new Thread(new SmsBroadcastUndelivered(mContext,
-                gsmInboundSmsHandler, mCdmaInboundSmsHandler));
-        broadcastThread.start();
-    }
-
     protected void init(Context context, PhoneNotifier notifier) {
         mCi.setPhoneType(PhoneConstants.PHONE_TYPE_CDMA);
         mCT = new CdmaCallTracker(this);
         mCdmaSSM = CdmaSubscriptionSourceManager.getInstance(context, mCi, this,
                 EVENT_CDMA_SUBSCRIPTION_SOURCE_CHANGED, null);
-        initSmsDispatcher();
         mDcTracker = new DcTracker(this);
         mRuimPhoneBookInterfaceManager = new RuimPhoneBookInterfaceManager(this);
         mSubInfo = new PhoneSubInfo(this);
@@ -272,8 +240,6 @@ public class CDMAPhone extends PhoneBase {
             mDcTracker.dispose();
             mSST.dispose();
             mCdmaSSM.dispose(this);
-            mSMS.dispose();
-            mCdmaInboundSmsHandler.dispose();
             mRuimPhoneBookInterfaceManager.dispose();
             mSubInfo.dispose();
             mEriManager.dispose();
index 9315ce9..a2e1b1d 100755 (executable)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -26,26 +27,28 @@ import android.telephony.Rlog;
 import android.telephony.SmsManager;
 
 import com.android.internal.telephony.GsmAlphabet;
-import com.android.internal.telephony.SMSDispatcher;
 import com.android.internal.telephony.SmsConstants;
+import com.android.internal.telephony.PhoneBase;
+import com.android.internal.telephony.SMSDispatcher;
+import com.android.internal.telephony.ImsSMSDispatcher;
 import com.android.internal.telephony.SmsHeader;
+import com.android.internal.telephony.SmsStorageMonitor;
 import com.android.internal.telephony.SmsUsageMonitor;
 import com.android.internal.telephony.TelephonyProperties;
 import com.android.internal.telephony.cdma.sms.UserData;
 
 import java.util.HashMap;
 
-final class CdmaSMSDispatcher extends SMSDispatcher {
+public class CdmaSMSDispatcher extends SMSDispatcher {
     private static final String TAG = "CdmaSMSDispatcher";
     private static final boolean VDBG = false;
+    private ImsSMSDispatcher mImsSMSDispatcher;
 
-    CdmaSMSDispatcher(CDMAPhone phone, SmsUsageMonitor usageMonitor) {
+    public CdmaSMSDispatcher(PhoneBase phone, SmsStorageMonitor storageMonitor,
+            SmsUsageMonitor usageMonitor, ImsSMSDispatcher imsSMSDispatcher) {
         super(phone, usageMonitor);
-    }
-
-    @Override
-    protected String getFormat() {
-        return android.telephony.SmsMessage.FORMAT_3GPP2;
+        mImsSMSDispatcher = imsSMSDispatcher;
+        Rlog.d(TAG, "CdmaSMSDispatcher created");
     }
 
     /**
@@ -67,6 +70,10 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
         }
     }
 
+    protected String getFormat() {
+        return SmsConstants.FORMAT_3GPP2;
+    }
+
     /**
      * Called from parent class to handle status report from {@code CdmaInboundSmsHandler}.
      * @param sms the CDMA SMS message to process
@@ -80,7 +87,7 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
                 PendingIntent intent = tracker.mDeliveryIntent;
                 Intent fillIn = new Intent();
                 fillIn.putExtra("pdu", sms.getPdu());
-                fillIn.putExtra("format", android.telephony.SmsMessage.FORMAT_3GPP2);
+                fillIn.putExtra("format", getFormat());
                 try {
                     intent.send(mContext, Activity.RESULT_OK, fillIn);
                 } catch (CanceledException ex) {}
@@ -95,7 +102,10 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
             byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
         SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
                 scAddr, destAddr, destPort, data, (deliveryIntent != null));
-        sendSubmitPdu(pdu, sentIntent, deliveryIntent, destAddr);
+        HashMap map =  SmsTrackerMapFactory(destAddr, scAddr, destPort, data, pdu);
+        SmsTracker tracker = SmsTrackerFactory(map, sentIntent, deliveryIntent,
+                getFormat());
+        sendSubmitPdu(tracker);
     }
 
     /** {@inheritDoc} */
@@ -104,7 +114,10 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
             PendingIntent sentIntent, PendingIntent deliveryIntent) {
         SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
                 scAddr, destAddr, text, (deliveryIntent != null), null);
-        sendSubmitPdu(pdu, sentIntent, deliveryIntent, destAddr);
+        HashMap map =  SmsTrackerMapFactory(destAddr, scAddr, text, pdu);
+        SmsTracker tracker = SmsTrackerFactory(map, sentIntent,
+                deliveryIntent, getFormat());
+        sendSubmitPdu(tracker);
     }
 
     /** {@inheritDoc} */
@@ -136,15 +149,18 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
         SmsMessage.SubmitPdu submitPdu = SmsMessage.getSubmitPdu(destinationAddress,
                 uData, (deliveryIntent != null) && lastPart);
 
-        sendSubmitPdu(submitPdu, sentIntent, deliveryIntent, destinationAddress);
+        HashMap map =  SmsTrackerMapFactory(destinationAddress, scAddress,
+                message, submitPdu);
+        SmsTracker tracker = SmsTrackerFactory(map, sentIntent,
+                deliveryIntent, getFormat());
+        sendSubmitPdu(tracker);
     }
 
-    void sendSubmitPdu(SmsMessage.SubmitPdu pdu,
-            PendingIntent sentIntent, PendingIntent deliveryIntent, String destAddr) {
+    protected void sendSubmitPdu(SmsTracker tracker) {
         if (SystemProperties.getBoolean(TelephonyProperties.PROPERTY_INECM_MODE, false)) {
-            if (sentIntent != null) {
+            if (tracker.mSentIntent != null) {
                 try {
-                    sentIntent.send(SmsManager.RESULT_ERROR_NO_SERVICE);
+                    tracker.mSentIntent.send(SmsManager.RESULT_ERROR_NO_SERVICE);
                 } catch (CanceledException ex) {}
             }
             if (VDBG) {
@@ -152,7 +168,7 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
             }
             return;
         }
-        sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent, destAddr);
+        sendRawPdu(tracker);
     }
 
     /** {@inheritDoc} */
@@ -164,6 +180,41 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
         byte[] pdu = (byte[]) map.get("pdu");
 
         Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
-        mCi.sendCdmaSms(pdu, reply);
+
+        Rlog.d(TAG, "sendSms: "
+                +" isIms()="+isIms()
+                +" mRetryCount="+tracker.mRetryCount
+                +" mImsRetry="+tracker.mImsRetry
+                +" mMessageRef="+tracker.mMessageRef
+                +" SS=" +mPhone.getServiceState().getState());
+
+        // sms over cdma is used:
+        //   if sms over IMS is not supported AND
+        //   this is not a retry case after sms over IMS failed
+        //     indicated by mImsRetry > 0
+        if (0 == tracker.mImsRetry && !isIms()) {
+            mCi.sendCdmaSms(pdu, reply);
+        } else {
+            mCi.sendImsCdmaSms(pdu, tracker.mImsRetry, tracker.mMessageRef, reply);
+            // increment it here, so in case of SMS_FAIL_RETRY over IMS
+            // next retry will be sent using IMS request again.
+            tracker.mImsRetry++;
+        }
+    }
+
+    @Override
+    public void sendRetrySms(SmsTracker tracker) {
+        //re-routing to ImsSMSDispatcher
+        mImsSMSDispatcher.sendRetrySms(tracker);
+    }
+
+    @Override
+    public boolean isIms() {
+        return mImsSMSDispatcher.isIms();
+    }
+
+    @Override
+    public String getImsSmsFormat() {
+        return mImsSMSDispatcher.getImsSmsFormat();
     }
 }
index 4300d2a..e36ac45 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -113,6 +114,9 @@ public class SmsMessage extends SmsMessageBase {
         } catch (RuntimeException ex) {
             Rlog.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
             return null;
+        } catch (OutOfMemoryError e) {
+            Log.e(LOG_TAG, "SMS PDU parsing failed with out of memory: ", e);
+            return null;
         }
     }
 
@@ -514,6 +518,13 @@ public class SmsMessage extends SmsMessageBase {
 
             length = dis.readUnsignedByte();
             addr.numberOfDigits = length;
+
+            // sanity check on the length
+            if (length > pdu.length) {
+                throw new RuntimeException(
+                        "createFromPdu: Invalid pdu, addr.numberOfDigits " + length
+                        + " > pdu len " + pdu.length);
+            }
             addr.origBytes = new byte[length];
             dis.read(addr.origBytes, 0, length); // digits
 
@@ -525,9 +536,18 @@ public class SmsMessage extends SmsMessageBase {
 
             //encoded BearerData:
             bearerDataLength = dis.readInt();
+            // sanity check on the length
+            if (bearerDataLength > pdu.length) {
+                throw new RuntimeException(
+                        "createFromPdu: Invalid pdu, bearerDataLength " + bearerDataLength
+                        + " > pdu len " + pdu.length);
+            }
             env.bearerData = new byte[bearerDataLength];
             dis.read(env.bearerData, 0, bearerDataLength);
             dis.close();
+        } catch (IOException ex) {
+            throw new RuntimeException(
+                    "createFromPdu: conversion from byte array to object failed: " + ex, ex);
         } catch (Exception ex) {
             Rlog.e(LOG_TAG, "createFromPdu: conversion from byte array to object failed: " + ex);
         }
index 9c4a913..a7991ef 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2006 The Android Open Source Project
- * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -50,7 +50,6 @@ import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDI
 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_BASEBAND_VERSION;
 
-import com.android.internal.telephony.InboundSmsHandler;
 import com.android.internal.telephony.SmsBroadcastUndelivered;
 import com.android.internal.telephony.dataconnection.DcTracker;
 import com.android.internal.telephony.CallForwardInfo;
@@ -157,7 +156,6 @@ public class GSMPhone extends PhoneBase {
         mCi.setPhoneType(PhoneConstants.PHONE_TYPE_GSM);
         mCT = new GsmCallTracker(this);
         mSST = new GsmServiceStateTracker (this);
-        mSMS = new GsmSMSDispatcher(this, mSmsUsageMonitor);
 
         mDcTracker = new DcTracker(this);
         if (!unitTestMode) {
@@ -172,13 +170,6 @@ public class GSMPhone extends PhoneBase {
         mCi.setOnSuppServiceNotification(this, EVENT_SSN, null);
         mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null);
 
-        // Create inbound SMS handler and broadcast undelivered messages in raw table.
-        mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(context,
-                mSmsStorageMonitor, this);
-        Thread broadcastThread = new Thread(new SmsBroadcastUndelivered(context,
-                mGsmInboundSmsHandler, null));
-        broadcastThread.start();
-
         if (DBG_PORT) {
             try {
                 //debugSocket = new LocalServerSocket("com.android.internal.telephony.debug");
@@ -239,8 +230,6 @@ public class GSMPhone extends PhoneBase {
             mCT.dispose();
             mDcTracker.dispose();
             mSST.dispose();
-            mSMS.dispose();
-            mGsmInboundSmsHandler.dispose();
             mSimPhoneBookIntManager.dispose();
             mSubInfo.dispose();
         }
@@ -1377,11 +1366,6 @@ public class GSMPhone extends PhoneBase {
                 }
                 break;
 
-            case EVENT_NEW_ICC_SMS:
-                // pass to InboundSmsHandler to process
-                mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, msg.obj);
-                break;
-
             case EVENT_SET_NETWORK_AUTOMATIC:
                 ar = (AsyncResult)msg.obj;
                 setNetworkSelectionModeAutomatic((Message)ar.result);
@@ -1611,7 +1595,6 @@ public class GSMPhone extends PhoneBase {
         }
         r.registerForNetworkSelectionModeAutomatic(
                 this, EVENT_SET_NETWORK_AUTOMATIC, null);
-        r.registerForNewSms(this, EVENT_NEW_ICC_SMS, null);
         r.registerForRecordsEvents(this, EVENT_ICC_RECORD_EVENTS, null);
         r.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null);
     }
@@ -1622,7 +1605,6 @@ public class GSMPhone extends PhoneBase {
             return;
         }
         r.unregisterForNetworkSelectionModeAutomatic(this);
-        r.unregisterForNewSms(this);
         r.unregisterForRecordsEvents(this);
         r.unregisterForRecordsLoaded(this);
     }
index 0ba7d0f..0bc066a 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2006 The Android Open Source Project
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -30,21 +31,44 @@ import com.android.internal.telephony.GsmAlphabet;
 import com.android.internal.telephony.PhoneBase;
 import com.android.internal.telephony.SMSDispatcher;
 import com.android.internal.telephony.SmsConstants;
+import com.android.internal.telephony.ImsSMSDispatcher;
+import com.android.internal.telephony.InboundSmsHandler;
 import com.android.internal.telephony.SmsHeader;
+import com.android.internal.telephony.SmsStorageMonitor;
 import com.android.internal.telephony.SmsUsageMonitor;
+import com.android.internal.telephony.TelephonyProperties;
+import com.android.internal.telephony.uicc.IccRecords;
 import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.telephony.uicc.UiccCardApplication;
+import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.telephony.uicc.UsimServiceTable;
+import com.android.internal.telephony.gsm.GsmInboundSmsHandler;
 
 import java.util.HashMap;
+import java.util.Iterator;
+import java.util.concurrent.atomic.AtomicReference;
 
 public final class GsmSMSDispatcher extends SMSDispatcher {
     private static final String TAG = "GsmSMSDispatcher";
+    private static final boolean VDBG = false;
+    private ImsSMSDispatcher mImsSMSDispatcher;
+    protected UiccController mUiccController = null;
+    private AtomicReference<IccRecords> mIccRecords = new AtomicReference<IccRecords>();
+    private AtomicReference<UiccCardApplication> mUiccApplication =
+            new AtomicReference<UiccCardApplication>();
+    private GsmInboundSmsHandler mGsmInboundSmsHandler;
 
     /** Status report received */
     private static final int EVENT_NEW_SMS_STATUS_REPORT = 100;
 
-    public GsmSMSDispatcher(PhoneBase phone, SmsUsageMonitor usageMonitor) {
+    public GsmSMSDispatcher(PhoneBase phone, SmsStorageMonitor storageMonitor,
+            SmsUsageMonitor usageMonitor, ImsSMSDispatcher imsSMSDispatcher,
+            GsmInboundSmsHandler gsmInboundSmsHandler) {
         super(phone, usageMonitor);
         mCi.setOnSmsStatus(this, EVENT_NEW_SMS_STATUS_REPORT, null);
+        mImsSMSDispatcher = imsSMSDispatcher;
+        mGsmInboundSmsHandler = gsmInboundSmsHandler;
+        Rlog.d(TAG, "GsmSMSDispatcher created");
     }
 
     @Override
@@ -71,6 +95,15 @@ public final class GsmSMSDispatcher extends SMSDispatcher {
             handleStatusReport((AsyncResult) msg.obj);
             break;
 
+        case EVENT_NEW_ICC_SMS:
+        // pass to InboundSmsHandler to process
+        mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, msg.obj);
+        break;
+
+        case EVENT_ICC_CHANGED:
+            onUpdateIccAvailability();
+            break;
+
         default:
             super.handleMessage(msg);
         }
@@ -100,7 +133,7 @@ public final class GsmSMSDispatcher extends SMSDispatcher {
                     PendingIntent intent = tracker.mDeliveryIntent;
                     Intent fillIn = new Intent();
                     fillIn.putExtra("pdu", IccUtils.hexStringToBytes(pduString));
-                    fillIn.putExtra("format", SmsConstants.FORMAT_3GPP);
+                    fillIn.putExtra("format", getFormat());
                     try {
                         intent.send(mContext, Activity.RESULT_OK, fillIn);
                     } catch (CanceledException ex) {}
@@ -120,8 +153,10 @@ public final class GsmSMSDispatcher extends SMSDispatcher {
         SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
                 scAddr, destAddr, destPort, data, (deliveryIntent != null));
         if (pdu != null) {
-            sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent,
-                    destAddr);
+            HashMap map =  SmsTrackerMapFactory(destAddr, scAddr, destPort, data, pdu);
+            SmsTracker tracker = SmsTrackerFactory(map, sentIntent, deliveryIntent,
+                    getFormat());
+            sendRawPdu(tracker);
         } else {
             Rlog.e(TAG, "GsmSMSDispatcher.sendData(): getSubmitPdu() returned null");
         }
@@ -134,8 +169,10 @@ public final class GsmSMSDispatcher extends SMSDispatcher {
         SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
                 scAddr, destAddr, text, (deliveryIntent != null));
         if (pdu != null) {
-            sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent,
-                    destAddr);
+            HashMap map =  SmsTrackerMapFactory(destAddr, scAddr, text, pdu);
+            SmsTracker tracker = SmsTrackerFactory(map, sentIntent, deliveryIntent,
+                    getFormat());
+            sendRawPdu(tracker);
         } else {
             Rlog.e(TAG, "GsmSMSDispatcher.sendText(): getSubmitPdu() returned null");
         }
@@ -157,8 +194,11 @@ public final class GsmSMSDispatcher extends SMSDispatcher {
                 message, deliveryIntent != null, SmsHeader.toByteArray(smsHeader),
                 encoding, smsHeader.languageTable, smsHeader.languageShiftTable);
         if (pdu != null) {
-            sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent,
-                    destinationAddress);
+            HashMap map =  SmsTrackerMapFactory(destinationAddress, scAddress,
+                    message, pdu);
+            SmsTracker tracker = SmsTrackerFactory(map, sentIntent,
+                    deliveryIntent, getFormat());
+            sendRawPdu(tracker);
         } else {
             Rlog.e(TAG, "GsmSMSDispatcher.sendNewSubmitPdu(): getSubmitPdu() returned null");
         }
@@ -188,6 +228,84 @@ public final class GsmSMSDispatcher extends SMSDispatcher {
                 pdu[1] = (byte) tracker.mMessageRef; // TP-MR
             }
         }
-        mCi.sendSMS(IccUtils.bytesToHexString(smsc), IccUtils.bytesToHexString(pdu), reply);
+        Rlog.d(TAG, "sendSms: "
+                +" isIms()="+isIms()
+                +" mRetryCount="+tracker.mRetryCount
+                +" mImsRetry="+tracker.mImsRetry
+                +" mMessageRef="+tracker.mMessageRef
+                +" SS=" +mPhone.getServiceState().getState());
+
+        // sms over gsm is used:
+        //   if sms over IMS is not supported AND
+        //   this is not a retry case after sms over IMS failed
+        //     indicated by mImsRetry > 0
+        if (0 == tracker.mImsRetry && !isIms()) {
+            if (tracker.mRetryCount > 0) {
+                // per TS 23.040 Section 9.2.3.6:  If TP-MTI SMS-SUBMIT (0x01) type
+                //   TP-RD (bit 2) is 1 for retry
+                //   and TP-MR is set to previously failed sms TP-MR
+                if (((0x01 & pdu[0]) == 0x01)) {
+                    pdu[0] |= 0x04; // TP-RD
+                    pdu[1] = (byte) tracker.mMessageRef; // TP-MR
+                }
+            }
+            mCi.sendSMS(IccUtils.bytesToHexString(smsc),
+                    IccUtils.bytesToHexString(pdu), reply);
+        } else {
+            mCi.sendImsGsmSms(IccUtils.bytesToHexString(smsc),
+                    IccUtils.bytesToHexString(pdu), tracker.mImsRetry,
+                    tracker.mMessageRef, reply);
+            // increment it here, so in case of SMS_FAIL_RETRY over IMS
+            // next retry will be sent using IMS request again.
+            tracker.mImsRetry++;
+        }
+    }
+
+    @Override
+    public void sendRetrySms(SmsTracker tracker) {
+        //re-routing to ImsSMSDispatcher
+        mImsSMSDispatcher.sendRetrySms(tracker);
+    }
+
+    protected UiccCardApplication getUiccCardApplication() {
+        return mUiccController.getUiccCardApplication(UiccController.APP_FAM_3GPP);
+    }
+
+    private void onUpdateIccAvailability() {
+        if (mUiccController == null ) {
+            return;
+        }
+
+        UiccCardApplication newUiccApplication = getUiccCardApplication();
+
+        UiccCardApplication app = mUiccApplication.get();
+        if (app != newUiccApplication) {
+            if (app != null) {
+                Rlog.d(TAG, "Removing stale icc objects.");
+                if (mIccRecords.get() != null) {
+                    mIccRecords.get().unregisterForNewSms(this);
+                }
+                mIccRecords.set(null);
+                mUiccApplication.set(null);
+            }
+            if (newUiccApplication != null) {
+                Rlog.d(TAG, "New Uicc application found");
+                mUiccApplication.set(newUiccApplication);
+                mIccRecords.set(newUiccApplication.getIccRecords());
+                if (mIccRecords.get() != null) {
+                    mIccRecords.get().registerForNewSms(this, EVENT_NEW_ICC_SMS, null);
+                }
+            }
+        }
+    }
+
+    @Override
+    public boolean isIms() {
+        return mImsSMSDispatcher.isIms();
+    }
+
+    @Override
+    public String getImsSmsFormat() {
+        return mImsSMSDispatcher.getImsSmsFormat();
     }
 }
index a383d18..6d5ecd9 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2006 The Android Open Source Project
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -98,6 +99,9 @@ public class SmsMessage extends SmsMessageBase {
         } catch (RuntimeException ex) {
             Rlog.e(LOG_TAG, "SMS PDU parsing failed: ", ex);
             return null;
+        } catch (OutOfMemoryError e) {
+            Rlog.e(LOG_TAG, "SMS PDU parsing failed with out of memory: ", e);
+            return null;
         }
     }
 
index 7a92345..ae1b110 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (c) 2012-13, The Linux Foundation. All rights reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -221,6 +222,20 @@ class SipCommandInterface extends BaseCommands implements CommandsInterface {
     }
 
     @Override
+    public void sendImsGsmSms (String smscPDU, String pdu,
+            int retry, int messageRef, Message response) {
+    }
+
+    @Override
+    public void sendImsCdmaSms(byte[] pdu, int retry, int messageRef,
+            Message response) {
+    }
+
+    @Override
+    public void getImsRegistrationState (Message result) {
+    }
+
+    @Override
     public void deleteSmsOnSim(int index, Message response) {
     }
 
index 6f23fe7..c7164b7 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2006 The Android Open Source Project
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -1660,4 +1661,21 @@ public final class SimulatedCommands extends BaseCommands
     public void setInitialAttachApn(String apn, String protocol, int authType, String username,
             String password, Message result) {
     }
+
+    @Override
+    public void getImsRegistrationState(Message response) {
+        unimplemented(response);
+    }
+
+    @Override
+    public void sendImsCdmaSms(byte[] pdu, int retry, int messageRef,
+            Message response){
+        unimplemented(response);
+    }
+
+    @Override
+    public void sendImsGsmSms(String smscPDU, String pdu,
+            int retry, int messageRef, Message response){
+        unimplemented(response);
+    }
 }
index cd0fad3..d9dfe72 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -355,6 +356,16 @@ class UsimDataDownloadCommands extends BaseCommands {
     }
 
     @Override
+    public void sendImsGsmSms (String smscPDU, String pdu,
+            int retry, int messageRef, Message response) {
+    }
+
+    @Override
+    public void sendImsCdmaSms(byte[] pdu, int retry, int messageRef,
+            Message response) {
+    }
+
+    @Override
     public void deleteSmsOnSim(int index, Message response) {
     }
 
@@ -546,6 +557,10 @@ class UsimDataDownloadCommands extends BaseCommands {
     }
 
     @Override
+    public void getImsRegistrationState (Message result) {
+    }
+
+    @Override
     public void sendCDMAFeatureCode(String FeatureCode, Message response) {
     }