Merge "Added data service manager"
diff --git a/proto/telephony.proto b/proto/telephony.proto
index 700c961..7d2e84b 100644
--- a/proto/telephony.proto
+++ b/proto/telephony.proto
@@ -760,6 +760,8 @@
       DEACTIVATE_REASON_RADIO_OFF = 2;
 
       DEACTIVATE_REASON_PDP_RESET = 3;
+
+      DEACTIVATE_REASON_HANDOVER = 4;
     }
   }
 
diff --git a/src/java/com/android/internal/telephony/CommandsInterface.java b/src/java/com/android/internal/telephony/CommandsInterface.java
index ab12091..aa3ee43 100644
--- a/src/java/com/android/internal/telephony/CommandsInterface.java
+++ b/src/java/com/android/internal/telephony/CommandsInterface.java
@@ -17,6 +17,7 @@
 package com.android.internal.telephony;
 
 import android.net.KeepalivePacketData;
+import android.net.LinkProperties;
 import android.os.Handler;
 import android.os.Message;
 import android.os.WorkSource;
@@ -1625,22 +1626,27 @@
 
     /**
      * Setup a packet data connection On successful completion, the result
-     * message will return a {@link com.android.internal.telephony.dataconnection.DataCallResponse}
-     * object containing the connection information.
+     * message will return a SetupDataResult object containing the connection information.
      *
-     * @param radioTechnology
-     *            Radio technology to use. Values is one of RIL_RADIO_TECHNOLOGY_*
+     * @param accessNetworkType
+     *            Access network to use. Values is one of AccessNetworkConstants.AccessNetworkType.
      * @param dataProfile
      *            Data profile for data call setup
      * @param isRoaming
      *            Device is roaming or not
      * @param allowRoaming
      *            Flag indicating data roaming is enabled or not
+     * @param reason
+     *            The reason for data setup
+     * @param linkProperties
+     *            If the reason is for handover, this indicates the link properties of the existing
+     *            data connection
      * @param result
      *            Callback message
      */
-    void setupDataCall(int radioTechnology, DataProfile dataProfile, boolean isRoaming,
-                       boolean allowRoaming, Message result);
+    void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
+                       boolean allowRoaming, int reason, LinkProperties linkProperties,
+                       Message result);
 
     /**
      * Deactivate packet data connection
diff --git a/src/java/com/android/internal/telephony/DriverCall.java b/src/java/com/android/internal/telephony/DriverCall.java
index a923d8f..96e1556 100644
--- a/src/java/com/android/internal/telephony/DriverCall.java
+++ b/src/java/com/android/internal/telephony/DriverCall.java
@@ -37,6 +37,30 @@
         // statements that use this enum
     }
 
+    /**
+     * Audio information
+     */
+    /** Unspecified audio codec */
+    public static final int AUDIO_QUALITY_UNSPECIFIED = 0;
+    /** AMR (Narrowband) audio codec */
+    public static final int AUDIO_QUALITY_AMR = 1;
+    /** AMR (Wideband) audio codec */
+    public static final int AUDIO_QUALITY_AMR_WB = 2;
+    /** GSM Enhanced Full-Rate audio codec */
+    public static final int AUDIO_QUALITY_GSM_EFR = 3;
+    /** GSM Full-Rate audio codec */
+    public static final int AUDIO_QUALITY_GSM_FR = 4;
+    /** GSM Half-Rate audio codec */
+    public static final int AUDIO_QUALITY_GSM_HR = 5;
+    /** Enhanced Variable rate codec */
+    public static final int AUDIO_QUALITY_EVRC = 6;
+    /** Enhanced Variable rate codec revision B */
+    public static final int AUDIO_QUALITY_EVRC_B = 7;
+    /** Enhanced Variable rate codec (Wideband) */
+    public static final int AUDIO_QUALITY_EVRC_WB = 8;
+    /** Enhanced Variable rate codec (Narrowband) */
+    public static final int AUDIO_QUALITY_EVRC_NW = 9;
+
     public int index;
     public boolean isMT;
     public State state;     // May be null if unavail
@@ -50,6 +74,7 @@
     public String name;
     public int namePresentation;
     public UUSInfo uusInfo;
+    public int audioQuality = AUDIO_QUALITY_UNSPECIFIED;
 
     /** returns null on error */
     static DriverCall
@@ -113,7 +138,8 @@
                 + (isVoice ? "voc" : "nonvoc") + ","
                 + (isVoicePrivacy ? "evp" : "noevp") + ","
                 /*+ "number=" + number */ + ",cli=" + numberPresentation + ","
-                /*+ "name="+ name */ + "," + namePresentation;
+                /*+ "name="+ name */ + "," + namePresentation + ","
+                + "audioQuality=" + audioQuality;
     }
 
     public static State
diff --git a/src/java/com/android/internal/telephony/GsmCdmaConnection.java b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
index a8d79cf..42157f1 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaConnection.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaConnection.java
@@ -141,6 +141,8 @@
         mParent.attach(this, dc);
 
         fetchDtmfToneDelay(phone);
+
+        setAudioQuality(getAudioQualityFromDC(dc.audioQuality));
     }
 
     /** This is an MO call, created when dialing */
@@ -635,6 +637,17 @@
             }
         }
 
+        int newAudioQuality = getAudioQualityFromDC(dc.audioQuality);
+        if (getAudioQuality() != newAudioQuality) {
+            if (Phone.DEBUG_PHONE) {
+                log("update: audioQuality # changed!:  "
+                        + (newAudioQuality == Connection.AUDIO_QUALITY_HIGH_DEFINITION
+                        ? "high" : "standard"));
+            }
+            setAudioQuality(newAudioQuality);
+            changed = true;
+        }
+
         // A null cnapName should be the same as ""
         if (TextUtils.isEmpty(dc.name)) {
             if (!TextUtils.isEmpty(mCnapName)) {
@@ -927,6 +940,16 @@
         }
     }
 
+    private int getAudioQualityFromDC(int audioQuality) {
+        switch (audioQuality) {
+            case DriverCall.AUDIO_QUALITY_AMR_WB:
+            case DriverCall.AUDIO_QUALITY_EVRC_NW:
+                return Connection.AUDIO_QUALITY_HIGH_DEFINITION;
+            default:
+                return Connection.AUDIO_QUALITY_STANDARD;
+        }
+    }
+
     /**
      * Set post dial state and acquire wake lock while switching to "started" or "pause"
      * state, the wake lock will be released if state switches out of "started" or "pause"
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index fc3caa1..0c51dca 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -1767,6 +1767,57 @@
     }
 
     @Override
+    public void getCallBarring(String facility, String password, Message onComplete,
+            int serviceClass) {
+        if (isPhoneTypeGsm()) {
+            Phone imsPhone = mImsPhone;
+            if ((imsPhone != null)
+                    && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)
+                    || imsPhone.isUtEnabled())) {
+                imsPhone.getCallBarring(facility, password, onComplete, serviceClass);
+                return;
+            }
+            mCi.queryFacilityLock(facility, password, serviceClass, onComplete);
+        } else {
+            loge("getCallBarringOption: not possible in CDMA");
+        }
+    }
+
+    @Override
+    public void setCallBarring(String facility, boolean lockState, String password,
+            Message onComplete, int serviceClass) {
+        if (isPhoneTypeGsm()) {
+            Phone imsPhone = mImsPhone;
+            if ((imsPhone != null)
+                    && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)
+                    || imsPhone.isUtEnabled())) {
+                imsPhone.setCallBarring(facility, lockState, password, onComplete, serviceClass);
+                return;
+            }
+            mCi.setFacilityLock(facility, lockState, password, serviceClass, onComplete);
+        } else {
+            loge("setCallBarringOption: not possible in CDMA");
+        }
+    }
+
+    /**
+     * Changes access code used for call barring
+     *
+     * @param facility is one of CB_FACILTY_*
+     * @param oldPwd is old password
+     * @param newPwd is new password
+     * @param onComplete is callback message when the action is completed.
+     */
+    public void changeCallBarringPassword(String facility, String oldPwd, String newPwd,
+            Message onComplete) {
+        if (isPhoneTypeGsm()) {
+            mCi.changeBarringPassword(facility, oldPwd, newPwd, onComplete);
+        } else {
+            loge("changeCallBarringPassword: not possible in CDMA");
+        }
+    }
+
+    @Override
     public void getOutgoingCallerIdDisplay(Message onComplete) {
         if (isPhoneTypeGsm()) {
             Phone imsPhone = mImsPhone;
diff --git a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
index 0fc08c6..c3ae1b0 100644
--- a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
@@ -83,7 +83,7 @@
     final protected Context mContext;
     final protected AppOpsManager mAppOps;
     final private UserManager mUserManager;
-    protected SMSDispatcher mDispatcher;
+    protected SmsDispatchersController mDispatchersController;
 
     protected Handler mHandler = new Handler() {
         @Override
@@ -131,7 +131,7 @@
         mContext = phone.getContext();
         mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-        mDispatcher = new ImsSMSDispatcher(phone,
+        mDispatchersController = new SmsDispatchersController(phone,
                 phone.mSmsStorageMonitor, phone.mSmsUsageMonitor);
     }
 
@@ -170,7 +170,7 @@
 
     protected void updatePhoneObject(Phone phone) {
         mPhone = phone;
-        mDispatcher.updatePhoneObject(phone);
+        mDispatchersController.updatePhoneObject(phone);
     }
 
     protected void enforceReceiveAndSend(String message) {
@@ -379,7 +379,8 @@
             return;
         }
         destAddr = filterDestAddress(destAddr);
-        mDispatcher.sendData(destAddr, scAddr, destPort, data, sentIntent, deliveryIntent);
+        mDispatchersController.sendData(destAddr, scAddr, destPort, data, sentIntent,
+                deliveryIntent);
     }
 
     /**
@@ -451,7 +452,7 @@
             enforcePrivilegedAppPermissions();
         }
         destAddr = filterDestAddress(destAddr);
-        mDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
+        mDispatchersController.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
                 null/*messageUri*/, callingPackage, persistMessageForNonDefaultSmsApp);
     }
 
@@ -472,7 +473,17 @@
                 "\n format=" + format +
                 "\n receivedIntent=" + receivedIntent);
         }
-        mDispatcher.injectSmsPdu(pdu, format, receivedIntent);
+        mDispatchersController.injectSmsPdu(pdu, format,
+                result -> {
+                    if (receivedIntent != null) {
+                        try {
+                            receivedIntent.send(result);
+                        } catch (PendingIntent.CanceledException e) {
+                            Rlog.d(LOG_TAG, "receivedIntent cancelled.");
+                        }
+                    }
+                }
+        );
     }
 
     /**
@@ -546,7 +557,7 @@
                     singleDeliveryIntent = deliveryIntents.get(i);
                 }
 
-                mDispatcher.sendText(destAddr, scAddr, singlePart,
+                mDispatchersController.sendText(destAddr, scAddr, singlePart,
                         singleSentIntent, singleDeliveryIntent,
                         null/*messageUri*/, callingPackage,
                         persistMessageForNonDefaultSmsApp);
@@ -554,19 +565,19 @@
             return;
         }
 
-        mDispatcher.sendMultipartText(destAddr, scAddr, (ArrayList<String>) parts,
+        mDispatchersController.sendMultipartText(destAddr, scAddr, (ArrayList<String>) parts,
                 (ArrayList<PendingIntent>) sentIntents, (ArrayList<PendingIntent>) deliveryIntents,
                 null/*messageUri*/, callingPackage, persistMessageForNonDefaultSmsApp);
     }
 
 
     public int getPremiumSmsPermission(String packageName) {
-        return mDispatcher.getPremiumSmsPermission(packageName);
+        return mDispatchersController.getPremiumSmsPermission(packageName);
     }
 
 
     public void setPremiumSmsPermission(String packageName, int permission) {
-        mDispatcher.setPremiumSmsPermission(packageName, permission);
+        mDispatchersController.setPremiumSmsPermission(packageName, permission);
     }
 
     /**
@@ -919,11 +930,11 @@
     }
 
     public boolean isImsSmsSupported() {
-        return mDispatcher.isIms();
+        return mDispatchersController.isIms();
     }
 
     public String getImsSmsFormat() {
-        return mDispatcher.getImsSmsFormat();
+        return mDispatchersController.getImsSmsFormat();
     }
 
     public void sendStoredText(String callingPkg, Uri messageUri, String scAddress,
@@ -951,7 +962,7 @@
             return;
         }
         textAndAddress[1] = filterDestAddress(textAndAddress[1]);
-        mDispatcher.sendText(textAndAddress[1], scAddress, textAndAddress[0],
+        mDispatchersController.sendText(textAndAddress[1], scAddress, textAndAddress[0],
                 sentIntent, deliveryIntent, messageUri, callingPkg,
                 true /* persistMessageForNonDefaultSmsApp */);
     }
@@ -1007,14 +1018,14 @@
                     singleDeliveryIntent = deliveryIntents.get(i);
                 }
 
-                mDispatcher.sendText(textAndAddress[1], scAddress, singlePart,
+                mDispatchersController.sendText(textAndAddress[1], scAddress, singlePart,
                         singleSentIntent, singleDeliveryIntent, messageUri, callingPkg,
                         true  /* persistMessageForNonDefaultSmsApp */);
             }
             return;
         }
 
-        mDispatcher.sendMultipartText(
+        mDispatchersController.sendMultipartText(
                 textAndAddress[1], // destAddress
                 scAddress,
                 parts,
diff --git a/src/java/com/android/internal/telephony/ImsSMSDispatcher.java b/src/java/com/android/internal/telephony/ImsSMSDispatcher.java
deleted file mode 100644
index 4d8f62c..0000000
--- a/src/java/com/android/internal/telephony/ImsSMSDispatcher.java
+++ /dev/null
@@ -1,408 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony;
-
-import static android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE;
-import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
-import android.net.Uri;
-import android.os.AsyncResult;
-import android.os.Message;
-import android.provider.Telephony.Sms.Intents;
-import android.telephony.Rlog;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.cdma.CdmaInboundSmsHandler;
-import com.android.internal.telephony.cdma.CdmaSMSDispatcher;
-import com.android.internal.telephony.gsm.GsmInboundSmsHandler;
-import com.android.internal.telephony.gsm.GsmSMSDispatcher;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-
-public class ImsSMSDispatcher extends SMSDispatcher {
-    private static final String TAG = "RIL_ImsSms";
-
-    private SMSDispatcher mCdmaDispatcher;
-    private SMSDispatcher mGsmDispatcher;
-
-    private GsmInboundSmsHandler mGsmInboundSmsHandler;
-    private 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(Phone phone, SmsStorageMonitor storageMonitor,
-            SmsUsageMonitor usageMonitor) {
-        super(phone, usageMonitor, null);
-        Rlog.d(TAG, "ImsSMSDispatcher created");
-
-        // Create dispatchers, inbound SMS handlers and
-        // broadcast undelivered messages in raw table.
-        mCdmaDispatcher = new CdmaSMSDispatcher(phone, usageMonitor, this);
-        mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(phone.getContext(),
-                storageMonitor, phone);
-        mCdmaInboundSmsHandler = CdmaInboundSmsHandler.makeInboundSmsHandler(phone.getContext(),
-                storageMonitor, phone, (CdmaSMSDispatcher) mCdmaDispatcher);
-        mGsmDispatcher = new GsmSMSDispatcher(phone, usageMonitor, this, mGsmInboundSmsHandler);
-        SmsBroadcastUndelivered.initialize(phone.getContext(),
-            mGsmInboundSmsHandler, mCdmaInboundSmsHandler);
-        InboundSmsHandler.registerNewMessageNotificationActionHandler(phone.getContext());
-
-        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(Phone phone) {
-        Rlog.d(TAG, "In IMS updatePhoneObject ");
-        super.updatePhoneObject(phone);
-        mCdmaDispatcher.updatePhoneObject(phone);
-        mGsmDispatcher.updatePhoneObject(phone);
-        mGsmInboundSmsHandler.updatePhoneObject(phone);
-        mCdmaInboundSmsHandler.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
-    public 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
-    public void sendMultipartText(String destAddr, String scAddr,
-            ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
-            ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg,
-            boolean persistMessage) {
-        if (isCdmaMo()) {
-            mCdmaDispatcher.sendMultipartText(destAddr, scAddr,
-                    parts, sentIntents, deliveryIntents, messageUri, callingPkg, persistMessage);
-        } else {
-            mGsmDispatcher.sendMultipartText(destAddr, scAddr,
-                    parts, sentIntents, deliveryIntents, messageUri, callingPkg, persistMessage);
-        }
-    }
-
-    @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 sendSmsByPstn(SmsTracker tracker) {
-        // This function should be defined in Gsm/CdmaDispatcher.
-        Rlog.e(TAG, "sendSmsByPstn should never be called from here!");
-    }
-
-    @Override
-    public void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent,
-            PendingIntent deliveryIntent, Uri messageUri, String callingPkg,
-            boolean persistMessage) {
-        Rlog.d(TAG, "sendText");
-        if (isCdmaMo()) {
-            mCdmaDispatcher.sendText(destAddr, scAddr,
-                    text, sentIntent, deliveryIntent, messageUri, callingPkg, persistMessage);
-        } else {
-            mGsmDispatcher.sendText(destAddr, scAddr,
-                    text, sentIntent, deliveryIntent, messageUri, callingPkg, persistMessage);
-        }
-    }
-
-    @VisibleForTesting
-    @Override
-    public void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) {
-        Rlog.d(TAG, "ImsSMSDispatcher:injectSmsPdu");
-        try {
-            // TODO We need to decide whether we should allow injecting GSM(3gpp)
-            // SMS pdus when the phone is camping on CDMA(3gpp2) network and vice versa.
-            android.telephony.SmsMessage msg =
-                    android.telephony.SmsMessage.createFromPdu(pdu, format);
-
-            // Only class 1 SMS are allowed to be injected.
-            if (msg == null ||
-                    msg.getMessageClass() != android.telephony.SmsMessage.MessageClass.CLASS_1) {
-                if (msg == null) {
-                    Rlog.e(TAG, "injectSmsPdu: createFromPdu returned null");
-                }
-                if (receivedIntent != null) {
-                    receivedIntent.send(Intents.RESULT_SMS_GENERIC_ERROR);
-                }
-                return;
-            }
-
-            AsyncResult ar = new AsyncResult(receivedIntent, msg, null);
-
-            if (format.equals(SmsConstants.FORMAT_3GPP)) {
-                Rlog.i(TAG, "ImsSMSDispatcher:injectSmsText Sending msg=" + msg +
-                        ", format=" + format + "to mGsmInboundSmsHandler");
-                mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_INJECT_SMS, ar);
-            } else if (format.equals(SmsConstants.FORMAT_3GPP2)) {
-                Rlog.i(TAG, "ImsSMSDispatcher:injectSmsText Sending msg=" + msg +
-                        ", format=" + format + "to mCdmaInboundSmsHandler");
-                mCdmaInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_INJECT_SMS, ar);
-            } else {
-                // Invalid pdu format.
-                Rlog.e(TAG, "Invalid pdu format: " + format);
-                if (receivedIntent != null)
-                    receivedIntent.send(Intents.RESULT_SMS_GENERIC_ERROR);
-            }
-        } catch (Exception e) {
-            Rlog.e(TAG, "injectSmsPdu failed: ", e);
-            try {
-                if (receivedIntent != null)
-                    receivedIntent.send(Intents.RESULT_SMS_GENERIC_ERROR);
-            } catch (CanceledException ex) {}
-        }
-    }
-
-    @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.getData();
-
-        // 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!");
-            tracker.onFailed(mContext, RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/);
-            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 void sendSubmitPdu(SmsTracker tracker) {
-        sendRawPdu(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 SmsTracker getNewSubmitPduTracker(String destinationAddress, String scAddress,
-            String message, SmsHeader smsHeader, int format, PendingIntent sentIntent,
-            PendingIntent deliveryIntent, boolean lastPart,
-            AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
-            String fullMessageText) {
-        Rlog.e(TAG, "Error! Not implemented for IMS.");
-        return null;
-    }
-
-    @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));
-    }
-}
diff --git a/src/java/com/android/internal/telephony/ImsSmsDispatcher.java b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
new file mode 100644
index 0000000..ac629f8
--- /dev/null
+++ b/src/java/com/android/internal/telephony/ImsSmsDispatcher.java
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.internal.telephony;
+
+import android.app.Activity;
+import android.os.RemoteException;
+import android.os.Message;
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.provider.Telephony.Sms;
+import android.content.Intent;
+import android.telephony.Rlog;
+
+import com.android.ims.ImsException;
+import com.android.ims.ImsManager;
+import com.android.ims.ImsServiceProxy;
+import com.android.ims.internal.IImsSmsListener;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
+import com.android.internal.telephony.util.SMSDispatcherUtil;
+import com.android.internal.telephony.gsm.SmsMessage;
+
+import android.telephony.ims.internal.feature.ImsFeature;
+import android.telephony.ims.internal.feature.MmTelFeature;
+import android.telephony.ims.internal.stub.SmsImplBase;
+import android.telephony.ims.internal.stub.SmsImplBase.SendStatusResult;
+import android.telephony.ims.internal.stub.SmsImplBase.StatusReportResult;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
+import android.provider.Telephony.Sms.Intents;
+import android.util.Pair;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Responsible for communications with {@link com.android.ims.ImsManager} to send/receive messages
+ * over IMS.
+ */
+public class ImsSmsDispatcher extends SMSDispatcher {
+    // Initial condition for ims connection retry.
+    private static final int IMS_RETRY_STARTING_TIMEOUT_MS = 500; // ms
+    // Ceiling bitshift amount for service query timeout, calculated as:
+    // 2^mImsServiceRetryCount * IMS_RETRY_STARTING_TIMEOUT_MS, where
+    // mImsServiceRetryCount ∊ [0, CEILING_SERVICE_RETRY_COUNT].
+    private static final int CEILING_SERVICE_RETRY_COUNT = 6;
+
+    @VisibleForTesting
+    public Map<Integer, SmsTracker> mTrackers = new ConcurrentHashMap<>();
+    @VisibleForTesting
+    public AtomicInteger mNextToken = new AtomicInteger();
+    private final Object mLock = new Object();
+    private volatile boolean mIsSmsCapable;
+    private volatile boolean mIsImsServiceUp;
+    private volatile boolean mIsRegistered;
+    private volatile int mImsServiceRetryCount;
+
+    /**
+     * Default implementation of interface that calculates the ImsService retry timeout.
+     * Override-able for testing.
+     */
+    private IRetryTimeout mRetryTimeout = () -> {
+        int timeout = (1 << mImsServiceRetryCount) * IMS_RETRY_STARTING_TIMEOUT_MS;
+        if (mImsServiceRetryCount <= CEILING_SERVICE_RETRY_COUNT) {
+            mImsServiceRetryCount++;
+        }
+        return timeout;
+    };
+
+    /**
+     * Listen to the IMS service state change
+     *
+     */
+    private ImsRegistrationImplBase.Callback mRegistrationCallback =
+            new ImsRegistrationImplBase.Callback() {
+                @Override
+                public void onRegistered(
+                        @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
+                    Rlog.d(TAG, "onImsConnected imsRadioTech=" + imsRadioTech);
+                    synchronized (mLock) {
+                        mIsRegistered = true;
+                    }
+                }
+
+                @Override
+                public void onRegistering(
+                        @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
+                    Rlog.d(TAG, "onImsProgressing imsRadioTech=" + imsRadioTech);
+                    synchronized (mLock) {
+                        mIsRegistered = false;
+                    }
+                }
+
+                @Override
+                public void onDeregistered(com.android.ims.ImsReasonInfo info) {
+                    Rlog.d(TAG, "onImsDisconnected imsReasonInfo=" + info);
+                    synchronized (mLock) {
+                        mIsRegistered = false;
+                    }
+                }
+            };
+
+    private ImsFeature.CapabilityCallback mCapabilityCallback =
+            new ImsFeature.CapabilityCallback() {
+                @Override
+                public void onCapabilitiesStatusChanged(ImsFeature.Capabilities config) {
+                    synchronized (mLock) {
+                        mIsSmsCapable = config.isCapable(
+                                MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS);
+                    }
+                }
+    };
+
+    // Callback fires when ImsManager MMTel Feature changes state
+    private ImsServiceProxy.IFeatureUpdate mNotifyStatusChangedCallback =
+            new ImsServiceProxy.IFeatureUpdate() {
+                @Override
+                public void notifyStateChanged() {
+                    try {
+                        int status = getImsManager().getImsServiceStatus();
+                        Rlog.d(TAG, "Status Changed: " + status);
+                        switch (status) {
+                            case android.telephony.ims.feature.ImsFeature.STATE_READY: {
+                                synchronized (mLock) {
+                                    setListeners();
+                                    mIsImsServiceUp = true;
+                                }
+                                break;
+                            }
+                            case android.telephony.ims.feature.ImsFeature.STATE_INITIALIZING:
+                                // fall through
+                            case android.telephony.ims.feature.ImsFeature.STATE_NOT_AVAILABLE:
+                                synchronized (mLock) {
+                                    mIsImsServiceUp = false;
+                                }
+                                break;
+                            default: {
+                                Rlog.w(TAG, "Unexpected State!");
+                            }
+                        }
+                    } catch (ImsException e) {
+                        // Could not get the ImsService, retry!
+                        retryGetImsService();
+                    }
+                }
+
+                @Override
+                public void notifyUnavailable() {
+                    retryGetImsService();
+                }
+            };
+
+    private final IImsSmsListener mImsSmsListener = new IImsSmsListener.Stub() {
+        @Override
+        public void onSendSmsResult(int token, int messageRef, @SendStatusResult int status,
+                int reason) throws RemoteException {
+            SmsTracker tracker = mTrackers.get(token);
+            if (tracker == null) {
+                throw new IllegalArgumentException("Invalid token.");
+            }
+            switch(reason) {
+                case SmsImplBase.SEND_STATUS_OK:
+                    tracker.onSent(mContext);
+                    break;
+                case SmsImplBase.SEND_STATUS_ERROR:
+                    tracker.onFailed(mContext, reason, 0 /* errorCode */);
+                    mTrackers.remove(token);
+                    break;
+                case SmsImplBase.SEND_STATUS_ERROR_RETRY:
+                    tracker.mRetryCount += 1;
+                    sendSms(tracker);
+                    break;
+                case SmsImplBase.SEND_STATUS_ERROR_FALLBACK:
+                    fallbackToPstn(token, tracker);
+                    break;
+                default:
+            }
+        }
+
+        @Override
+        public void onSmsStatusReportReceived(int token, int messageRef, String format, byte[] pdu)
+                throws RemoteException {
+            Rlog.d(TAG, "Status report received.");
+            SmsTracker tracker = mTrackers.get(token);
+            if (tracker == null) {
+                throw new RemoteException("Invalid token.");
+            }
+            Pair<Boolean, Boolean> result = mSmsDispatchersController.handleSmsStatusReport(
+                    tracker, format, pdu);
+            Rlog.d(TAG, "Status report handle result, success: " + result.first +
+                    "complete: " + result.second);
+            try {
+                getImsManager().acknowledgeSmsReport(
+                        token,
+                        messageRef,
+                        result.first ? SmsImplBase.STATUS_REPORT_STATUS_OK
+                                : SmsImplBase.STATUS_REPORT_STATUS_ERROR);
+            } catch (ImsException e) {
+                Rlog.e(TAG, "Failed to acknowledgeSmsReport(). Error: "
+                        + e.getMessage());
+            }
+            if (result.second) {
+                mTrackers.remove(token);
+            }
+        }
+
+        @Override
+        public void onSmsReceived(int token, String format, byte[] pdu)
+                throws RemoteException {
+            Rlog.d(TAG, "SMS received.");
+            mSmsDispatchersController.injectSmsPdu(pdu, format, result -> {
+                Rlog.d(TAG, "SMS handled result: " + result);
+                try {
+                    getImsManager().acknowledgeSms(token,
+                            0,
+                            result == Intents.RESULT_SMS_HANDLED
+                                    ? SmsImplBase.STATUS_REPORT_STATUS_OK
+                                    : SmsImplBase.DELIVER_STATUS_ERROR);
+                } catch (ImsException e) {
+                    Rlog.e(TAG, "Failed to acknowledgeSms(). Error: " + e.getMessage());
+                }
+            });
+        }
+    };
+
+    public ImsSmsDispatcher(Phone phone, SmsDispatchersController smsDispatchersController) {
+        super(phone, smsDispatchersController);
+
+        mImsServiceRetryCount = 0;
+        // Send a message to connect to the Ims Service and open a connection through
+        // getImsService().
+        sendEmptyMessage(EVENT_GET_IMS_SERVICE);
+    }
+
+    @Override
+    public void handleMessage(Message msg) {
+        switch (msg.what) {
+            case EVENT_GET_IMS_SERVICE:
+                try {
+                    getImsService();
+                } catch (ImsException e) {
+                    Rlog.e(TAG, "setListeners: " + e);
+                    retryGetImsService();
+                }
+                break;
+            default:
+                super.handleMessage(msg);
+        }
+    }
+
+    private void getImsService() throws ImsException {
+        Rlog.d(TAG, "getImsService");
+        // Adding to set, will be safe adding multiple times. If the ImsService is not active yet,
+        // this method will throw an ImsException.
+        getImsManager().addNotifyStatusChangedCallbackIfAvailable(mNotifyStatusChangedCallback);
+        // Wait for ImsService.STATE_READY to start listening for SMS.
+        // Call the callback right away for compatibility with older devices that do not use states.
+        mNotifyStatusChangedCallback.notifyStateChanged();
+    }
+
+    private void setListeners() throws ImsException {
+        getImsManager().addRegistrationCallback(mRegistrationCallback);
+        getImsManager().addCapabilitiesCallback(mCapabilityCallback);
+        getImsManager().setSmsListener(mImsSmsListener);
+        getImsManager().onSmsReady();
+        mImsServiceRetryCount = 0;
+    }
+
+    private void retryGetImsService() {
+        // The binder connection is already up. Do not try to get it again.
+        if (getImsManager().isServiceAvailable()) {
+            return;
+        }
+        // remove callback so we do not receive updates from old ImsServiceProxy when switching
+        // between ImsServices.
+        getImsManager().removeNotifyStatusChangedCallback(mNotifyStatusChangedCallback);
+        // Exponential backoff during retry, limited to 32 seconds.
+        Rlog.e(TAG, "getImsService: Retrying getting ImsService...");
+        removeMessages(EVENT_GET_IMS_SERVICE);
+        sendEmptyMessageDelayed(EVENT_GET_IMS_SERVICE, mRetryTimeout.get());
+    }
+
+    public boolean isAvailable() {
+        synchronized (mLock) {
+            return mIsImsServiceUp && mIsRegistered && mIsSmsCapable;
+        }
+    }
+
+    @Override
+    protected String getFormat() {
+        try {
+            return getImsManager().getSmsFormat();
+        } catch (ImsException e) {
+            Rlog.e(TAG, "Failed to get sms format. Error: " + e.getMessage());
+            return SmsConstants.FORMAT_UNKNOWN;
+        }
+    }
+
+    @Override
+    protected boolean shouldBlockSms() {
+        return SMSDispatcherUtil.shouldBlockSms(isCdmaMo(), mPhone);
+    }
+
+    @Override
+    protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+            String message, boolean statusReportRequested, SmsHeader smsHeader) {
+        return SMSDispatcherUtil.getSubmitPdu(isCdmaMo(), scAddr, destAddr, message,
+                statusReportRequested, smsHeader);
+    }
+
+    @Override
+    protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+            int destPort, byte[] message, boolean statusReportRequested) {
+        return SMSDispatcherUtil.getSubmitPdu(isCdmaMo(), scAddr, destAddr, destPort, message,
+                statusReportRequested);
+    }
+
+    @Override
+    protected TextEncodingDetails calculateLength(CharSequence messageBody, boolean use7bitOnly) {
+        return SMSDispatcherUtil.calculateLength(isCdmaMo(), messageBody, use7bitOnly);
+    }
+
+    @Override
+    public void sendSms(SmsTracker tracker) {
+        Rlog.d(TAG, "sendSms: "
+                + " mRetryCount=" + tracker.mRetryCount
+                + " mMessageRef=" + tracker.mMessageRef
+                + " SS=" + mPhone.getServiceState().getState());
+
+        HashMap<String, Object> map = tracker.getData();
+
+        byte[] pdu = (byte[]) map.get(MAP_KEY_PDU);
+        byte smsc[] = (byte[]) map.get(MAP_KEY_SMSC);
+        boolean isRetry = tracker.mRetryCount > 0;
+
+        if (SmsConstants.FORMAT_3GPP.equals(getFormat()) && 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
+            }
+        }
+
+        int token = mNextToken.incrementAndGet();
+        mTrackers.put(token, tracker);
+        try {
+            getImsManager().sendSms(
+                    token,
+                    tracker.mMessageRef,
+                    getFormat(),
+                    smsc != null ? new String(smsc) : null,
+                    isRetry,
+                    pdu);
+        } catch (ImsException e) {
+            Rlog.e(TAG, "sendSms failed. Falling back to PSTN. Error: " + e.getMessage());
+            fallbackToPstn(token, tracker);
+        }
+    }
+
+    private ImsManager getImsManager() {
+        return ImsManager.getInstance(mContext, mPhone.getPhoneId());
+    }
+
+    @VisibleForTesting
+    public void fallbackToPstn(int token, SmsTracker tracker) {
+        mSmsDispatchersController.sendRetrySms(tracker);
+        mTrackers.remove(token);
+    }
+
+    @Override
+    protected boolean isCdmaMo() {
+        return mSmsDispatchersController.isCdmaFormat(getFormat());
+    }
+
+    @VisibleForTesting
+    public interface IRetryTimeout {
+        int get();
+    }
+}
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index 2d663cd..cb87032 100644
--- a/src/java/com/android/internal/telephony/InboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -603,9 +603,9 @@
      */
     private void handleInjectSms(AsyncResult ar) {
         int result;
-        PendingIntent receivedIntent = null;
+        SmsDispatchersController.SmsInjectionCallback callback = null;
         try {
-            receivedIntent = (PendingIntent) ar.userObj;
+            callback = (SmsDispatchersController.SmsInjectionCallback) ar.userObj;
             SmsMessage sms = (SmsMessage) ar.result;
             if (sms == null) {
               result = Intents.RESULT_SMS_GENERIC_ERROR;
@@ -617,10 +617,8 @@
             result = Intents.RESULT_SMS_GENERIC_ERROR;
         }
 
-        if (receivedIntent != null) {
-            try {
-                receivedIntent.send(result);
-            } catch (CanceledException e) { }
+        if (callback != null) {
+            callback.onSmsInjectedResult(result);
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/PhoneInternalInterface.java b/src/java/com/android/internal/telephony/PhoneInternalInterface.java
index 46a9e8c..c64c090 100644
--- a/src/java/com/android/internal/telephony/PhoneInternalInterface.java
+++ b/src/java/com/android/internal/telephony/PhoneInternalInterface.java
@@ -592,6 +592,35 @@
                                  Message onComplete);
 
     /**
+     * Gets a call barring option. The return value of ((AsyncResult) onComplete.obj) will be an
+     * Integer representing the sum of enabled serivice classes (sum of SERVICE_CLASS_*)
+     *
+     * @param facility is one of CB_FACILTY_*
+     * @param password is password or "" if not required
+     * @param serviceClass is a sum of SERVICE_CLASS_*
+     * @param onComplete is callback message when the action is completed.
+     */
+    public void getCallBarring(String facility,
+            String password,
+            Message onComplete,
+            int serviceClass);
+
+    /**
+     * Sets a call barring option.
+     *
+     * @param facility is one of CB_FACILTY_*
+     * @param lockState is true means lock, false means unlock
+     * @param password is password or "" if not required
+     * @param serviceClass is a sum of SERVICE_CLASS_*
+     * @param onComplete is callback message when the action is completed.
+     */
+    public void setCallBarring(String facility,
+            boolean lockState,
+            String password,
+            Message onComplete,
+            int serviceClass);
+
+    /**
      * getOutgoingCallerIdDisplay
      * gets outgoing caller id display. The return value of
      * ((AsyncResult)onComplete.obj) is an array of int, with a length of 2.
diff --git a/src/java/com/android/internal/telephony/PhoneSwitcher.java b/src/java/com/android/internal/telephony/PhoneSwitcher.java
index 1c6e023..cd28b2b 100644
--- a/src/java/com/android/internal/telephony/PhoneSwitcher.java
+++ b/src/java/com/android/internal/telephony/PhoneSwitcher.java
@@ -124,7 +124,8 @@
         mCommandsInterfaces = cis;
 
         try {
-            tr.addOnSubscriptionsChangedListener("PhoneSwitcher", mSubscriptionsChangedListener);
+            tr.addOnSubscriptionsChangedListener(context.getOpPackageName(),
+                    mSubscriptionsChangedListener);
         } catch (RemoteException e) {
         }
 
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index eefcdc8..ce4743f 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -55,6 +55,7 @@
 import android.net.ConnectivityManager;
 import android.net.KeepalivePacketData;
 import android.net.LinkAddress;
+import android.net.LinkProperties;
 import android.net.NetworkUtils;
 import android.os.AsyncResult;
 import android.os.Build;
@@ -81,12 +82,14 @@
 import android.telephony.RadioAccessFamily;
 import android.telephony.RadioAccessSpecifier;
 import android.telephony.Rlog;
+import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.SmsManager;
 import android.telephony.TelephonyHistogram;
 import android.telephony.TelephonyManager;
 import android.telephony.data.DataCallResponse;
 import android.telephony.data.DataProfile;
+import android.telephony.data.DataService;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.SparseArray;
@@ -1225,8 +1228,9 @@
     }
 
     @Override
-    public void setupDataCall(int radioTechnology, DataProfile dataProfile, boolean isRoaming,
-                              boolean allowRoaming, Message result) {
+    public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
+                              boolean allowRoaming, int reason, LinkProperties linkProperties,
+                              Message result) {
 
         IRadio radioProxy = getRadioProxy(result);
         if (radioProxy != null) {
@@ -1237,17 +1241,49 @@
             // Convert to HAL data profile
             DataProfileInfo dpi = convertToHalDataProfile(dataProfile);
 
-            if (RILJ_LOGD) {
-                riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
-                        + ",radioTechnology=" + radioTechnology + ",isRoaming="
-                        + isRoaming + ",allowRoaming=" + allowRoaming + "," + dataProfile);
-            }
-
+            android.hardware.radio.V1_2.IRadio radioProxy12 =
+                    android.hardware.radio.V1_2.IRadio.castFrom(radioProxy);
             try {
-                radioProxy.setupDataCall(rr.mSerial, radioTechnology, dpi,
-                        dataProfile.isModemCognitive(), allowRoaming, isRoaming);
-                mMetrics.writeRilSetupDataCall(mPhoneId, rr.mSerial, radioTechnology, dpi.profileId,
-                        dpi.apn, dpi.authType, dpi.protocol);
+                if (radioProxy12 == null) {
+                    // IRadio V1.0
+                    if (RILJ_LOGD) {
+                        riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                                + ",radioTechnology=unknown,isRoaming=" + isRoaming
+                                + ",allowRoaming=" + allowRoaming + "," + dataProfile);
+                    }
+                    // The RAT field in setup data call request was never used before. Starting from
+                    // P, the new API passes in access network type instead of RAT. Since it's
+                    // not possible to convert access network type back to RAT, but we still need to
+                    // support the 1.0 API, we passed in unknown RAT to the modem. And modem must
+                    // setup the data call on its current camped network.
+                    radioProxy.setupDataCall(rr.mSerial, ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN,
+                            dpi, dataProfile.isModemCognitive(), allowRoaming, isRoaming);
+                } else {
+                    // IRadio V1.2
+                    ArrayList<String> addresses = null;
+                    ArrayList<String> dnses = null;
+                    if (linkProperties != null) {
+                        addresses = new ArrayList<>();
+                        for (InetAddress address : linkProperties.getAddresses()) {
+                            addresses.add(address.getHostAddress());
+                        }
+                        dnses = new ArrayList<>();
+                        for (InetAddress dns : linkProperties.getDnsServers()) {
+                            dnses.add(dns.getHostAddress());
+                        }
+                    }
+
+                    if (RILJ_LOGD) {
+                        riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
+                                + ",accessNetworkType=" + accessNetworkType + ",isRoaming="
+                                + isRoaming + ",allowRoaming=" + allowRoaming + "," + dataProfile
+                                + ",addresses=" + addresses + ",dnses=" + dnses);
+                    }
+
+                    radioProxy12.setupDataCall_1_2(rr.mSerial, accessNetworkType, dpi,
+                            dataProfile.isModemCognitive(), allowRoaming, isRoaming, reason,
+                            addresses, dnses);
+                }
             } catch (RemoteException | RuntimeException e) {
                 handleRadioProxyExceptionForRR(rr, "setupDataCall", e);
             }
@@ -1531,10 +1567,17 @@
                         + requestToString(rr.mRequest) + " cid = " + cid + " reason = " + reason);
             }
 
+            android.hardware.radio.V1_2.IRadio radioProxy12 =
+                    android.hardware.radio.V1_2.IRadio.castFrom(radioProxy);
+
             try {
-                radioProxy.deactivateDataCall(rr.mSerial, cid, (reason == 0) ? false : true);
-                mMetrics.writeRilDeactivateDataCall(mPhoneId, rr.mSerial,
-                        cid, reason);
+                if (radioProxy12 == null) {
+                    radioProxy.deactivateDataCall(rr.mSerial, cid,
+                            (reason == DataService.REQUEST_REASON_SHUTDOWN));
+                } else {
+                    radioProxy12.deactivateDataCall_1_2(rr.mSerial, cid, reason);
+                }
+                mMetrics.writeRilDeactivateDataCall(mPhoneId, rr.mSerial, cid, reason);
             } catch (RemoteException | RuntimeException e) {
                 handleRadioProxyExceptionForRR(rr, "deactivateDataCall", e);
             }
diff --git a/src/java/com/android/internal/telephony/RadioResponse.java b/src/java/com/android/internal/telephony/RadioResponse.java
index d12e35b..b636917 100644
--- a/src/java/com/android/internal/telephony/RadioResponse.java
+++ b/src/java/com/android/internal/telephony/RadioResponse.java
@@ -180,6 +180,15 @@
 
     /**
      * @param responseInfo Response info struct containing response type, serial no. and error
+     * @param calls Current call list
+     */
+    public void getCurrentCallsResponse_1_2(RadioResponseInfo responseInfo,
+                                        ArrayList<android.hardware.radio.V1_2.Call> calls) {
+        responseCurrentCalls_1_2(responseInfo, calls);
+    }
+
+    /**
+     * @param responseInfo Response info struct containing response type, serial no. and error
      */
     public void dialResponse(RadioResponseInfo responseInfo) {
         responseVoid(responseInfo);
@@ -1489,6 +1498,88 @@
         }
     }
 
+    private void responseCurrentCalls_1_2(RadioResponseInfo responseInfo,
+                                      ArrayList<android.hardware.radio.V1_2.Call> calls) {
+        RILRequest rr = mRil.processResponse(responseInfo);
+
+        if (rr != null) {
+            int num = calls.size();
+            ArrayList<DriverCall> dcCalls = new ArrayList<DriverCall>(num);
+            DriverCall dc;
+
+            for (int i = 0; i < num; i++) {
+                dc = new DriverCall();
+                // TODO: change name of function stateFromCLCC() in DriverCall.java to name
+                // clarifying what is CLCC
+                dc.state = DriverCall.stateFromCLCC((int) (calls.get(i).base.state));
+                dc.index = calls.get(i).base.index;
+                dc.TOA = calls.get(i).base.toa;
+                dc.isMpty = calls.get(i).base.isMpty;
+                dc.isMT = calls.get(i).base.isMT;
+                dc.als = calls.get(i).base.als;
+                dc.isVoice = calls.get(i).base.isVoice;
+                dc.isVoicePrivacy = calls.get(i).base.isVoicePrivacy;
+                dc.number = calls.get(i).base.number;
+                dc.numberPresentation =
+                        DriverCall.presentationFromCLIP(
+                                (int) (calls.get(i).base.numberPresentation));
+                dc.name = calls.get(i).base.name;
+                dc.namePresentation =
+                        DriverCall.presentationFromCLIP((int) (calls.get(i).base.namePresentation));
+                if (calls.get(i).base.uusInfo.size() == 1) {
+                    dc.uusInfo = new UUSInfo();
+                    dc.uusInfo.setType(calls.get(i).base.uusInfo.get(0).uusType);
+                    dc.uusInfo.setDcs(calls.get(i).base.uusInfo.get(0).uusDcs);
+                    if (!TextUtils.isEmpty(calls.get(i).base.uusInfo.get(0).uusData)) {
+                        byte[] userData = calls.get(i).base.uusInfo.get(0).uusData.getBytes();
+                        dc.uusInfo.setUserData(userData);
+                    } else {
+                        mRil.riljLog("responseCurrentCalls: uusInfo data is null or empty");
+                    }
+
+                    mRil.riljLogv(String.format("Incoming UUS : type=%d, dcs=%d, length=%d",
+                            dc.uusInfo.getType(), dc.uusInfo.getDcs(),
+                            dc.uusInfo.getUserData().length));
+                    mRil.riljLogv("Incoming UUS : data (hex): "
+                            + IccUtils.bytesToHexString(dc.uusInfo.getUserData()));
+                } else {
+                    mRil.riljLogv("Incoming UUS : NOT present!");
+                }
+
+                // Make sure there's a leading + on addresses with a TOA of 145
+                dc.number = PhoneNumberUtils.stringFromStringAndTOA(dc.number, dc.TOA);
+
+                dc.audioQuality = (int) (calls.get(i).audioQuality);
+
+                dcCalls.add(dc);
+
+                if (dc.isVoicePrivacy) {
+                    mRil.mVoicePrivacyOnRegistrants.notifyRegistrants();
+                    mRil.riljLog("InCall VoicePrivacy is enabled");
+                } else {
+                    mRil.mVoicePrivacyOffRegistrants.notifyRegistrants();
+                    mRil.riljLog("InCall VoicePrivacy is disabled");
+                }
+            }
+
+            Collections.sort(dcCalls);
+
+            if ((num == 0) && mRil.mTestingEmergencyCall.getAndSet(false)) {
+                if (mRil.mEmergencyCallbackModeRegistrant != null) {
+                    mRil.riljLog("responseCurrentCalls: call ended, testing emergency call,"
+                            + " notify ECM Registrants");
+                    mRil.mEmergencyCallbackModeRegistrant.notifyRegistrant();
+                }
+            }
+
+            if (responseInfo.error == RadioError.NONE) {
+                sendMessageResponse(rr.mResult, dcCalls);
+            }
+            mRil.processResponseDone(rr, responseInfo, dcCalls);
+        }
+    }
+
+
     private void responseVoid(RadioResponseInfo responseInfo) {
         RILRequest rr = mRil.processResponse(responseInfo);
 
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
index 2ec5101..0c160e2 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -61,6 +61,7 @@
 import android.telephony.PhoneNumberUtils;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
+import android.telephony.SmsManager;
 import android.telephony.TelephonyManager;
 import android.text.Html;
 import android.text.Spanned;
@@ -77,6 +78,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.cdma.sms.UserData;
 import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
 import com.android.internal.telephony.uicc.UiccCard;
 import com.android.internal.telephony.uicc.UiccController;
@@ -92,6 +94,13 @@
     static final String TAG = "SMSDispatcher";    // accessed from inner class
     static final boolean DBG = false;
     private static final String SEND_NEXT_MSG_EXTRA = "SendNextMsg";
+    protected static final String MAP_KEY_PDU = "pdu";
+    protected static final String MAP_KEY_SMSC = "smsc";
+    protected static final String MAP_KEY_DEST_ADDR = "destAddr";
+    protected static final String MAP_KEY_SC_ADDR = "scAddr";
+    protected static final String MAP_KEY_DEST_PORT = "destPort";
+    protected static final String MAP_KEY_DATA = "data";
+    protected static final String MAP_KEY_TEXT = "text";
 
     private static final int PREMIUM_RULE_USE_SIM = 1;
     private static final int PREMIUM_RULE_USE_NETWORK = 2;
@@ -123,18 +132,11 @@
     /** Handle status report from {@code CdmaInboundSmsHandler}. */
     protected static final int EVENT_HANDLE_STATUS_REPORT = 10;
 
-    /** 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 static final int EVENT_GET_IMS_SERVICE = 16;
+
 
     protected Phone mPhone;
     protected final Context mContext;
@@ -159,10 +161,7 @@
      */
     private static int sConcatenatedRef = new Random().nextInt(256);
 
-    /** Outgoing message counter. Shared by all dispatchers. */
-    private SmsUsageMonitor mUsageMonitor;
-
-    private ImsSMSDispatcher mImsSMSDispatcher;
+    protected SmsDispatchersController mSmsDispatchersController;
 
     /** Number of outgoing SmsTrackers waiting for user confirmation. */
     private int mPendingTrackerCount;
@@ -179,16 +178,13 @@
     /**
      * Create a new SMS dispatcher.
      * @param phone the Phone to use
-     * @param usageMonitor the SmsUsageMonitor to use
      */
-    protected SMSDispatcher(Phone phone, SmsUsageMonitor usageMonitor,
-            ImsSMSDispatcher imsSMSDispatcher) {
+    protected SMSDispatcher(Phone phone, SmsDispatchersController smsDispatchersController) {
         mPhone = phone;
-        mImsSMSDispatcher = imsSMSDispatcher;
+        mSmsDispatchersController = smsDispatchersController;
         mContext = phone.getContext();
         mResolver = mContext.getContentResolver();
         mCi = phone.mCi;
-        mUsageMonitor = usageMonitor;
         mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
         mSettingsObserver = new SettingsObserver(this, mPremiumSmsRule, mContext);
         mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
@@ -224,7 +220,6 @@
 
     protected void updatePhoneObject(Phone phone) {
         mPhone = phone;
-        mUsageMonitor = phone.mSmsUsageMonitor;
         Rlog.d(TAG, "Active phone changed to " + mPhone.getPhoneName() );
     }
 
@@ -392,7 +387,7 @@
         @Override
         protected void onServiceReady(ICarrierMessagingService carrierMessagingService) {
             HashMap<String, Object> map = mTracker.getData();
-            String text = (String) map.get("text");
+            String text = (String) map.get(MAP_KEY_TEXT);
 
             if (text != null) {
                 try {
@@ -424,8 +419,8 @@
         @Override
         protected void onServiceReady(ICarrierMessagingService carrierMessagingService) {
             HashMap<String, Object> map = mTracker.getData();
-            byte[] data = (byte[]) map.get("data");
-            int destPort = (int) map.get("destPort");
+            byte[] data = (byte[]) map.get(MAP_KEY_DATA);
+            int destPort = (int) map.get(MAP_KEY_DEST_PORT);
 
             if (data != null) {
                 try {
@@ -630,7 +625,19 @@
     /**
      * Send an SMS PDU. Usually just calls {@link sendRawPdu}.
      */
-    protected abstract void sendSubmitPdu(SmsTracker tracker);
+    private void sendSubmitPdu(SmsTracker tracker) {
+        if (shouldBlockSms()) {
+            Rlog.d(TAG, "Block SMS in Emergency Callback mode");
+            tracker.onFailed(mContext, SmsManager.RESULT_ERROR_NO_SERVICE, 0/*errorCode*/);
+        } else {
+            sendRawPdu(tracker);
+        }
+    }
+
+    /**
+     * @return true if MO SMS should be blocked.
+     */
+    protected abstract boolean shouldBlockSms();
 
     /**
      * Called when SMS send completes. Broadcasts a sentIntent on success.
@@ -727,7 +734,9 @@
                 } else {
                     sentIntent.send(RESULT_ERROR_NO_SERVICE);
                 }
-            } catch (CanceledException ex) {}
+            } catch (CanceledException ex) {
+                Rlog.e(TAG, "Failed to send result");
+            }
         }
     }
 
@@ -768,8 +777,25 @@
      *  broadcast when the message is delivered to the recipient.  The
      *  raw pdu of the status report is in the extended data ("pdu").
      */
-    protected abstract void sendData(String destAddr, String scAddr, int destPort,
-            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent);
+    protected void sendData(String destAddr, String scAddr, int destPort,
+            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+        SmsMessageBase.SubmitPduBase pdu = getSubmitPdu(
+                scAddr, destAddr, destPort, data, (deliveryIntent != null));
+        if (pdu != null) {
+            HashMap map = getSmsTrackerMap(destAddr, scAddr, destPort, data, pdu);
+            SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
+                    null /*messageUri*/, false /*isExpectMore*/,
+                    null /*fullMessageText*/, false /*isText*/,
+                    true /*persistMessage*/);
+
+            if (!sendSmsByCarrierApp(true /* isDataSms */, tracker)) {
+                sendSubmitPdu(tracker);
+            }
+        } else {
+            Rlog.e(TAG, "SMSDispatcher.sendData(): getSubmitPdu() returned null");
+            triggerSentIntentForFailure(sentIntent);
+        }
+    }
 
     /**
      * Send a text based SMS.
@@ -798,21 +824,59 @@
      * @param persistMessage whether to save the sent message into SMS DB for a
      *   non-default SMS app.
      */
-    protected abstract void sendText(String destAddr, String scAddr, String text,
-            PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri,
-            String callingPkg, boolean persistMessage);
+    public void sendText(String destAddr, String scAddr, String text,
+                         PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri,
+                         String callingPkg, boolean persistMessage) {
+        Rlog.d(TAG, "sendText");
+        SmsMessageBase.SubmitPduBase pdu = getSubmitPdu(
+                scAddr, destAddr, text, (deliveryIntent != null), null);
+        if (pdu != null) {
+            HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu);
+            SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
+                    messageUri, false /*isExpectMore*/, text, true /*isText*/,
+                    persistMessage);
 
-    /**
-     * Inject an SMS PDU into the android platform.
-     *
-     * @param pdu is the byte array of pdu to be injected into android telephony layer
-     * @param format is the format of SMS pdu (3gpp or 3gpp2)
-     * @param receivedIntent if not NULL this <code>PendingIntent</code> is
-     *  broadcast when the message is successfully received by the
-     *  android telephony layer. This intent is broadcasted at
-     *  the same time an SMS received from radio is responded back.
-     */
-    protected abstract void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent);
+            if (!sendSmsByCarrierApp(false /* isDataSms */, tracker)) {
+                sendSubmitPdu(tracker);
+            }
+        } else {
+            Rlog.e(TAG, "SmsDispatcher.sendText(): getSubmitPdu() returned null");
+            triggerSentIntentForFailure(sentIntent);
+        }
+    }
+
+    private void triggerSentIntentForFailure(PendingIntent sentIntent) {
+        if (sentIntent != null) {
+            try {
+                sentIntent.send(SmsManager.RESULT_ERROR_GENERIC_FAILURE);
+            } catch (CanceledException ex) {
+                Rlog.e(TAG, "Intent has been canceled!");
+            }
+        }
+    }
+
+    private boolean sendSmsByCarrierApp(boolean isDataSms, SmsTracker tracker ) {
+        String carrierPackage = getCarrierAppPackageName();
+        if (carrierPackage != null) {
+            Rlog.d(TAG, "Found carrier package.");
+            SmsSender smsSender;
+            if (isDataSms) {
+                smsSender = new DataSmsSender(tracker);
+            } else {
+                smsSender = new TextSmsSender(tracker);
+            }
+            smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender));
+            return true;
+        }
+
+        return false;
+    }
+
+    protected abstract SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+            String message, boolean statusReportRequested, SmsHeader smsHeader);
+
+    protected abstract SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+            int destPort, byte[] message, boolean statusReportRequested);
 
     /**
      * Calculate the number of septets needed to encode the message. This function should only be
@@ -927,7 +991,8 @@
         if (carrierPackage != null) {
             Rlog.d(TAG, "Found carrier package.");
             MultipartSmsSender smsSender = new MultipartSmsSender(parts, trackers);
-            smsSender.sendSmsByCarrierApp(carrierPackage, new MultipartSmsSenderCallback(smsSender));
+            smsSender.sendSmsByCarrierApp(carrierPackage,
+                    new MultipartSmsSenderCallback(smsSender));
         } else {
             Rlog.v(TAG, "No carrier package.");
             for (SmsTracker tracker : trackers) {
@@ -943,11 +1008,57 @@
     /**
      * Create a new SubmitPdu and return the SMS tracker.
      */
-    protected abstract SmsTracker getNewSubmitPduTracker(String destinationAddress, String scAddress,
+    private SmsTracker getNewSubmitPduTracker(String destinationAddress, String scAddress,
             String message, SmsHeader smsHeader, int encoding,
             PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart,
             AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
-            String fullMessageText);
+            String fullMessageText) {
+        if (isCdmaMo()) {
+            UserData uData = new UserData();
+            uData.payloadStr = message;
+            uData.userDataHeader = smsHeader;
+            if (encoding == SmsConstants.ENCODING_7BIT) {
+                uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET;
+            } else { // assume UTF-16
+                uData.msgEncoding = UserData.ENCODING_UNICODE_16;
+            }
+            uData.msgEncodingSet = true;
+
+            /* By setting the statusReportRequested bit only for the
+             * last message fragment, this will result in only one
+             * callback to the sender when that last fragment delivery
+             * has been acknowledged. */
+            //TODO FIX
+            SmsMessageBase.SubmitPduBase submitPdu =
+                    com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(destinationAddress,
+                            uData, (deliveryIntent != null) && lastPart);
+
+            HashMap map = getSmsTrackerMap(destinationAddress, scAddress,
+                    message, submitPdu);
+            return getSmsTracker(map, sentIntent, deliveryIntent,
+                    getFormat(), unsentPartCount, anyPartFailed, messageUri, smsHeader,
+                    false /*isExpectMore*/, fullMessageText, true /*isText*/,
+                    true /*persistMessage*/);
+
+        } else {
+            SmsMessageBase.SubmitPduBase pdu =
+                    com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
+                            destinationAddress, message, deliveryIntent != null,
+                            SmsHeader.toByteArray(smsHeader), encoding, smsHeader.languageTable,
+                            smsHeader.languageShiftTable);
+            if (pdu != null) {
+                HashMap map =  getSmsTrackerMap(destinationAddress, scAddress,
+                        message, pdu);
+                return getSmsTracker(map, sentIntent,
+                        deliveryIntent, getFormat(), unsentPartCount, anyPartFailed, messageUri,
+                        smsHeader, !lastPart, fullMessageText, true /*isText*/,
+                        false /*persistMessage*/);
+            } else {
+                Rlog.e(TAG, "GsmSMSDispatcher.sendNewSubmitPdu(): getSubmitPdu() returned null");
+                return null;
+            }
+        }
+    }
 
     /**
      * Send an SMS
@@ -974,7 +1085,7 @@
     @VisibleForTesting
     public void sendRawPdu(SmsTracker tracker) {
         HashMap map = tracker.getData();
-        byte pdu[] = (byte[]) map.get("pdu");
+        byte pdu[] = (byte[]) map.get(MAP_KEY_PDU);
 
         if (mSmsSendDisabled) {
             Rlog.e(TAG, "Device does not support sending sms.");
@@ -1016,7 +1127,8 @@
         // handler with the SmsTracker to request user confirmation before sending.
         if (checkDestination(tracker)) {
             // check for excessive outgoing SMS usage by this app
-            if (!mUsageMonitor.check(appInfo.packageName, SINGLE_PART_SMS)) {
+            if (!mSmsDispatchersController.getUsageMonitor().check(
+                    appInfo.packageName, SINGLE_PART_SMS)) {
                 sendMessage(obtainMessage(EVENT_SEND_LIMIT_REACHED_CONFIRMATION, tracker));
                 return;
             }
@@ -1050,7 +1162,8 @@
                     simCountryIso = mTelephonyManager.getNetworkCountryIso();
                 }
 
-                smsCategory = mUsageMonitor.checkDestination(tracker.mDestAddress, simCountryIso);
+                smsCategory = mSmsDispatchersController.getUsageMonitor().checkDestination(
+                        tracker.mDestAddress, simCountryIso);
             }
             if (rule == PREMIUM_RULE_USE_NETWORK || rule == PREMIUM_RULE_USE_BOTH) {
                 String networkCountryIso = mTelephonyManager.getNetworkCountryIso();
@@ -1060,7 +1173,8 @@
                 }
 
                 smsCategory = SmsUsageMonitor.mergeShortCodeCategories(smsCategory,
-                        mUsageMonitor.checkDestination(tracker.mDestAddress, networkCountryIso));
+                        mSmsDispatchersController.getUsageMonitor().checkDestination(
+                                tracker.mDestAddress, networkCountryIso));
             }
 
             if (smsCategory == SmsUsageMonitor.CATEGORY_NOT_SHORT_CODE
@@ -1076,7 +1190,8 @@
             }
 
             // Wait for user confirmation unless the user has set permission to always allow/deny
-            int premiumSmsPermission = mUsageMonitor.getPremiumSmsPermission(
+            int premiumSmsPermission =
+                    mSmsDispatchersController.getUsageMonitor().getPremiumSmsPermission(
                     tracker.getAppPackageName());
             if (premiumSmsPermission == SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) {
                 // First time trying to send to premium SMS.
@@ -1233,32 +1348,6 @@
     }
 
     /**
-     * Returns the premium SMS permission for the specified package. If the package has never
-     * been seen before, the default {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER}
-     * will be returned.
-     * @param packageName the name of the package to query permission
-     * @return one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_UNKNOWN},
-     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER},
-     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or
-     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW}
-     */
-    public int getPremiumSmsPermission(String packageName) {
-        return mUsageMonitor.getPremiumSmsPermission(packageName);
-    }
-
-    /**
-     * Sets the premium SMS permission for the specified package and save the value asynchronously
-     * to persistent storage.
-     * @param packageName the name of the package to set permission
-     * @param permission one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER},
-     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or
-     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW}
-     */
-    public void setPremiumSmsPermission(String packageName, int permission) {
-        mUsageMonitor.setPremiumSmsPermission(packageName, permission);
-    }
-
-    /**
      * Send the message along to the radio.
      *
      * @param tracker holds the SMS message to send
@@ -1266,23 +1355,16 @@
     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
      */
     public void sendRetrySms(SmsTracker tracker) {
-        // re-routing to ImsSMSDispatcher
-        if (mImsSMSDispatcher != null) {
-            mImsSMSDispatcher.sendRetrySms(tracker);
+        // re-routing to SmsDispatchersController
+        if (mSmsDispatchersController != null) {
+            mSmsDispatchersController.sendRetrySms(tracker);
         } else {
-            Rlog.e(TAG, mImsSMSDispatcher + " is null. Retry failed");
+            Rlog.e(TAG, mSmsDispatchersController + " is null. Retry failed");
         }
     }
 
@@ -1320,7 +1402,8 @@
         }
 
         sendMultipartText(destinationAddress, scAddress, parts, sentIntents, deliveryIntents,
-                null/*messageUri*/, null/*callingPkg*/, tracker.mPersistMessage);
+                null/*messageUri*/, null/*callingPkg*/,
+                tracker.mPersistMessage);
     }
 
     /**
@@ -1632,30 +1715,30 @@
             PendingIntent deliveryIntent, String format, Uri messageUri, boolean isExpectMore,
             String fullMessageText, boolean isText, boolean persistMessage) {
         return getSmsTracker(data, sentIntent, deliveryIntent, format, null/*unsentPartCount*/,
-                null/*anyPartFailed*/, messageUri, null/*smsHeader*/, isExpectMore,
-                fullMessageText, isText, persistMessage);
+                null/*anyPartFailed*/, messageUri, null/*smsHeader*/,
+                isExpectMore, fullMessageText, isText, persistMessage);
     }
 
     protected HashMap<String, Object> getSmsTrackerMap(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);
+        map.put(MAP_KEY_DEST_ADDR, destAddr);
+        map.put(MAP_KEY_SC_ADDR, scAddr);
+        map.put(MAP_KEY_TEXT, text);
+        map.put(MAP_KEY_SMSC, pdu.encodedScAddress);
+        map.put(MAP_KEY_PDU, pdu.encodedMessage);
         return map;
     }
 
     protected HashMap<String, Object> getSmsTrackerMap(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", destPort);
-        map.put("data", data);
-        map.put("smsc", pdu.encodedScAddress);
-        map.put("pdu", pdu.encodedMessage);
+        map.put(MAP_KEY_DEST_ADDR, destAddr);
+        map.put(MAP_KEY_SC_ADDR, scAddr);
+        map.put(MAP_KEY_DEST_PORT, destPort);
+        map.put(MAP_KEY_DATA, data);
+        map.put(MAP_KEY_SMSC, pdu.encodedScAddress);
+        map.put(MAP_KEY_PDU, pdu.encodedMessage);
         return map;
     }
 
@@ -1720,7 +1803,8 @@
                 }
                 sendMessage(msg);
             }
-            setPremiumSmsPermission(mTracker.getAppPackageName(), newSmsPermission);
+            mSmsDispatchersController.setPremiumSmsPermission(mTracker.getAppPackageName(),
+                    newSmsPermission);
         }
 
         @Override
@@ -1755,23 +1839,14 @@
     }
 
     public boolean isIms() {
-        if (mImsSMSDispatcher != null) {
-            return mImsSMSDispatcher.isIms();
+        if (mSmsDispatchersController != null) {
+            return mSmsDispatchersController.isIms();
         } else {
-            Rlog.e(TAG, mImsSMSDispatcher + " is null");
+            Rlog.e(TAG, "mSmsDispatchersController  is null");
             return false;
         }
     }
 
-    public String getImsSmsFormat() {
-        if (mImsSMSDispatcher != null) {
-            return mImsSMSDispatcher.getImsSmsFormat();
-        } else {
-            Rlog.e(TAG, mImsSMSDispatcher + " is null");
-            return null;
-        }
-    }
-
     private String getMultipartMessageText(ArrayList<String> parts) {
         final StringBuilder sb = new StringBuilder();
         for (String part : parts) {
@@ -1819,4 +1894,8 @@
             throw new SecurityException("Caller is not phone or carrier app!");
         }
     }
+
+    protected boolean isCdmaMo() {
+        return mSmsDispatchersController.isCdmaMo();
+    }
 }
diff --git a/src/java/com/android/internal/telephony/SmsDispatchersController.java b/src/java/com/android/internal/telephony/SmsDispatchersController.java
new file mode 100644
index 0000000..f62a90a
--- /dev/null
+++ b/src/java/com/android/internal/telephony/SmsDispatchersController.java
@@ -0,0 +1,562 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.provider.Telephony.Sms;
+import android.provider.Telephony.Sms.Intents;
+import android.telephony.Rlog;
+import android.telephony.SmsManager;
+import android.util.Pair;
+
+import com.android.ims.ImsManager;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.cdma.CdmaInboundSmsHandler;
+import com.android.internal.telephony.cdma.CdmaSMSDispatcher;
+import com.android.internal.telephony.gsm.GsmInboundSmsHandler;
+import com.android.internal.telephony.gsm.GsmSMSDispatcher;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ *
+ */
+public class SmsDispatchersController extends Handler {
+    private static final String TAG = "SmsDispatchersController";
+
+    /** Radio is ON */
+    private static final int EVENT_RADIO_ON = 11;
+
+    /** IMS registration/SMS format changed */
+    private static final int EVENT_IMS_STATE_CHANGED = 12;
+
+    /** Callback from RIL_REQUEST_IMS_REGISTRATION_STATE */
+    private static final int EVENT_IMS_STATE_DONE = 13;
+
+    private SMSDispatcher mCdmaDispatcher;
+    private SMSDispatcher mGsmDispatcher;
+    private ImsSmsDispatcher mImsSmsDispatcher;
+
+    private GsmInboundSmsHandler mGsmInboundSmsHandler;
+    private CdmaInboundSmsHandler mCdmaInboundSmsHandler;
+
+    private Phone mPhone;
+    /** Outgoing message counter. Shared by all dispatchers. */
+    private final SmsUsageMonitor mUsageMonitor;
+    private final CommandsInterface mCi;
+    private final Context mContext;
+
+    /** true if IMS is registered and sms is supported, false otherwise.*/
+    private boolean mIms = false;
+    private String mImsSmsFormat = SmsConstants.FORMAT_UNKNOWN;
+
+    public SmsDispatchersController(Phone phone, SmsStorageMonitor storageMonitor,
+            SmsUsageMonitor usageMonitor) {
+        Rlog.d(TAG, "SmsDispatchersController created");
+
+        mContext = phone.getContext();
+        mUsageMonitor = usageMonitor;
+        mCi = phone.mCi;
+        mPhone = phone;
+
+        // Create dispatchers, inbound SMS handlers and
+        // broadcast undelivered messages in raw table.
+        mImsSmsDispatcher = new ImsSmsDispatcher(phone, this);
+        mCdmaDispatcher = new CdmaSMSDispatcher(phone, this);
+        mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(phone.getContext(),
+                storageMonitor, phone);
+        mCdmaInboundSmsHandler = CdmaInboundSmsHandler.makeInboundSmsHandler(phone.getContext(),
+                storageMonitor, phone, (CdmaSMSDispatcher) mCdmaDispatcher);
+        mGsmDispatcher = new GsmSMSDispatcher(phone, this, mGsmInboundSmsHandler);
+        SmsBroadcastUndelivered.initialize(phone.getContext(),
+                mGsmInboundSmsHandler, mCdmaInboundSmsHandler);
+        InboundSmsHandler.registerNewMessageNotificationActionHandler(phone.getContext());
+
+        mCi.registerForOn(this, EVENT_RADIO_ON, null);
+        mCi.registerForImsNetworkStateChanged(this, EVENT_IMS_STATE_CHANGED, null);
+    }
+
+    /* Updates the phone object when there is a change */
+    protected void updatePhoneObject(Phone phone) {
+        Rlog.d(TAG, "In IMS updatePhoneObject ");
+        mCdmaDispatcher.updatePhoneObject(phone);
+        mGsmDispatcher.updatePhoneObject(phone);
+        mGsmInboundSmsHandler.updatePhoneObject(phone);
+        mCdmaInboundSmsHandler.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:
+                if (isCdmaMo()) {
+                    mCdmaDispatcher.handleMessage(msg);
+                } else {
+                    mGsmDispatcher.handleMessage(msg);
+                }
+        }
+    }
+
+    private void setImsSmsFormat(int format) {
+        switch (format) {
+            case PhoneConstants.PHONE_TYPE_GSM:
+                mImsSmsFormat = SmsConstants.FORMAT_3GPP;
+                break;
+            case PhoneConstants.PHONE_TYPE_CDMA:
+                mImsSmsFormat = SmsConstants.FORMAT_3GPP2;
+                break;
+            default:
+                mImsSmsFormat = SmsConstants.FORMAT_UNKNOWN;
+                break;
+        }
+    }
+
+    private void updateImsInfo(AsyncResult ar) {
+        int[] responseArray = (int[]) ar.result;
+        setImsSmsFormat(responseArray[1]);
+        mIms = responseArray[0] == 1 && !SmsConstants.FORMAT_UNKNOWN.equals(mImsSmsFormat);
+        Rlog.d(TAG, "IMS registration state: " + mIms + " format: " + mImsSmsFormat);
+    }
+
+    /**
+     * Inject an SMS PDU into the android platform.
+     *
+     * @param pdu is the byte array of pdu to be injected into android telephony layer
+     * @param format is the format of SMS pdu (3gpp or 3gpp2)
+     * @param callback if not NULL this callback is triggered when the message is successfully
+     *                 received by the android telephony layer. This callback is triggered at
+     *                 the same time an SMS received from radio is responded back.
+     */
+    @VisibleForTesting
+    public void injectSmsPdu(byte[] pdu, String format, SmsInjectionCallback callback) {
+        Rlog.d(TAG, "SmsDispatchersController:injectSmsPdu");
+        try {
+            // TODO We need to decide whether we should allow injecting GSM(3gpp)
+            // SMS pdus when the phone is camping on CDMA(3gpp2) network and vice versa.
+            android.telephony.SmsMessage msg =
+                    android.telephony.SmsMessage.createFromPdu(pdu, format);
+
+            // Only class 1 SMS are allowed to be injected.
+            if (msg == null
+                    || msg.getMessageClass() != android.telephony.SmsMessage.MessageClass.CLASS_1) {
+                if (msg == null) {
+                    Rlog.e(TAG, "injectSmsPdu: createFromPdu returned null");
+                }
+                callback.onSmsInjectedResult(Intents.RESULT_SMS_GENERIC_ERROR);
+                return;
+            }
+
+            AsyncResult ar = new AsyncResult(callback, msg, null);
+
+            if (format.equals(SmsConstants.FORMAT_3GPP)) {
+                Rlog.i(TAG, "SmsDispatchersController:injectSmsText Sending msg=" + msg
+                        + ", format=" + format + "to mGsmInboundSmsHandler");
+                mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_INJECT_SMS, ar);
+            } else if (format.equals(SmsConstants.FORMAT_3GPP2)) {
+                Rlog.i(TAG, "SmsDispatchersController:injectSmsText Sending msg=" + msg
+                        + ", format=" + format + "to mCdmaInboundSmsHandler");
+                mCdmaInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_INJECT_SMS, ar);
+            } else {
+                // Invalid pdu format.
+                Rlog.e(TAG, "Invalid pdu format: " + format);
+                callback.onSmsInjectedResult(Intents.RESULT_SMS_GENERIC_ERROR);
+            }
+        } catch (Exception e) {
+            Rlog.e(TAG, "injectSmsPdu failed: ", e);
+            callback.onSmsInjectedResult(Intents.RESULT_SMS_GENERIC_ERROR);
+        }
+    }
+
+    /**
+     * Retry the message along to the radio.
+     *
+     * @param tracker holds the SMS message to send
+     */
+    public void sendRetrySms(SMSDispatcher.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.getData();
+
+        // 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!");
+            tracker.onFailed(mContext, SmsManager.RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/);
+            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);
+    }
+
+    public boolean isIms() {
+        return mIms;
+    }
+
+    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.
+     */
+    protected 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.
+     */
+    public boolean isCdmaFormat(String format) {
+        return (mCdmaDispatcher.getFormat().equals(format));
+    }
+
+    /**
+     * Send a data based SMS to a specific application port.
+     *
+     * @param destAddr the address to send the message to
+     * @param scAddr is the service center address or null to use
+     *  the current default SMSC
+     * @param destPort the port to deliver the message to
+     * @param data the body of the message to send
+     * @param sentIntent if not NULL this <code>PendingIntent</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:<br>
+     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
+     *  <code>RESULT_ERROR_NULL_PDU</code><br>
+     *  <code>RESULT_ERROR_NO_SERVICE</code><br>.
+     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
+     *  the extra "errorCode" containing a radio technology specific value,
+     *  generally only useful for troubleshooting.<br>
+     *  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>PendingIntent</code> is
+     *  broadcast when the message is delivered to the recipient.  The
+     *  raw pdu of the status report is in the extended data ("pdu").
+     */
+    protected void sendData(String destAddr, String scAddr, int destPort,
+                            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+        if (mImsSmsDispatcher.isAvailable()) {
+            mImsSmsDispatcher.sendData(destAddr, scAddr, destPort, data, sentIntent,
+                    deliveryIntent);
+        } else if (isCdmaMo()) {
+            mCdmaDispatcher.sendData(destAddr, scAddr, destPort, data, sentIntent, deliveryIntent);
+        } else {
+            mGsmDispatcher.sendData(destAddr, scAddr, destPort, data, sentIntent, deliveryIntent);
+        }
+    }
+
+    /**
+     * Send a text based SMS.
+     *  @param destAddr the address to send the message to
+     * @param scAddr is the service center address or null to use
+     *  the current default SMSC
+     * @param text the body of the message to send
+     * @param sentIntent if not NULL this <code>PendingIntent</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:<br>
+     *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
+     *  <code>RESULT_ERROR_RADIO_OFF</code><br>
+     *  <code>RESULT_ERROR_NULL_PDU</code><br>
+     *  <code>RESULT_ERROR_NO_SERVICE</code><br>.
+     *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
+     *  the extra "errorCode" containing a radio technology specific value,
+     *  generally only useful for troubleshooting.<br>
+     *  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>PendingIntent</code> is
+     *  broadcast when the message is delivered to the recipient.  The
+     * @param messageUri optional URI of the message if it is already stored in the system
+     * @param callingPkg the calling package name
+     * @param persistMessage whether to save the sent message into SMS DB for a
+     *   non-default SMS app.
+     */
+    public void sendText(String destAddr, String scAddr, String text,
+                            PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri,
+                            String callingPkg, boolean persistMessage) {
+        if (mImsSmsDispatcher.isAvailable()) {
+            mImsSmsDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
+                    messageUri, callingPkg, persistMessage);
+        } else if (isCdmaMo()) {
+            mCdmaDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent, messageUri,
+                    callingPkg, persistMessage);
+        } else {
+            mGsmDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent, messageUri,
+                    callingPkg, persistMessage);
+        }
+    }
+
+    /**
+     * Send a multi-part text based SMS.
+     *  @param destAddr the address to send the message to
+     * @param scAddr is the service center address or null to use
+     *   the current default SMSC
+     * @param parts an <code>ArrayList</code> of strings that, in order,
+     *   comprise the original message
+     * @param sentIntents if not null, an <code>ArrayList</code> of
+     *   <code>PendingIntent</code>s (one for each message part) that is
+     *   broadcast when the corresponding message part has been sent.
+     *   The result code will be <code>Activity.RESULT_OK<code> for success,
+     *   or one of these errors:
+     *   <code>RESULT_ERROR_GENERIC_FAILURE</code>
+     *   <code>RESULT_ERROR_RADIO_OFF</code>
+     *   <code>RESULT_ERROR_NULL_PDU</code>
+     *   <code>RESULT_ERROR_NO_SERVICE</code>.
+     *  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 deliveryIntents if not null, an <code>ArrayList</code> of
+     *   <code>PendingIntent</code>s (one for each message part) that is
+     *   broadcast when the corresponding message part has been delivered
+     *   to the recipient.  The raw pdu of the status report is in the
+     * @param messageUri optional URI of the message if it is already stored in the system
+     * @param callingPkg the calling package name
+     * @param persistMessage whether to save the sent message into SMS DB for a
+     *   non-default SMS app.
+     */
+    protected void sendMultipartText(String destAddr, String scAddr,
+            ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
+            ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg,
+            boolean persistMessage) {
+        if (mImsSmsDispatcher.isAvailable()) {
+            mImsSmsDispatcher.sendMultipartText(destAddr, scAddr, parts, sentIntents,
+                    deliveryIntents, messageUri, callingPkg, persistMessage);
+        } else if (isCdmaMo()) {
+            mCdmaDispatcher.sendMultipartText(destAddr, scAddr, parts, sentIntents, deliveryIntents,
+                    messageUri, callingPkg, persistMessage);
+        } else {
+            mGsmDispatcher.sendMultipartText(destAddr, scAddr, parts, sentIntents, deliveryIntents,
+                    messageUri, callingPkg, persistMessage);
+        }
+    }
+
+    /**
+     * Returns the premium SMS permission for the specified package. If the package has never
+     * been seen before, the default {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER}
+     * will be returned.
+     * @param packageName the name of the package to query permission
+     * @return one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_UNKNOWN},
+     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER},
+     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or
+     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW}
+     */
+    public int getPremiumSmsPermission(String packageName) {
+        return mUsageMonitor.getPremiumSmsPermission(packageName);
+    }
+
+    /**
+     * Sets the premium SMS permission for the specified package and save the value asynchronously
+     * to persistent storage.
+     * @param packageName the name of the package to set permission
+     * @param permission one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER},
+     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or
+     *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW}
+     */
+    public void setPremiumSmsPermission(String packageName, int permission) {
+        mUsageMonitor.setPremiumSmsPermission(packageName, permission);
+    }
+
+    public SmsUsageMonitor getUsageMonitor() {
+        return mUsageMonitor;
+    }
+
+    /**
+     * Triggers the correct method for handling the sms status report based on the format.
+     *
+     * @param tracker the sms tracker.
+     * @param format the format.
+     * @param pdu the pdu of the report.
+     * @return a Pair in which the first boolean is whether the report was handled successfully
+     *          or not and the second boolean is whether processing the sms is complete and the
+     *          tracker no longer need to be kept track of, false if we should expect more callbacks
+     *          and the tracker should be kept.
+     */
+    public Pair<Boolean, Boolean> handleSmsStatusReport(SMSDispatcher.SmsTracker tracker,
+            String format, byte[] pdu) {
+        if (isCdmaFormat(format)) {
+            return handleCdmaStatusReport(tracker, format, pdu);
+        } else {
+            return handleGsmStatusReport(tracker, format, pdu);
+        }
+    }
+
+    private Pair<Boolean, Boolean> handleCdmaStatusReport(SMSDispatcher.SmsTracker tracker,
+            String format, byte[] pdu) {
+        tracker.updateSentMessageStatus(mContext, Sms.STATUS_COMPLETE);
+        boolean success = triggerDeliveryIntent(tracker, format, pdu);
+        return new Pair(success, true /* complete */);
+    }
+
+    private Pair<Boolean, Boolean> handleGsmStatusReport(SMSDispatcher.SmsTracker tracker,
+            String format, byte[] pdu) {
+        com.android.internal.telephony.gsm.SmsMessage sms =
+                com.android.internal.telephony.gsm.SmsMessage.newFromCDS(pdu);
+        boolean complete = false;
+        boolean success = false;
+        if (sms != null) {
+            int tpStatus = sms.getStatus();
+            if(tpStatus >= Sms.STATUS_FAILED || tpStatus < Sms.STATUS_PENDING ) {
+                // Update the message status (COMPLETE or FAILED)
+                tracker.updateSentMessageStatus(mContext, tpStatus);
+                complete = true;
+            }
+            success = triggerDeliveryIntent(tracker, format, pdu);
+        }
+        return new Pair(success, complete);
+    }
+
+    private boolean triggerDeliveryIntent(SMSDispatcher.SmsTracker tracker, String format,
+                                          byte[] pdu) {
+        PendingIntent intent = tracker.mDeliveryIntent;
+        Intent fillIn = new Intent();
+        fillIn.putExtra("pdu", pdu);
+        fillIn.putExtra("format", format);
+        try {
+            intent.send(mContext, Activity.RESULT_OK, fillIn);
+            return true;
+        } catch (CanceledException ex) {
+            return false;
+        }
+    }
+
+
+    public interface SmsInjectionCallback {
+        void onSmsInjectedResult(int result);
+    }
+}
diff --git a/src/java/com/android/internal/telephony/SubscriptionMonitor.java b/src/java/com/android/internal/telephony/SubscriptionMonitor.java
index 4307875..8ccec6f 100644
--- a/src/java/com/android/internal/telephony/SubscriptionMonitor.java
+++ b/src/java/com/android/internal/telephony/SubscriptionMonitor.java
@@ -70,7 +70,7 @@
     public SubscriptionMonitor(ITelephonyRegistry tr, Context context,
             SubscriptionController subscriptionController, int numPhones) {
         try {
-            tr.addOnSubscriptionsChangedListener("SubscriptionMonitor",
+            tr.addOnSubscriptionsChangedListener(context.getOpPackageName(),
                     mSubscriptionsChangedListener);
         } catch (RemoteException e) {
         }
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index 1cfdc33..97f4c83 100755
--- a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -20,38 +20,31 @@
 import android.app.PendingIntent;
 import android.app.PendingIntent.CanceledException;
 import android.content.Intent;
-import android.net.Uri;
 import android.os.Message;
-import android.os.SystemProperties;
 import android.provider.Telephony.Sms;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
-import android.telephony.SmsManager;
 import android.telephony.TelephonyManager;
+import android.util.Pair;
 
-import com.android.internal.telephony.GsmAlphabet;
 import com.android.internal.telephony.GsmCdmaPhone;
-import com.android.internal.telephony.ImsSMSDispatcher;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.SMSDispatcher;
 import com.android.internal.telephony.SmsConstants;
+import com.android.internal.telephony.SMSDispatcher;
+import com.android.internal.telephony.SmsDispatchersController;
 import com.android.internal.telephony.SmsHeader;
-import com.android.internal.telephony.SmsUsageMonitor;
-import com.android.internal.telephony.TelephonyProperties;
-import com.android.internal.telephony.cdma.sms.UserData;
+import com.android.internal.telephony.util.SMSDispatcherUtil;
+import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
+import com.android.internal.telephony.SmsMessageBase;
 
-import java.util.HashMap;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
 
 public class CdmaSMSDispatcher extends SMSDispatcher {
     private static final String TAG = "CdmaSMSDispatcher";
     private static final boolean VDBG = false;
 
-    public CdmaSMSDispatcher(Phone phone, SmsUsageMonitor usageMonitor,
-            ImsSMSDispatcher imsSMSDispatcher) {
-        super(phone, usageMonitor, imsSMSDispatcher);
+    public CdmaSMSDispatcher(Phone phone, SmsDispatchersController smsDispatchersController) {
+        super(phone, smsDispatchersController);
         Rlog.d(TAG, "CdmaSMSDispatcher created");
     }
 
@@ -79,6 +72,29 @@
         }
     }
 
+    @Override
+    protected boolean shouldBlockSms() {
+        return SMSDispatcherUtil.shouldBlockSms(isCdmaMo(), mPhone);
+    }
+
+    @Override
+    protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+            String message, boolean statusReportRequested, SmsHeader smsHeader) {
+        return SMSDispatcherUtil.getSubmitPduCdma(scAddr, destAddr, message,
+                statusReportRequested, smsHeader);
+    }
+
+    @Override
+    protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+            int destPort, byte[] message, boolean statusReportRequested) {
+        return SMSDispatcherUtil.getSubmitPduCdma(scAddr, destAddr, destPort, message,
+                statusReportRequested);
+    }
+
+    @Override
+    protected TextEncodingDetails calculateLength(CharSequence messageBody, boolean use7bitOnly) {
+        return SMSDispatcherUtil.calculateLengthCdma(messageBody, use7bitOnly);
+    }
     /**
      * Called from parent class to handle status report from {@code CdmaInboundSmsHandler}.
      * @param sms the CDMA SMS message to process
@@ -87,18 +103,12 @@
         for (int i = 0, count = deliveryPendingList.size(); i < count; i++) {
             SmsTracker tracker = deliveryPendingList.get(i);
             if (tracker.mMessageRef == sms.mMessageRef) {
-                // Found it.  Remove from list and broadcast.
-                deliveryPendingList.remove(i);
-                // Update the message status (COMPLETE)
-                tracker.updateSentMessageStatus(mContext, Sms.STATUS_COMPLETE);
-
-                PendingIntent intent = tracker.mDeliveryIntent;
-                Intent fillIn = new Intent();
-                fillIn.putExtra("pdu", sms.getPdu());
-                fillIn.putExtra("format", getFormat());
-                try {
-                    intent.send(mContext, Activity.RESULT_OK, fillIn);
-                } catch (CanceledException ex) {}
+                Pair<Boolean, Boolean> result =
+                        mSmsDispatchersController.handleSmsStatusReport(tracker, getFormat(),
+                                sms.getPdu());
+                if (result.second) {
+                    deliveryPendingList.remove(i);
+                }
                 break;  // Only expect to see one tracker matching this message.
             }
         }
@@ -106,129 +116,6 @@
 
     /** {@inheritDoc} */
     @Override
-    public void sendData(String destAddr, String scAddr, int destPort,
-            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
-        SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
-                scAddr, destAddr, destPort, data, (deliveryIntent != null));
-        if (pdu != null) {
-            HashMap map = getSmsTrackerMap(destAddr, scAddr, destPort, data, pdu);
-            SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
-                    null /*messageUri*/, false /*isExpectMore*/, null /*fullMessageText*/,
-                    false /*isText*/, true /*persistMessage*/);
-
-            String carrierPackage = getCarrierAppPackageName();
-            if (carrierPackage != null) {
-                Rlog.d(TAG, "Found carrier package.");
-                DataSmsSender smsSender = new DataSmsSender(tracker);
-                smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender));
-            } else {
-                Rlog.v(TAG, "No carrier package.");
-                sendSubmitPdu(tracker);
-            }
-        } else {
-            Rlog.e(TAG, "CdmaSMSDispatcher.sendData(): getSubmitPdu() returned null");
-            if (sentIntent != null) {
-                try {
-                    sentIntent.send(SmsManager.RESULT_ERROR_GENERIC_FAILURE);
-                } catch (CanceledException ex) {
-                    Rlog.e(TAG, "Intent has been canceled!");
-                }
-            }
-        }
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent,
-            PendingIntent deliveryIntent, Uri messageUri, String callingPkg,
-            boolean persistMessage) {
-        SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
-                scAddr, destAddr, text, (deliveryIntent != null), null);
-        if (pdu != null) {
-            HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu);
-            SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
-                    messageUri, false /*isExpectMore*/, text, true /*isText*/, persistMessage);
-
-            String carrierPackage = getCarrierAppPackageName();
-            if (carrierPackage != null) {
-                Rlog.d(TAG, "Found carrier package.");
-                TextSmsSender smsSender = new TextSmsSender(tracker);
-                smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender));
-            } else {
-                Rlog.v(TAG, "No carrier package.");
-                sendSubmitPdu(tracker);
-            }
-        } else {
-            Rlog.e(TAG, "CdmaSMSDispatcher.sendText(): getSubmitPdu() returned null");
-            if (sentIntent != null) {
-                try {
-                    sentIntent.send(SmsManager.RESULT_ERROR_GENERIC_FAILURE);
-                } catch (CanceledException ex) {
-                    Rlog.e(TAG, "Intent has been canceled!");
-                }
-            }
-        }
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    protected void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) {
-        throw new IllegalStateException("This method must be called only on ImsSMSDispatcher");
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    protected GsmAlphabet.TextEncodingDetails calculateLength(CharSequence messageBody,
-            boolean use7bitOnly) {
-        return SmsMessage.calculateLength(messageBody, use7bitOnly, false);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    protected SmsTracker getNewSubmitPduTracker(String destinationAddress, String scAddress,
-            String message, SmsHeader smsHeader, int encoding,
-            PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart,
-            AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
-            String fullMessageText) {
-        UserData uData = new UserData();
-        uData.payloadStr = message;
-        uData.userDataHeader = smsHeader;
-        if (encoding == SmsConstants.ENCODING_7BIT) {
-            uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET;
-        } else { // assume UTF-16
-            uData.msgEncoding = UserData.ENCODING_UNICODE_16;
-        }
-        uData.msgEncodingSet = true;
-
-        /* By setting the statusReportRequested bit only for the
-         * last message fragment, this will result in only one
-         * callback to the sender when that last fragment delivery
-         * has been acknowledged. */
-        SmsMessage.SubmitPdu submitPdu = SmsMessage.getSubmitPdu(destinationAddress,
-                uData, (deliveryIntent != null) && lastPart);
-
-        HashMap map = getSmsTrackerMap(destinationAddress, scAddress,
-                message, submitPdu);
-        return getSmsTracker(map, sentIntent, deliveryIntent,
-                getFormat(), unsentPartCount, anyPartFailed, messageUri, smsHeader,
-                false /*isExpextMore*/, fullMessageText, true /*isText*/,
-                true /*persistMessage*/);
-    }
-
-    @Override
-    protected void sendSubmitPdu(SmsTracker tracker) {
-        if (mPhone.isInEcm()) {
-            if (VDBG) {
-                Rlog.d(TAG, "Block SMS in Emergency Callback mode");
-            }
-            tracker.onFailed(mContext, SmsManager.RESULT_ERROR_NO_SERVICE, 0/*errorCode*/);
-            return;
-        }
-        sendRawPdu(tracker);
-    }
-
-    /** {@inheritDoc} */
-    @Override
     public void sendSms(SmsTracker tracker) {
         Rlog.d(TAG, "sendSms: "
                 + " isIms()=" + isIms()
@@ -237,12 +124,6 @@
                 + " mMessageRef=" + tracker.mMessageRef
                 + " SS=" + mPhone.getServiceState().getState());
 
-        sendSmsByPstn(tracker);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    protected void sendSmsByPstn(SmsTracker tracker) {
         int 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) {
diff --git a/src/java/com/android/internal/telephony/dataconnection/CellularDataService.java b/src/java/com/android/internal/telephony/dataconnection/CellularDataService.java
new file mode 100644
index 0000000..cbc3efe
--- /dev/null
+++ b/src/java/com/android/internal/telephony/dataconnection/CellularDataService.java
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony.dataconnection;
+
+import android.hardware.radio.V1_0.SetupDataCallResult;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.NetworkUtils;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.telephony.Rlog;
+import android.telephony.SubscriptionManager;
+import android.telephony.data.DataCallResponse;
+import android.telephony.data.DataProfile;
+import android.telephony.data.DataService;
+import android.telephony.data.DataServiceCallback;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class represents cellular data service which handles telephony data requests and response
+ * from the cellular modem.
+ */
+public class CellularDataService extends DataService {
+    private static final String TAG = CellularDataService.class.getSimpleName();
+
+    private static final boolean DBG = false;
+
+    private static final int SETUP_DATA_CALL_COMPLETE               = 1;
+    private static final int DEACTIVATE_DATA_ALL_COMPLETE           = 2;
+    private static final int SET_INITIAL_ATTACH_APN_COMPLETE        = 3;
+    private static final int SET_DATA_PROFILE_COMPLETE              = 4;
+    private static final int GET_DATA_CALL_LIST_COMPLETE            = 5;
+    private static final int DATA_CALL_LIST_CHANGED                 = 6;
+
+    private class CellularDataServiceProvider extends DataService.DataServiceProvider {
+
+        private final Map<Message, DataServiceCallback> mCallbackMap = new HashMap<>();
+
+        private final Looper mLooper;
+
+        private final Handler mHandler;
+
+        private final Phone mPhone;
+
+        private CellularDataServiceProvider(int slotId) {
+            super(slotId);
+
+            mPhone = PhoneFactory.getPhone(getSlotId());
+
+            HandlerThread thread = new HandlerThread(CellularDataService.class.getSimpleName());
+            thread.start();
+            mLooper = thread.getLooper();
+            mHandler = new Handler(mLooper) {
+                @Override
+                public void handleMessage(Message message) {
+                    DataServiceCallback callback = mCallbackMap.remove(message);
+
+                    AsyncResult ar = (AsyncResult) message.obj;
+                    switch (message.what) {
+                        case SETUP_DATA_CALL_COMPLETE:
+                            SetupDataCallResult result = (SetupDataCallResult) ar.result;
+                            callback.onSetupDataCallComplete(ar.exception != null
+                                    ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE
+                                    : DataServiceCallback.RESULT_SUCCESS,
+                                    convertDataCallResult(result));
+                            break;
+                        case DEACTIVATE_DATA_ALL_COMPLETE:
+                            callback.onDeactivateDataCallComplete(ar.exception != null
+                                    ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE
+                                    : DataServiceCallback.RESULT_SUCCESS);
+                            break;
+                        case SET_INITIAL_ATTACH_APN_COMPLETE:
+                            callback.onSetInitialAttachApnComplete(ar.exception != null
+                                    ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE
+                                    : DataServiceCallback.RESULT_SUCCESS);
+                            break;
+                        case SET_DATA_PROFILE_COMPLETE:
+                            callback.onSetDataProfileComplete(ar.exception != null
+                                    ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE
+                                    : DataServiceCallback.RESULT_SUCCESS);
+                            break;
+                        case GET_DATA_CALL_LIST_COMPLETE:
+                            callback.onGetDataCallListComplete(
+                                    ar.exception != null
+                                            ? DataServiceCallback.RESULT_ERROR_ILLEGAL_STATE
+                                            : DataServiceCallback.RESULT_SUCCESS,
+                                    ar.exception != null
+                                            ? null
+                                            : getDataCallList((List<SetupDataCallResult>) ar.result)
+                                    );
+                            break;
+                        case DATA_CALL_LIST_CHANGED:
+                            notifyDataCallListChanged(getDataCallList(
+                                    (List<SetupDataCallResult>) ar.result));
+                            break;
+                        default:
+                            loge("Unexpected event: " + message.what);
+                            return;
+                    }
+                }
+            };
+
+            if (DBG) log("Register for data call list changed.");
+            mPhone.mCi.registerForDataCallListChanged(mHandler, DATA_CALL_LIST_CHANGED, null);
+        }
+
+        private List<DataCallResponse> getDataCallList(List<SetupDataCallResult> dcList) {
+            List<DataCallResponse> dcResponseList = new ArrayList<>();
+            for (SetupDataCallResult dcResult : dcList) {
+                dcResponseList.add(convertDataCallResult(dcResult));
+            }
+            return dcResponseList;
+        }
+
+        @Override
+        public void setupDataCall(int radioTechnology, DataProfile dataProfile, boolean isRoaming,
+                                  boolean allowRoaming, int reason, LinkProperties linkProperties,
+                                  DataServiceCallback callback) {
+            if (DBG) log("setupDataCall " + getSlotId());
+
+            Message message = null;
+            // Only obtain the message when the caller wants a callback. If the caller doesn't care
+            // the request completed or results, then no need to pass the message down.
+            if (callback != null) {
+                message = Message.obtain(mHandler, SETUP_DATA_CALL_COMPLETE);
+                mCallbackMap.put(message, callback);
+            }
+
+            mPhone.mCi.setupDataCall(radioTechnology, dataProfile, isRoaming, allowRoaming, reason,
+                    linkProperties, message);
+        }
+
+        @Override
+        public void deactivateDataCall(int cid, int reason, DataServiceCallback callback) {
+            if (DBG) log("deactivateDataCall " + getSlotId());
+
+            Message message = null;
+            // Only obtain the message when the caller wants a callback. If the caller doesn't care
+            // the request completed or results, then no need to pass the message down.
+            if (callback != null) {
+                message = Message.obtain(mHandler, DEACTIVATE_DATA_ALL_COMPLETE);
+                mCallbackMap.put(message, callback);
+            }
+
+            mPhone.mCi.deactivateDataCall(cid, reason, message);
+        }
+
+        @Override
+        public void setInitialAttachApn(DataProfile dataProfile, boolean isRoaming,
+                                        DataServiceCallback callback) {
+            if (DBG) log("setInitialAttachApn " + getSlotId());
+
+            Message message = null;
+            // Only obtain the message when the caller wants a callback. If the caller doesn't care
+            // the request completed or results, then no need to pass the message down.
+            if (callback != null) {
+                message = Message.obtain(mHandler, SET_INITIAL_ATTACH_APN_COMPLETE);
+                mCallbackMap.put(message, callback);
+            }
+
+            mPhone.mCi.setInitialAttachApn(dataProfile, isRoaming, message);
+        }
+
+        @Override
+        public void setDataProfile(List<DataProfile> dps, boolean isRoaming,
+                                   DataServiceCallback callback) {
+            if (DBG) log("setDataProfile " + getSlotId());
+
+            Message message = null;
+            // Only obtain the message when the caller wants a callback. If the caller doesn't care
+            // the request completed or results, then no need to pass the message down.
+            if (callback != null) {
+                message = Message.obtain(mHandler, SET_DATA_PROFILE_COMPLETE);
+                mCallbackMap.put(message, callback);
+            }
+
+            mPhone.mCi.setDataProfile(dps.toArray(new DataProfile[dps.size()]), isRoaming, message);
+        }
+
+        @Override
+        public void getDataCallList(DataServiceCallback callback) {
+            if (DBG) log("getDataCallList " + getSlotId());
+
+            Message message = null;
+            // Only obtain the message when the caller wants a callback. If the caller doesn't care
+            // the request completed or results, then no need to pass the message down.
+            if (callback != null) {
+                message = Message.obtain(mHandler, GET_DATA_CALL_LIST_COMPLETE);
+                mCallbackMap.put(message, callback);
+            }
+            mPhone.mCi.getDataCallList(message);
+        }
+    }
+
+    @Override
+    public DataServiceProvider createDataServiceProvider(int slotId) {
+        log("Cellular data service created for slot " + slotId);
+        if (!SubscriptionManager.isValidSlotIndex(slotId)) {
+            loge("Tried to cellular data service with invalid slotId " + slotId);
+            return null;
+        }
+        return new CellularDataServiceProvider(slotId);
+    }
+
+    /**
+     * Convert SetupDataCallResult defined in types.hal into DataCallResponse
+     * @param dcResult setup data call result
+     * @return converted DataCallResponse object
+     */
+    @VisibleForTesting
+    public DataCallResponse convertDataCallResult(SetupDataCallResult dcResult) {
+        if (dcResult == null) return null;
+
+        // Process address
+        String[] addresses = null;
+        if (!TextUtils.isEmpty(dcResult.addresses)) {
+            addresses = dcResult.addresses.split("\\s+");
+        }
+
+        List<LinkAddress> laList = new ArrayList<>();
+        if (addresses != null) {
+            for (String address : addresses) {
+                address = address.trim();
+                if (address.isEmpty()) continue;
+
+                try {
+                    LinkAddress la;
+                    // Check if the address contains prefix length. If yes, LinkAddress
+                    // can parse that.
+                    if (address.split("/").length == 2) {
+                        la = new LinkAddress(address);
+                    } else {
+                        InetAddress ia = NetworkUtils.numericToInetAddress(address);
+                        la = new LinkAddress(ia, (ia instanceof Inet4Address) ? 32 : 128);
+                    }
+
+                    laList.add(la);
+                } catch (IllegalArgumentException e) {
+                    loge("Unknown address: " + address + ", exception = " + e);
+                }
+            }
+        }
+
+        // Process dns
+        String[] dnses = null;
+        if (!TextUtils.isEmpty(dcResult.dnses)) {
+            dnses = dcResult.dnses.split("\\s+");
+        }
+
+        List<InetAddress> dnsList = new ArrayList<>();
+        if (dnses != null) {
+            for (String dns : dnses) {
+                dns = dns.trim();
+                InetAddress ia;
+                try {
+                    ia = NetworkUtils.numericToInetAddress(dns);
+                    dnsList.add(ia);
+                } catch (IllegalArgumentException e) {
+                    loge("Unknown dns: " + dns + ", exception = " + e);
+                }
+            }
+        }
+
+        // Process gateway
+        String[] gateways = null;
+        if (!TextUtils.isEmpty(dcResult.gateways)) {
+            gateways = dcResult.gateways.split("\\s+");
+        }
+
+        List<InetAddress> gatewayList = new ArrayList<>();
+        if (gateways != null) {
+            for (String gateway : gateways) {
+                gateway = gateway.trim();
+                InetAddress ia;
+                try {
+                    ia = NetworkUtils.numericToInetAddress(gateway);
+                    gatewayList.add(ia);
+                } catch (IllegalArgumentException e) {
+                    loge("Unknown gateway: " + gateway + ", exception = " + e);
+                }
+            }
+        }
+
+        return new DataCallResponse(dcResult.status,
+                dcResult.suggestedRetryTime,
+                dcResult.cid,
+                dcResult.active,
+                dcResult.type,
+                dcResult.ifname,
+                laList,
+                dnsList,
+                gatewayList,
+                new ArrayList<>(Arrays.asList(dcResult.pcscf.trim().split("\\s+"))),
+                dcResult.mtu
+        );
+    }
+
+    private void log(String s) {
+        Rlog.d(TAG, s);
+    }
+
+    private void loge(String s) {
+        Rlog.e(TAG, s);
+    }
+}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
index 0cc846f..b94c8a1 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
@@ -44,6 +44,7 @@
 import android.telephony.TelephonyManager;
 import android.telephony.data.DataCallResponse;
 import android.telephony.data.DataProfile;
+import android.telephony.data.DataService;
 import android.text.TextUtils;
 import android.util.LocalLog;
 import android.util.Pair;
@@ -61,6 +62,7 @@
 import com.android.internal.telephony.RetryManager;
 import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.metrics.TelephonyMetrics;
 import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Protocol;
@@ -527,7 +529,10 @@
         boolean allowRoaming = mPhone.getDataRoamingEnabled()
                 || (isModemRoaming && !mPhone.getServiceState().getDataRoaming());
 
-        mPhone.mCi.setupDataCall(cp.mRilRat, dp, isModemRoaming, allowRoaming, msg);
+        mPhone.mCi.setupDataCall(ServiceState.rilRadioTechnologyToAccessNetworkType(cp.mRilRat), dp,
+                isModemRoaming, allowRoaming, DataService.REQUEST_REASON_NORMAL, null, msg);
+        TelephonyMetrics.getInstance().writeSetupDataCall(mPhone.getPhoneId(), cp.mRilRat,
+                dp.getProfileId(), dp.getApn(), dp.getProtocol());
     }
 
     public void onSubscriptionOverride(int overrideMask, int overrideValue) {
@@ -544,15 +549,14 @@
      * @param o is the object returned in the AsyncResult.obj.
      */
     private void tearDownData(Object o) {
-        int discReason = RILConstants.DEACTIVATE_REASON_NONE;
+        int discReason = DataService.REQUEST_REASON_NORMAL;
         ApnContext apnContext = null;
         if ((o != null) && (o instanceof DisconnectParams)) {
             DisconnectParams dp = (DisconnectParams)o;
             apnContext = dp.mApnContext;
-            if (TextUtils.equals(dp.mReason, Phone.REASON_RADIO_TURNED_OFF)) {
-                discReason = RILConstants.DEACTIVATE_REASON_RADIO_OFF;
-            } else if (TextUtils.equals(dp.mReason, Phone.REASON_PDP_RESET)) {
-                discReason = RILConstants.DEACTIVATE_REASON_PDP_RESET;
+            if (TextUtils.equals(dp.mReason, Phone.REASON_RADIO_TURNED_OFF)
+                    || TextUtils.equals(dp.mReason, Phone.REASON_PDP_RESET)) {
+                discReason = DataService.REQUEST_REASON_SHUTDOWN;
             }
         }
 
@@ -1332,12 +1336,10 @@
                     }
                     deferMessage(msg);
                     break;
-
                 case EVENT_TEAR_DOWN_NOW:
                     if (DBG) log("DcDefaultState EVENT_TEAR_DOWN_NOW");
-                    mPhone.mCi.deactivateDataCall(mCid, 0,  null);
+                    mPhone.mCi.deactivateDataCall(mCid, DataService.REQUEST_REASON_NORMAL,  null);
                     break;
-
                 case EVENT_LOST_CONNECTION:
                     if (DBG) {
                         String s = "DcDefaultState ignore EVENT_LOST_CONNECTION"
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
index c27d706..dd20592 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -34,13 +34,11 @@
 import android.net.LinkProperties;
 import android.net.NetworkCapabilities;
 import android.net.NetworkConfig;
-import android.net.NetworkInfo;
 import android.net.NetworkRequest;
 import android.net.NetworkUtils;
 import android.net.ProxyInfo;
 import android.net.TrafficStats;
 import android.net.Uri;
-import android.net.wifi.WifiManager;
 import android.os.AsyncResult;
 import android.os.Build;
 import android.os.Bundle;
@@ -245,24 +243,6 @@
             } else if (action.equals(INTENT_PROVISIONING_APN_ALARM)) {
                 if (DBG) log("Provisioning apn alarm");
                 onActionIntentProvisioningApnAlarm(intent);
-            } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
-                final android.net.NetworkInfo networkInfo = (NetworkInfo)
-                intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
-                mIsWifiConnected = (networkInfo != null && networkInfo.isConnected());
-                if (DBG) log("NETWORK_STATE_CHANGED_ACTION: mIsWifiConnected=" + mIsWifiConnected);
-            } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
-                if (DBG) log("Wifi state changed");
-                final boolean enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
-                        WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
-                if (!enabled) {
-                    // when WiFi got disabled, the NETWORK_STATE_CHANGED_ACTION
-                    // quit and won't report disconnected until next enabling.
-                    mIsWifiConnected = false;
-                }
-                if (DBG) {
-                    log("WIFI_STATE_CHANGED_ACTION: enabled=" + enabled
-                            + " mIsWifiConnected=" + mIsWifiConnected);
-                }
             } else if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
                 if (mIccRecords.get() != null && mIccRecords.get().getRecordsLoaded()) {
                     setDefaultDataRoamingEnabled();
@@ -488,9 +468,6 @@
     // True when in voice call
     private boolean mInVoiceCall = false;
 
-    // wifi connection status will be updated by sticky intent
-    private boolean mIsWifiConnected = false;
-
     /** Intent sent when the reconnect alarm fires. */
     private PendingIntent mReconnectIntent = null;
 
@@ -607,8 +584,6 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_SCREEN_ON);
         filter.addAction(Intent.ACTION_SCREEN_OFF);
-        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
-        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
         filter.addAction(INTENT_DATA_STALL_ALARM);
         filter.addAction(INTENT_PROVISIONING_APN_ALARM);
         filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
@@ -4171,7 +4146,6 @@
         pw.println(" mSentSinceLastRecv=" + mSentSinceLastRecv);
         pw.println(" mNoRecvPollCount=" + mNoRecvPollCount);
         pw.println(" mResolver=" + mResolver);
-        pw.println(" mIsWifiConnected=" + mIsWifiConnected);
         pw.println(" mReconnectIntent=" + mReconnectIntent);
         pw.println(" mAutoAttachOnCreation=" + mAutoAttachOnCreation.get());
         pw.println(" mIsScreenOn=" + mIsScreenOn);
diff --git a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index 8f18c61..6663a73 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -20,36 +20,33 @@
 import android.app.PendingIntent;
 import android.app.PendingIntent.CanceledException;
 import android.content.Intent;
-import android.net.Uri;
 import android.os.AsyncResult;
 import android.os.Message;
 import android.provider.Telephony.Sms;
 import android.provider.Telephony.Sms.Intents;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
+import android.util.Pair;
 
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.telephony.GsmAlphabet;
-import com.android.internal.telephony.ImsSMSDispatcher;
+import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
 import com.android.internal.telephony.InboundSmsHandler;
 import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.SMSDispatcher;
 import com.android.internal.telephony.SmsConstants;
+import com.android.internal.telephony.SmsDispatchersController;
+import com.android.internal.telephony.SMSDispatcher;
 import com.android.internal.telephony.SmsHeader;
-import com.android.internal.telephony.SmsUsageMonitor;
+import com.android.internal.telephony.SmsMessageBase;
 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.util.SMSDispatcherUtil;
 
 import java.util.HashMap;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 
 public final class GsmSMSDispatcher extends SMSDispatcher {
     private static final String TAG = "GsmSMSDispatcher";
-    private static final boolean VDBG = false;
     protected UiccController mUiccController = null;
     private AtomicReference<IccRecords> mIccRecords = new AtomicReference<IccRecords>();
     private AtomicReference<UiccCardApplication> mUiccApplication =
@@ -59,10 +56,9 @@
     /** Status report received */
     private static final int EVENT_NEW_SMS_STATUS_REPORT = 100;
 
-    public GsmSMSDispatcher(Phone phone, SmsUsageMonitor usageMonitor,
-            ImsSMSDispatcher imsSMSDispatcher,
+    public GsmSMSDispatcher(Phone phone, SmsDispatchersController smsDispatchersController,
             GsmInboundSmsHandler gsmInboundSmsHandler) {
-        super(phone, usageMonitor, imsSMSDispatcher);
+        super(phone, smsDispatchersController);
         mCi.setOnSmsStatus(this, EVENT_NEW_SMS_STATUS_REPORT, null);
         mGsmInboundSmsHandler = gsmInboundSmsHandler;
         mUiccController = UiccController.getInstance();
@@ -109,6 +105,29 @@
         }
     }
 
+    @Override
+    protected boolean shouldBlockSms() {
+        return SMSDispatcherUtil.shouldBlockSms(isCdmaMo(), mPhone);
+    }
+
+    @Override
+    protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+            String message, boolean statusReportRequested, SmsHeader smsHeader) {
+        return SMSDispatcherUtil.getSubmitPduGsm(scAddr, destAddr, message, statusReportRequested);
+    }
+
+    @Override
+    protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
+            int destPort, byte[] message, boolean statusReportRequested) {
+        return SMSDispatcherUtil.getSubmitPduGsm(scAddr, destAddr, destPort, message,
+                statusReportRequested);
+    }
+
+    @Override
+    protected TextEncodingDetails calculateLength(CharSequence messageBody, boolean use7bitOnly) {
+        return SMSDispatcherUtil.calculateLengthGsm(messageBody, use7bitOnly);
+    }
+
     /**
      * Called when a status report is received.  This should correspond to
      * a previously successful SEND.
@@ -121,25 +140,17 @@
         SmsMessage sms = SmsMessage.newFromCDS(pdu);
 
         if (sms != null) {
-            int tpStatus = sms.getStatus();
             int messageRef = sms.mMessageRef;
             for (int i = 0, count = deliveryPendingList.size(); i < count; i++) {
                 SmsTracker tracker = deliveryPendingList.get(i);
                 if (tracker.mMessageRef == messageRef) {
-                    // Found it.  Remove from list and broadcast.
-                    if(tpStatus >= Sms.STATUS_FAILED || tpStatus < Sms.STATUS_PENDING ) {
-                       deliveryPendingList.remove(i);
-                       // Update the message status (COMPLETE or FAILED)
-                       tracker.updateSentMessageStatus(mContext, tpStatus);
+                    Pair<Boolean, Boolean> result = mSmsDispatchersController.handleSmsStatusReport(
+                            tracker,
+                            getFormat(),
+                            pdu);
+                    if (result.second) {
+                        deliveryPendingList.remove(i);
                     }
-                    PendingIntent intent = tracker.mDeliveryIntent;
-                    Intent fillIn = new Intent();
-                    fillIn.putExtra("pdu", pdu);
-                    fillIn.putExtra("format", getFormat());
-                    try {
-                        intent.send(mContext, Activity.RESULT_OK, fillIn);
-                    } catch (CanceledException ex) {}
-
                     // Only expect to see one tracker matching this messageref
                     break;
                 }
@@ -150,101 +161,6 @@
 
     /** {@inheritDoc} */
     @Override
-    protected void sendData(String destAddr, String scAddr, int destPort,
-            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
-        SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
-                scAddr, destAddr, destPort, data, (deliveryIntent != null));
-        if (pdu != null) {
-            HashMap map = getSmsTrackerMap(destAddr, scAddr, destPort, data, pdu);
-            SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
-                    null /*messageUri*/, false /*isExpectMore*/, null /*fullMessageText*/,
-                    false /*isText*/, true /*persistMessage*/);
-
-            String carrierPackage = getCarrierAppPackageName();
-            if (carrierPackage != null) {
-                Rlog.d(TAG, "Found carrier package.");
-                DataSmsSender smsSender = new DataSmsSender(tracker);
-                smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender));
-            } else {
-                Rlog.v(TAG, "No carrier package.");
-                sendRawPdu(tracker);
-            }
-        } else {
-            Rlog.e(TAG, "GsmSMSDispatcher.sendData(): getSubmitPdu() returned null");
-        }
-    }
-
-    /** {@inheritDoc} */
-    @VisibleForTesting
-    @Override
-    public void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent,
-            PendingIntent deliveryIntent, Uri messageUri, String callingPkg,
-            boolean persistMessage) {
-        SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
-                scAddr, destAddr, text, (deliveryIntent != null));
-        if (pdu != null) {
-            HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu);
-            SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
-                    messageUri, false /*isExpectMore*/, text /*fullMessageText*/, true /*isText*/,
-                    persistMessage);
-
-            String carrierPackage = getCarrierAppPackageName();
-            if (carrierPackage != null) {
-                Rlog.d(TAG, "Found carrier package.");
-                TextSmsSender smsSender = new TextSmsSender(tracker);
-                smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender));
-            } else {
-                Rlog.v(TAG, "No carrier package.");
-                sendRawPdu(tracker);
-            }
-        } else {
-            Rlog.e(TAG, "GsmSMSDispatcher.sendText(): getSubmitPdu() returned null");
-        }
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    protected void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) {
-        throw new IllegalStateException("This method must be called only on ImsSMSDispatcher");
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    protected GsmAlphabet.TextEncodingDetails calculateLength(CharSequence messageBody,
-            boolean use7bitOnly) {
-        return SmsMessage.calculateLength(messageBody, use7bitOnly);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    protected SmsTracker getNewSubmitPduTracker(String destinationAddress, String scAddress,
-            String message, SmsHeader smsHeader, int encoding,
-            PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart,
-            AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
-            String fullMessageText) {
-        SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(scAddress, destinationAddress,
-                message, deliveryIntent != null, SmsHeader.toByteArray(smsHeader),
-                encoding, smsHeader.languageTable, smsHeader.languageShiftTable);
-        if (pdu != null) {
-            HashMap map =  getSmsTrackerMap(destinationAddress, scAddress,
-                    message, pdu);
-            return getSmsTracker(map, sentIntent,
-                    deliveryIntent, getFormat(), unsentPartCount, anyPartFailed, messageUri,
-                    smsHeader, !lastPart, fullMessageText, true /*isText*/,
-                    false /*persistMessage*/);
-        } else {
-            Rlog.e(TAG, "GsmSMSDispatcher.sendNewSubmitPdu(): getSubmitPdu() returned null");
-            return null;
-        }
-    }
-
-    @Override
-    protected void sendSubmitPdu(SmsTracker tracker) {
-        sendRawPdu(tracker);
-    }
-
-    /** {@inheritDoc} */
-    @Override
     protected void sendSms(SmsTracker tracker) {
         HashMap<String, Object> map = tracker.getData();
 
@@ -271,12 +187,6 @@
                 + " mMessageRef=" + tracker.mMessageRef
                 + " SS=" + mPhone.getServiceState().getState());
 
-        sendSmsByPstn(tracker);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    protected void sendSmsByPstn(SmsTracker tracker) {
         int 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) {
@@ -284,10 +194,7 @@
             return;
         }
 
-        HashMap<String, Object> map = tracker.getData();
-
         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:
@@ -295,15 +202,6 @@
         //   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
-                }
-            }
             if (tracker.mRetryCount == 0 && tracker.mExpectMore) {
                 mCi.sendSMSExpectMore(IccUtils.bytesToHexString(smsc),
                         IccUtils.bytesToHexString(pdu), reply);
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index 53e92f2..03b280e 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -956,6 +956,12 @@
     }
 
     public void getCallBarring(String facility, Message onComplete, int serviceClass) {
+        getCallBarring(facility, "", onComplete, serviceClass);
+    }
+
+    @Override
+    public void getCallBarring(String facility, String password, Message onComplete,
+            int serviceClass) {
         if (DBG) {
             Rlog.d(LOG_TAG, "getCallBarring facility=" + facility
                     + ", serviceClass = " + serviceClass);
@@ -965,6 +971,7 @@
 
         try {
             ImsUtInterface ut = mCT.getUtInterface();
+            // password is not required with Ut interface
             ut.queryCallBarring(getCBTypeFromFacility(facility), resp, serviceClass);
         } catch (ImsException e) {
             sendErrorResponse(onComplete, e);
@@ -977,6 +984,7 @@
                 CommandsInterface.SERVICE_CLASS_NONE);
     }
 
+    @Override
     public void setCallBarring(String facility, boolean lockState, String password,
             Message onComplete,  int serviceClass) {
         if (DBG) {
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
index a54190e..d5519dd 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
@@ -546,6 +546,16 @@
     }
 
     @Override
+    public void getCallBarring(String facility, String password, Message onComplete,
+            int serviceClass) {
+    }
+
+    @Override
+    public void setCallBarring(String facility, boolean lockState, String password,
+            Message onComplete, int serviceClass) {
+    }
+
+    @Override
     protected void onUpdateIccAvailability() {
     }
 
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
index 770fbb1..76cc26d 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.net.KeepalivePacketData;
+import android.net.LinkProperties;
 import android.os.Handler;
 import android.os.Message;
 import android.service.carrier.CarrierIdentifier;
@@ -269,8 +270,9 @@
     }
 
     @Override
-    public void setupDataCall(int radioTechnology, DataProfile dataProfile, boolean isRoaming,
-                              boolean allowRoaming, Message result) {
+    public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
+                              boolean allowRoaming, int reason, LinkProperties linkProperties,
+                              Message result) {
     }
 
     @Override
diff --git a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
index 9eb1666..a0c5630 100644
--- a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
+++ b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
@@ -17,7 +17,6 @@
 package com.android.internal.telephony.metrics;
 
 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
-
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_ANSWER;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_CDMA_SEND_SMS;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DEACTIVATE_DATA_CALL;
@@ -42,6 +41,7 @@
 import android.telephony.TelephonyHistogram;
 import android.telephony.TelephonyManager;
 import android.telephony.data.DataCallResponse;
+import android.telephony.data.DataService;
 import android.text.TextUtils;
 import android.util.Base64;
 import android.util.SparseArray;
@@ -70,10 +70,10 @@
 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.CarrierIdMatchingResult;
 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.ModemRestart;
 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilDeactivateDataCall;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilDeactivateDataCall.DeactivateReason;
 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCall;
 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse;
-import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse
-        .RilDataCallFailCause;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse.RilDataCallFailCause;
 import com.android.internal.telephony.nano.TelephonyProto.TelephonyLog;
 import com.android.internal.telephony.nano.TelephonyProto.TelephonyServiceState;
 import com.android.internal.telephony.nano.TelephonyProto.TelephonySettings;
@@ -1009,19 +1009,17 @@
      * Write setup data call event
      *
      * @param phoneId Phone id
-     * @param rilSerial RIL request serial number
      * @param radioTechnology The data call RAT
-     * @param profile Data profile
+     * @param profileId Data profile id
      * @param apn APN in string
-     * @param authType Authentication type
      * @param protocol Data connection protocol
      */
-    public void writeRilSetupDataCall(int phoneId, int rilSerial, int radioTechnology, int profile,
-                                      String apn, int authType, String protocol) {
+    public void writeSetupDataCall(int phoneId, int radioTechnology, int profileId, String apn,
+                                   String protocol) {
 
         RilSetupDataCall setupDataCall = new RilSetupDataCall();
         setupDataCall.rat = radioTechnology;
-        setupDataCall.dataProfile = profile + 1;  // off by 1 between proto and RIL constants.
+        setupDataCall.dataProfile = profileId + 1;  // off by 1 between proto and RIL constants.
         if (apn != null) {
             setupDataCall.apn = apn;
         }
@@ -1045,7 +1043,19 @@
 
         RilDeactivateDataCall deactivateDataCall = new RilDeactivateDataCall();
         deactivateDataCall.cid = cid;
-        deactivateDataCall.reason = reason + 1;
+        switch (reason) {
+            case DataService.REQUEST_REASON_NORMAL:
+                deactivateDataCall.reason = DeactivateReason.DEACTIVATE_REASON_NONE;
+                break;
+            case DataService.REQUEST_REASON_SHUTDOWN:
+                deactivateDataCall.reason = DeactivateReason.DEACTIVATE_REASON_RADIO_OFF;
+                break;
+            case DataService.REQUEST_REASON_HANDOVER:
+                deactivateDataCall.reason = DeactivateReason.DEACTIVATE_REASON_HANDOVER;
+                break;
+            default:
+                deactivateDataCall.reason = DeactivateReason.DEACTIVATE_REASON_UNKNOWN;
+        }
 
         addTelephonyEvent(new TelephonyEventBuilder(phoneId).setDeactivateDataCall(
                 deactivateDataCall).build());
diff --git a/src/java/com/android/internal/telephony/sip/SipCommandInterface.java b/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
index 9fd22ed..0cd8fab 100644
--- a/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
+++ b/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.net.KeepalivePacketData;
+import android.net.LinkProperties;
 import android.os.Handler;
 import android.os.Message;
 import android.service.carrier.CarrierIdentifier;
@@ -270,8 +271,9 @@
     }
 
     @Override
-    public void setupDataCall(int radioTechnology, DataProfile dataProfile, boolean isRoaming,
-                              boolean allowRoaming, Message result) {
+    public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
+                              boolean allowRoaming, int reason, LinkProperties linkProperties,
+                              Message result) {
     }
 
     @Override
diff --git a/src/java/com/android/internal/telephony/sip/SipPhoneBase.java b/src/java/com/android/internal/telephony/sip/SipPhoneBase.java
index c2160dd..ade427b 100755
--- a/src/java/com/android/internal/telephony/sip/SipPhoneBase.java
+++ b/src/java/com/android/internal/telephony/sip/SipPhoneBase.java
@@ -552,4 +552,14 @@
     @Override
     public void setBroadcastEmergencyCallStateChanges(boolean broadcast) {
     }
+
+    @Override
+    public void getCallBarring(String facility, String password, Message onComplete,
+            int serviceClass) {
+    }
+
+    @Override
+    public void setCallBarring(String facility, boolean lockState, String password,
+            Message onComplete, int serviceClass) {
+    }
 }
diff --git a/src/java/com/android/internal/telephony/test/SimulatedCommands.java b/src/java/com/android/internal/telephony/test/SimulatedCommands.java
index 277fbd6..ff8c0a8 100644
--- a/src/java/com/android/internal/telephony/test/SimulatedCommands.java
+++ b/src/java/com/android/internal/telephony/test/SimulatedCommands.java
@@ -20,6 +20,7 @@
 import android.hardware.radio.V1_0.VoiceRegStateResult;
 import android.net.KeepalivePacketData;
 import android.net.LinkAddress;
+import android.net.LinkProperties;
 import android.net.NetworkUtils;
 import android.os.AsyncResult;
 import android.os.Handler;
@@ -1123,11 +1124,12 @@
     }
 
     @Override
-    public void setupDataCall(int radioTechnology, DataProfile dataProfile, boolean isRoaming,
-                              boolean allowRoaming, Message result) {
+    public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
+                              boolean allowRoaming, int reason, LinkProperties linkProperties,
+                              Message result) {
 
-        SimulatedCommandsVerifier.getInstance().setupDataCall(radioTechnology, dataProfile,
-                isRoaming, allowRoaming, result);
+        SimulatedCommandsVerifier.getInstance().setupDataCall(accessNetworkType, dataProfile,
+                isRoaming, allowRoaming, reason, linkProperties, result);
 
         if (mDcResponse == null) {
             try {
diff --git a/src/java/com/android/internal/telephony/test/SimulatedCommandsVerifier.java b/src/java/com/android/internal/telephony/test/SimulatedCommandsVerifier.java
index 4a3939b..f060ef4 100644
--- a/src/java/com/android/internal/telephony/test/SimulatedCommandsVerifier.java
+++ b/src/java/com/android/internal/telephony/test/SimulatedCommandsVerifier.java
@@ -17,6 +17,7 @@
 package com.android.internal.telephony.test;
 
 import android.net.KeepalivePacketData;
+import android.net.LinkProperties;
 import android.os.Handler;
 import android.os.Message;
 import android.service.carrier.CarrierIdentifier;
@@ -1142,8 +1143,9 @@
     }
 
     @Override
-    public void setupDataCall(int radioTechnology, DataProfile dataProfile, boolean isRoaming,
-                              boolean allowRoaming, Message result) {
+    public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
+                              boolean allowRoaming, int reason, LinkProperties linkProperties,
+                              Message result) {
     }
 
     @Override
diff --git a/src/java/com/android/internal/telephony/uicc/IccCardProxy.java b/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
index 7c18d35..f9629e7 100644
--- a/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
+++ b/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
@@ -382,10 +382,6 @@
     }
 
     private void registerUiccCardEvents() {
-        if (mUiccSlot != null) {
-            // todo: reregistration is not needed unless slot mapping changes
-            mUiccSlot.registerForAbsent(this, EVENT_ICC_ABSENT, null);
-        }
         if (mUiccApplication != null) {
             mUiccApplication.registerForReady(this, EVENT_APP_READY, null);
         }
@@ -399,7 +395,6 @@
     }
 
     private void unregisterUiccCardEvents() {
-        if (mUiccSlot != null) mUiccSlot.unregisterForAbsent(this);
         if (mUiccCard != null) mUiccCard.unregisterForCarrierPrivilegeRulesLoaded(this);
         if (mUiccApplication != null) {
             mUiccApplication.unregisterForReady(this);
diff --git a/src/java/com/android/internal/telephony/uicc/UiccProfile.java b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
index a425d9e..cf32dd5 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccProfile.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
@@ -879,10 +879,9 @@
             }
         }
         if (VDBG) {
-            log("areAllApplicationsReady: outside loop, return " + (mUiccApplications[0] != null));
+            log("areAllApplicationsReady: outside loop, return " + (mUiccApplication != null));
         }
-        // Returns false if there is no application in the UiccProfile.
-        return mUiccApplications[0] != null;
+        return mUiccApplication != null;
     }
 
     private boolean areAllRecordsLoaded() {
@@ -896,10 +895,9 @@
             }
         }
         if (VDBG) {
-            log("areAllRecordsLoaded: outside loop, return " + (mUiccApplications[0] != null));
+            log("areAllRecordsLoaded: outside loop, return " + (mUiccApplication != null));
         }
-        // Returns false if there is no application in the UiccProfile.
-        return mUiccApplications[0] != null;
+        return mUiccApplication != null;
     }
 
     private int checkIndexLocked(int index, AppType expectedAppType, AppType altExpectedAppType) {
diff --git a/src/java/com/android/internal/telephony/uicc/UiccSlot.java b/src/java/com/android/internal/telephony/uicc/UiccSlot.java
index 4fabf0b..dd17b75 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccSlot.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccSlot.java
@@ -26,8 +26,6 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.PowerManager;
-import android.os.Registrant;
-import android.os.RegistrantList;
 import android.telephony.Rlog;
 import android.util.LocalLog;
 import android.view.WindowManager;
@@ -62,9 +60,6 @@
     private String mIccId;
     private Integer mPhoneId = null;
 
-    // todo: remove if this is not needed
-    private RegistrantList mAbsentRegistrants = new RegistrantList();
-
     private static final int EVENT_CARD_REMOVED = 13;
     private static final int EVENT_CARD_ADDED = 14;
 
@@ -98,7 +93,6 @@
                 // No notifications while radio is off or we just powering up
                 if (radioState == RadioState.RADIO_ON && mLastRadioState == RadioState.RADIO_ON) {
                     if (DBG) log("update: notify card removed");
-                    mAbsentRegistrants.notifyRegistrants();
                     sendMessage(obtainMessage(EVENT_CARD_REMOVED, null));
                 }
 
@@ -147,11 +141,10 @@
             if (iss.slotState == IccSlotStatus.SlotState.SLOTSTATE_INACTIVE) {
                 if (mActive) {
                     mActive = false;
-                    // treat as radio state unavailable
-                    onRadioStateUnavailable();
-                    // set mPhoneId to null only after sim state changed broadcast is sent as it
-                    // needs the phoneId. The broadcast is sent from onRadioStateUnavailable()
+                    mLastRadioState = RadioState.RADIO_UNAVAILABLE;
                     mPhoneId = null;
+                    if (mUiccCard != null) mUiccCard.dispose();
+                    mUiccCard = null;
                 }
                 parseAtr(iss.atr);
                 mCardState = iss.cardState;
@@ -321,30 +314,6 @@
         mLastRadioState = RadioState.RADIO_UNAVAILABLE;
     }
 
-    /**
-     * Notifies handler of any transition into State.ABSENT
-     */
-    public void registerForAbsent(Handler h, int what, Object obj) {
-        synchronized (mLock) {
-            Registrant r = new Registrant(h, what, obj);
-
-            mAbsentRegistrants.add(r);
-
-            if (mCardState == CardState.CARDSTATE_ABSENT) {
-                r.notifyRegistrant();
-            }
-        }
-    }
-
-    /**
-     * Unregister a handler for card absent notification
-     */
-    public void unregisterForAbsent(Handler h) {
-        synchronized (mLock) {
-            mAbsentRegistrants.remove(h);
-        }
-    }
-
     private void log(String msg) {
         Rlog.d(TAG, msg);
     }
@@ -365,11 +334,6 @@
         pw.println(" mCi=" + mCi);
         pw.println(" mActive=" + mActive);
         pw.println(" mLastRadioState=" + mLastRadioState);
-        pw.println(" mAbsentRegistrants: size=" + mAbsentRegistrants.size());
-        for (int i = 0; i < mAbsentRegistrants.size(); i++) {
-            pw.println("  mAbsentRegistrants[" + i + "]="
-                    + ((Registrant) mAbsentRegistrants.get(i)).getHandler());
-        }
         pw.println(" mCardState=" + mCardState);
         if (mUiccCard != null) {
             pw.println(" mUiccCard=" + mUiccCard);
diff --git a/src/java/com/android/internal/telephony/util/SMSDispatcherUtil.java b/src/java/com/android/internal/telephony/util/SMSDispatcherUtil.java
new file mode 100644
index 0000000..d2ee223
--- /dev/null
+++ b/src/java/com/android/internal/telephony/util/SMSDispatcherUtil.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.internal.telephony.util;
+
+import com.android.internal.telephony.ImsSmsDispatcher;
+import com.android.internal.telephony.cdma.CdmaSMSDispatcher;
+import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.SmsMessageBase;
+import com.android.internal.telephony.SmsHeader;
+import com.android.internal.telephony.gsm.GsmSMSDispatcher;
+
+/**
+ * Utilities used by {@link com.android.internal.telephony.SMSDispatcher}'s subclasses.
+ *
+ * These methods can not be moved to {@link CdmaSMSDispatcher} and {@link GsmSMSDispatcher} because
+ * they also need to be called from {@link ImsSmsDispatcher} and the utilities will invoke the cdma
+ * or gsm version of the method based on the format.
+ */
+public final class SMSDispatcherUtil {
+    // Prevent instantiation.
+    private SMSDispatcherUtil() {}
+
+    /**
+     * Whether to block SMS or not.
+     *
+     * @param isCdma true if cdma format should be used.
+     * @param phone the Phone to use
+     * @return true to block sms; false otherwise.
+     */
+    public static boolean shouldBlockSms(boolean isCdma, Phone phone) {
+        return isCdma && phone.isInEcm();
+    }
+
+    /**
+     * Trigger the proper implementation for getting submit pdu for text sms based on format.
+     *
+     * @param isCdma true if cdma format should be used.
+     * @param scAddr is the service center address or null to use the current default SMSC
+     * @param destAddr the address to send the message to
+     * @param message the body of the message.
+     * @param statusReportRequested whether or not a status report is requested.
+     * @param smsHeader message header.
+     * @return the submit pdu.
+     */
+    public static SmsMessageBase.SubmitPduBase getSubmitPdu(boolean isCdma, String scAddr,
+            String destAddr, String message, boolean statusReportRequested, SmsHeader smsHeader) {
+        if (isCdma) {
+            return getSubmitPduCdma(scAddr, destAddr, message, statusReportRequested, smsHeader);
+        } else {
+            return getSubmitPduGsm(scAddr, destAddr, message, statusReportRequested);
+        }
+    }
+
+    /**
+     * Gsm implementation for
+     * {@link #getSubmitPdu(boolean, String, String, String, boolean, SmsHeader)}
+     *
+     * @param scAddr is the service center address or null to use the current default SMSC
+     * @param destAddr the address to send the message to
+     * @param message the body of the message.
+     * @param statusReportRequested whether or not a status report is requested.
+     * @return the submit pdu.
+     */
+    public static SmsMessageBase.SubmitPduBase getSubmitPduGsm(String scAddr, String destAddr,
+            String message, boolean statusReportRequested) {
+        return com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddr, destAddr, message,
+                statusReportRequested);
+    }
+
+    /**
+     * Cdma implementation for
+     * {@link #getSubmitPdu(boolean, String, String, String, boolean, SmsHeader)}
+     *
+     *  @param scAddr is the service center address or null to use the current default SMSC
+     * @param destAddr the address to send the message to
+     * @param message the body of the message.
+     * @param statusReportRequested whether or not a status report is requested.
+     * @param smsHeader message header.
+     * @return the submit pdu.
+     */
+    public static SmsMessageBase.SubmitPduBase getSubmitPduCdma(String scAddr, String destAddr,
+            String message, boolean statusReportRequested, SmsHeader smsHeader) {
+        return com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddr, destAddr,
+                message, statusReportRequested, smsHeader);
+    }
+
+    /**
+     * Trigger the proper implementation for getting submit pdu for data sms based on format.
+     *
+     * @param isCdma true if cdma format should be used.
+     * @param destAddr the address to send the message to
+     * @param scAddr is the service center address or null to use the current default SMSC
+     * @param destPort the port to deliver the message to
+     * @param message the body of the message to send
+     * @param statusReportRequested whether or not a status report is requested.
+     * @return the submit pdu.
+     */
+    public static SmsMessageBase.SubmitPduBase getSubmitPdu(boolean isCdma, String scAddr,
+            String destAddr, int destPort, byte[] message, boolean statusReportRequested) {
+        if (isCdma) {
+            return getSubmitPduCdma(scAddr, destAddr, destPort, message, statusReportRequested);
+        } else {
+            return getSubmitPduGsm(scAddr, destAddr, destPort, message, statusReportRequested);
+        }
+    }
+
+    /**
+     * Cdma implementation of {@link #getSubmitPdu(boolean, String, String, int, byte[], boolean)}
+
+     * @param destAddr the address to send the message to
+     * @param scAddr is the service center address or null to use the current default SMSC
+     * @param destPort the port to deliver the message to
+     * @param message the body of the message to send
+     * @param statusReportRequested whether or not a status report is requested.
+     * @return the submit pdu.
+     */
+    public static SmsMessageBase.SubmitPduBase getSubmitPduCdma(String scAddr, String destAddr,
+            int destPort, byte[] message, boolean statusReportRequested) {
+        return com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddr, destAddr,
+                destPort, message, statusReportRequested);
+    }
+
+    /**
+     * Gsm implementation of {@link #getSubmitPdu(boolean, String, String, int, byte[], boolean)}
+     *
+     * @param destAddr the address to send the message to
+     * @param scAddr is the service center address or null to use the current default SMSC
+     * @param destPort the port to deliver the message to
+     * @param message the body of the message to send
+     * @param statusReportRequested whether or not a status report is requested.
+     * @return the submit pdu.
+     */
+    public static SmsMessageBase.SubmitPduBase getSubmitPduGsm(String scAddr, String destAddr,
+            int destPort, byte[] message, boolean statusReportRequested) {
+        return com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddr, destAddr,
+                destPort, message, statusReportRequested);
+
+    }
+
+    /**
+     * Calculate the number of septets needed to encode the message. This function should only be
+     * called for individual segments of multipart message.
+     *
+     * @param isCdma  true if cdma format should be used.
+     * @param messageBody the message to encode
+     * @param use7bitOnly ignore (but still count) illegal characters if true
+     * @return TextEncodingDetails
+     */
+    public static TextEncodingDetails calculateLength(boolean isCdma, CharSequence messageBody,
+            boolean use7bitOnly) {
+        if (isCdma) {
+            return calculateLengthCdma(messageBody, use7bitOnly);
+        } else {
+            return calculateLengthGsm(messageBody, use7bitOnly);
+        }
+    }
+
+    /**
+     * Gsm implementation for {@link #calculateLength(boolean, CharSequence, boolean)}
+     *
+     * @param messageBody the message to encode
+     * @param use7bitOnly ignore (but still count) illegal characters if true
+     * @return TextEncodingDetails
+     */
+    public static TextEncodingDetails calculateLengthGsm(CharSequence messageBody,
+            boolean use7bitOnly) {
+        return com.android.internal.telephony.cdma.SmsMessage.calculateLength(messageBody,
+                use7bitOnly, false);
+
+    }
+
+    /**
+     * Cdma implementation for {@link #calculateLength(boolean, CharSequence, boolean)}
+     *
+     * @param messageBody the message to encode
+     * @param use7bitOnly ignore (but still count) illegal characters if true
+     * @return TextEncodingDetails
+     */
+    public static TextEncodingDetails calculateLengthCdma(CharSequence messageBody,
+            boolean use7bitOnly) {
+        return com.android.internal.telephony.gsm.SmsMessage.calculateLength(messageBody,
+                use7bitOnly);
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java
new file mode 100644
index 0000000..f052a9a
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.internal.telephony;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.os.HandlerThread;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+public class ImsSmsDispatcherTest extends TelephonyTest {
+    @Mock private SmsDispatchersController mSmsDispatchersController;
+    @Mock private SMSDispatcher.SmsTracker mSmsTracker;
+    private ImsSmsDispatcher mImsSmsDispatcher;
+    private ImsSmsDispatcherTestHandler mImsSmsDispatcherTestHandler;
+
+
+    private class ImsSmsDispatcherTestHandler extends HandlerThread {
+
+        private ImsSmsDispatcherTestHandler(String name) {
+            super(name);
+        }
+
+        @Override
+        public void onLooperPrepared() {
+            mImsSmsDispatcher = spy(new ImsSmsDispatcher(mPhone, mSmsDispatchersController));
+            setReady(true);
+        }
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp(getClass().getSimpleName());
+
+        mImsSmsDispatcherTestHandler = new ImsSmsDispatcherTestHandler(TAG);
+        mImsSmsDispatcherTestHandler.start();
+        waitUntilReady();
+    }
+
+    @Test @SmallTest
+    public void testSendSms() {
+        int token = mImsSmsDispatcher.mNextToken.get();
+        int trackersSize = mImsSmsDispatcher.mTrackers.size();
+
+        mImsSmsDispatcher.sendSms(mSmsTracker);
+
+        assertEquals(token + 1, mImsSmsDispatcher.mNextToken.get());
+        assertEquals(trackersSize + 1, mImsSmsDispatcher.mTrackers.size());
+    }
+
+    @Test @SmallTest
+    public void testFallbackToPstn() {
+        int token = 0;
+        mImsSmsDispatcher.mTrackers.put(token, mSmsTracker);
+        doNothing().when(mSmsDispatchersController).sendRetrySms(mSmsTracker);
+
+        mImsSmsDispatcher.fallbackToPstn(token, mSmsTracker);
+
+        verify(mSmsDispatchersController).sendRetrySms(mSmsTracker);
+        assertNull(mImsSmsDispatcher.mTrackers.get(token));
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mImsSmsDispatcher = null;
+        mImsSmsDispatcherTestHandler.quit();
+        super.tearDown();
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/NetworkScanResultTest.java b/tests/telephonytests/src/com/android/internal/telephony/NetworkScanResultTest.java
index f149211..47a9979 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/NetworkScanResultTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/NetworkScanResultTest.java
@@ -42,8 +42,7 @@
         ArrayList<CellInfo> infos = new ArrayList<CellInfo>();
 
         CellIdentityGsm cig = new CellIdentityGsm(310, 310, 1, 2, 3, 4);
-        CellSignalStrengthGsm cssg = new CellSignalStrengthGsm();
-        cssg.initialize(5, 6, 7);
+        CellSignalStrengthGsm cssg = new CellSignalStrengthGsm(5, 6, 7);
         CellInfoGsm gsm = new CellInfoGsm();
         gsm.setRegistered(true);
         gsm.setTimeStampType(8);
@@ -53,8 +52,7 @@
         infos.add(gsm);
 
         CellIdentityLte cil = new CellIdentityLte(320, 320, 11, 12, 13, 14);
-        CellSignalStrengthLte cssl = new CellSignalStrengthLte();
-        cssl.initialize(15, 16, 17, 18, 19, 20);
+        CellSignalStrengthLte cssl = new CellSignalStrengthLte(15, 16, 17, 18, 19, 20);
         CellInfoLte lte = new CellInfoLte();
         lte.setRegistered(false);
         lte.setTimeStampType(21);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
index 753e03c..d877ac4 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
@@ -1119,8 +1119,8 @@
         expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
         CellIdentityGsm ci = new CellIdentityGsm(
                 LAC, CID, ARFCN, BSIC, MCC_STR, MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
-        CellSignalStrengthGsm cs = new CellSignalStrengthGsm();
-        cs.initialize(SIGNAL_STRENGTH, BIT_ERROR_RATE, TIME_ADVANCE);
+        CellSignalStrengthGsm cs = new CellSignalStrengthGsm(
+                SIGNAL_STRENGTH, BIT_ERROR_RATE, TIME_ADVANCE);
         expected.setCellIdentity(ci);
         expected.setCellSignalStrength(cs);
         expected.setCellConnectionStatus(CellInfo.CONNECTION_UNKNOWN);
@@ -1285,8 +1285,8 @@
         expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
         CellIdentityGsm ci = new CellIdentityGsm(
                 LAC, CID, ARFCN, BSIC, MCC_STR, MNC_STR, ALPHA_LONG, ALPHA_SHORT);
-        CellSignalStrengthGsm cs = new CellSignalStrengthGsm();
-        cs.initialize(SIGNAL_STRENGTH, BIT_ERROR_RATE, TIME_ADVANCE);
+        CellSignalStrengthGsm cs = new CellSignalStrengthGsm(
+                SIGNAL_STRENGTH, BIT_ERROR_RATE, TIME_ADVANCE);
         expected.setCellIdentity(ci);
         expected.setCellSignalStrength(cs);
         expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
@@ -1306,8 +1306,8 @@
         expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
         CellIdentityGsm ci = new CellIdentityGsm(
                 LAC, CID, ARFCN, BSIC, MCC_STR, MNC_STR, EMPTY_ALPHA_LONG, EMPTY_ALPHA_SHORT);
-        CellSignalStrengthGsm cs = new CellSignalStrengthGsm();
-        cs.initialize(SIGNAL_STRENGTH, BIT_ERROR_RATE, TIME_ADVANCE);
+        CellSignalStrengthGsm cs = new CellSignalStrengthGsm(
+                SIGNAL_STRENGTH, BIT_ERROR_RATE, TIME_ADVANCE);
         expected.setCellIdentity(ci);
         expected.setCellSignalStrength(cs);
         expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
@@ -1329,8 +1329,8 @@
         expected.setTimeStampType(RIL_TIMESTAMP_TYPE_OEM_RIL);
         CellIdentityGsm ci = new CellIdentityGsm(
                 LAC, CID, ARFCN, BSIC, null, null, ALPHA_LONG, ALPHA_SHORT);
-        CellSignalStrengthGsm cs = new CellSignalStrengthGsm();
-        cs.initialize(SIGNAL_STRENGTH, BIT_ERROR_RATE, TIME_ADVANCE);
+        CellSignalStrengthGsm cs = new CellSignalStrengthGsm(
+                SIGNAL_STRENGTH, BIT_ERROR_RATE, TIME_ADVANCE);
         expected.setCellIdentity(ci);
         expected.setCellConnectionStatus(CellInfo.CONNECTION_NONE);
         expected.setCellSignalStrength(cs);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ImsSMSDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
similarity index 78%
rename from tests/telephonytests/src/com/android/internal/telephony/ImsSMSDispatcherTest.java
rename to tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
index 105e0f8..ea40ea8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ImsSMSDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SmsDispatchersControllerTest.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.isNull;
@@ -38,6 +39,7 @@
 import android.content.IntentFilter;
 import android.os.HandlerThread;
 import android.os.Message;
+import android.provider.Telephony.Sms.Intents;
 import android.test.FlakyTest;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Singleton;
@@ -49,24 +51,14 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
-public class ImsSMSDispatcherTest extends TelephonyTest {
+public class SmsDispatchersControllerTest extends TelephonyTest {
     @Mock
     private SMSDispatcher.SmsTracker mTracker;
 
-    private ImsSMSDispatcher mImsSmsDispatcher;
+    private SmsDispatchersController mSmsDispatchersController;
     private ImsSmsDispatcherTestHandler mImsSmsDispatcherTestHandler;
-    private boolean mReceivedTestIntent = false;
-    private Object mLock = new Object();
+    private boolean mInjectionCallbackTriggered = false;
     private static final String TEST_INTENT = "com.android.internal.telephony.TEST_INTENT";
-    private BroadcastReceiver mTestReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            logd("onReceive");
-            synchronized (mLock) {
-                mReceivedTestIntent = true;
-            }
-        }
-    };
 
     private class ImsSmsDispatcherTestHandler extends HandlerThread {
 
@@ -76,7 +68,7 @@
 
         @Override
         public void onLooperPrepared() {
-            mImsSmsDispatcher = new ImsSMSDispatcher(mPhone, mSmsStorageMonitor,
+            mSmsDispatchersController = new SmsDispatchersController(mPhone, mSmsStorageMonitor,
                     mSmsUsageMonitor);
             //Initial state of RIL is power on, need to wait util RADIO_ON msg get handled
             waitForMs(200);
@@ -96,29 +88,29 @@
 
     @After
     public void tearDown() throws Exception {
-        mImsSmsDispatcher = null;
+        mSmsDispatchersController = null;
         mImsSmsDispatcherTestHandler.quit();
         super.tearDown();
     }
 
     @Test @SmallTest @FlakyTest @Ignore
     public void testSmsHandleStateUpdate() throws Exception {
-        assertEquals(SmsConstants.FORMAT_UNKNOWN, mImsSmsDispatcher.getImsSmsFormat());
+        assertEquals(SmsConstants.FORMAT_UNKNOWN, mSmsDispatchersController.getImsSmsFormat());
         //Mock ImsNetWorkStateChange with GSM phone type
         switchImsSmsFormat(PhoneConstants.PHONE_TYPE_GSM);
-        assertEquals(SmsConstants.FORMAT_3GPP, mImsSmsDispatcher.getImsSmsFormat());
-        assertTrue(mImsSmsDispatcher.isIms());
+        assertEquals(SmsConstants.FORMAT_3GPP, mSmsDispatchersController.getImsSmsFormat());
+        assertTrue(mSmsDispatchersController.isIms());
 
         //Mock ImsNetWorkStateChange with Cdma Phone type
         switchImsSmsFormat(PhoneConstants.PHONE_TYPE_CDMA);
-        assertEquals(SmsConstants.FORMAT_3GPP2, mImsSmsDispatcher.getImsSmsFormat());
-        assertTrue(mImsSmsDispatcher.isIms());
+        assertEquals(SmsConstants.FORMAT_3GPP2, mSmsDispatchersController.getImsSmsFormat());
+        assertTrue(mSmsDispatchersController.isIms());
     }
 
     @Test @SmallTest @FlakyTest @Ignore
     public void testSendImsGmsTest() throws Exception {
         switchImsSmsFormat(PhoneConstants.PHONE_TYPE_GSM);
-        mImsSmsDispatcher.sendText("111"/* desAddr*/, "222" /*scAddr*/, TAG,
+        mSmsDispatchersController.sendText("111"/* desAddr*/, "222" /*scAddr*/, TAG,
                 null, null, null, null, false);
         verify(mSimulatedCommandsVerifier).sendImsGsmSms(eq("038122f2"),
                 eq("0100038111f1000014c9f67cda9c12d37378983e4697e5d4f29c0e"), eq(0), eq(0),
@@ -128,7 +120,7 @@
     @Test @SmallTest
     public void testSendImsGmsTestWithOutDesAddr() throws Exception {
         switchImsSmsFormat(PhoneConstants.PHONE_TYPE_GSM);
-        mImsSmsDispatcher.sendText(null, "222" /*scAddr*/, TAG,
+        mSmsDispatchersController.sendText(null, "222" /*scAddr*/, TAG,
                 null, null, null, null, false);
         verify(mSimulatedCommandsVerifier, times(0)).sendImsGsmSms(anyString(), anyString(),
                 anyInt(), anyInt(), any(Message.class));
@@ -137,7 +129,7 @@
     @Test @SmallTest
     public void testSendImsCdmaTest() throws Exception {
         switchImsSmsFormat(PhoneConstants.PHONE_TYPE_CDMA);
-        mImsSmsDispatcher.sendText("111"/* desAddr*/, "222" /*scAddr*/, TAG,
+        mSmsDispatchersController.sendText("111"/* desAddr*/, "222" /*scAddr*/, TAG,
                 null, null, null, null, false);
         verify(mSimulatedCommandsVerifier).sendImsCdmaSms((byte[])any(), eq(0), eq(0),
                 any(Message.class));
@@ -151,7 +143,7 @@
         replaceInstance(SMSDispatcher.SmsTracker.class, "mFormat", mTracker,
                 SmsConstants.FORMAT_3GPP2);
         doReturn(PhoneConstants.PHONE_TYPE_CDMA).when(mPhone).getPhoneType();
-        mImsSmsDispatcher.sendRetrySms(mTracker);
+        mSmsDispatchersController.sendRetrySms(mTracker);
         verify(mSimulatedCommandsVerifier).sendImsCdmaSms(captor.capture(), eq(0), eq(0),
                 any(Message.class));
         assertEquals(1, captor.getAllValues().size());
@@ -164,7 +156,7 @@
         switchImsSmsFormat(PhoneConstants.PHONE_TYPE_GSM);
         replaceInstance(SMSDispatcher.SmsTracker.class, "mFormat", mTracker,
                 SmsConstants.FORMAT_3GPP);
-        mImsSmsDispatcher.sendRetrySms(mTracker);
+        mSmsDispatchersController.sendRetrySms(mTracker);
         verify(mSimulatedCommandsVerifier).sendImsGsmSms((String)isNull(), (String)isNull(), eq(0),
                 eq(0), any(Message.class));
     }
@@ -176,18 +168,15 @@
         restoreInstance(Singleton.class, "mInstance", mIActivityManagerSingleton);
         restoreInstance(ActivityManager.class, "IActivityManagerSingleton", null);
 
-        Context realContext = TestApplication.getAppContext();
-        realContext.registerReceiver(mTestReceiver, new IntentFilter(TEST_INTENT));
-
-        PendingIntent pendingIntent = PendingIntent.getBroadcast(realContext, 0,
-                new Intent(TEST_INTENT), 0);
-
         // inject null sms pdu. This should cause intent to be received since pdu is null.
-        mImsSmsDispatcher.injectSmsPdu(null, SmsConstants.FORMAT_3GPP, pendingIntent);
+        mSmsDispatchersController.injectSmsPdu(null, SmsConstants.FORMAT_3GPP,
+                (SmsDispatchersController.SmsInjectionCallback) result -> {
+                    mInjectionCallbackTriggered = true;
+                   assertEquals(Intents.RESULT_SMS_GENERIC_ERROR, result);
+                }
+        );
         waitForMs(100);
-        synchronized (mLock) {
-            assertEquals(true, mReceivedTestIntent);
-        }
+        assertEquals(true, mInjectionCallbackTriggered);
     }
 
     private void switchImsSmsFormat(int phoneType) {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsDispatcherTest.java
index a308762..82a7317 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsDispatcherTest.java
@@ -20,8 +20,8 @@
 import android.os.Message;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import com.android.internal.telephony.ImsSMSDispatcher;
 import com.android.internal.telephony.SMSDispatcher;
+import com.android.internal.telephony.SmsDispatchersController;
 import com.android.internal.telephony.TelephonyTest;
 
 import static org.mockito.Mockito.*;
@@ -37,7 +37,7 @@
     @Mock
     private SmsMessage mCdmaSmsMessage;
     @Mock
-    private ImsSMSDispatcher mImsSmsDispatcher;
+    private SmsDispatchersController mSmsDispatchersController;
     @Mock
     private SMSDispatcher.SmsTracker mSmsTracker;
 
@@ -52,8 +52,7 @@
 
         @Override
         public void onLooperPrepared() {
-            mCdmaSmsDispatcher = new CdmaSMSDispatcher(mPhone, mSmsUsageMonitor,
-                    mImsSmsDispatcher);
+            mCdmaSmsDispatcher = new CdmaSMSDispatcher(mPhone, mSmsDispatchersController);
             setReady(true);
         }
     }
@@ -63,7 +62,7 @@
         super.setUp(this.getClass().getSimpleName());
 
         setupMockPackagePermissionChecks();
-
+        doReturn(mSmsUsageMonitor).when(mSmsDispatchersController).getUsageMonitor();
         mCdmaSmsDispatcherTestHandler = new CdmaSmsDispatcherTestHandler(TAG);
         mCdmaSmsDispatcherTestHandler.start();
         waitUntilReady();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/CellularDataServiceTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/CellularDataServiceTest.java
new file mode 100644
index 0000000..a34febc
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/CellularDataServiceTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+
+import static junit.framework.Assert.assertEquals;
+
+import android.hardware.radio.V1_0.SetupDataCallResult;
+import android.net.LinkAddress;
+import android.net.NetworkUtils;
+import android.telephony.data.DataCallResponse;
+
+import com.android.internal.telephony.dataconnection.CellularDataService;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+public class CellularDataServiceTest extends TelephonyTest {
+
+    private CellularDataService mCellularDataService;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp(getClass().getSimpleName());
+        mCellularDataService = new CellularDataService();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    @Test
+    public void testConvertDataCallResult() throws Exception {
+
+        SetupDataCallResult result = new SetupDataCallResult();
+        result.status = 0;
+        result.suggestedRetryTime = -1;
+        result.cid = 1;
+        result.active = 1;
+        result.type = "IP";
+        result.ifname = "eth0";
+        result.addresses = "10.0.2.15";
+        result.dnses = "10.0.2.3";
+        result.gateways = "10.0.2.15 fe80::2";
+        result.pcscf = "";
+        result.mtu = 1500;
+
+        DataCallResponse response = new DataCallResponse(0, -1, 1, 1, "IP",
+                "eth0",
+                Arrays.asList(new LinkAddress(NetworkUtils.numericToInetAddress("10.0.2.15"), 32)),
+                Arrays.asList(NetworkUtils.numericToInetAddress("10.0.2.3")),
+                Arrays.asList(NetworkUtils.numericToInetAddress("10.0.2.15"),
+                        NetworkUtils.numericToInetAddress("fe80::2")),
+                Arrays.asList(""),
+                1500);
+
+        assertEquals(response, mCellularDataService.convertDataCallResult(result));
+
+        result.status = 0;
+        result.suggestedRetryTime = -1;
+        result.cid = 0;
+        result.active = 2;
+        result.type = "IPV4V6";
+        result.ifname = "ifname";
+        result.addresses = "2607:fb90:a620:651d:eabe:f8da:c107:44be/64";
+        result.dnses = "fd00:976a::9      fd00:976a::10";
+        result.gateways = "fe80::4c61:1832:7b28:d36c    1.2.3.4";
+        result.pcscf = "fd00:976a:c206:20::6   fd00:976a:c206:20::9    fd00:976a:c202:1d::9";
+        result.mtu = 1500;
+
+        response = new DataCallResponse(0, -1, 0, 2, "IPV4V6",
+                "ifname",
+                Arrays.asList(new LinkAddress("2607:fb90:a620:651d:eabe:f8da:c107:44be/64")),
+                Arrays.asList(NetworkUtils.numericToInetAddress("fd00:976a::9"),
+                        NetworkUtils.numericToInetAddress("fd00:976a::10")),
+                Arrays.asList(NetworkUtils.numericToInetAddress("fe80::4c61:1832:7b28:d36c"),
+                        NetworkUtils.numericToInetAddress("1.2.3.4")),
+                Arrays.asList("fd00:976a:c206:20::6", "fd00:976a:c206:20::9",
+                        "fd00:976a:c202:1d::9"),
+                1500);
+
+        assertEquals(response, mCellularDataService.convertDataCallResult(result));
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
index 98b3deb..975c9a1 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
@@ -20,14 +20,12 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkPolicyManager.OVERRIDE_CONGESTED;
 import static android.net.NetworkPolicyManager.OVERRIDE_UNMETERED;
-
 import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
 import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_ADDRESS;
 import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_DNS;
 import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_GATEWAY;
 import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_IFNAME;
 import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_PCSCF_ADDRESS;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -46,14 +44,15 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Message;
+import android.telephony.AccessNetworkConstants.AccessNetworkType;
 import android.telephony.CarrierConfigManager;
 import android.telephony.ServiceState;
 import android.telephony.data.DataCallResponse;
 import android.telephony.data.DataProfile;
+import android.telephony.data.DataService;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.RetryManager;
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.dataconnection.DataConnection.ConnectionParams;
@@ -226,8 +225,8 @@
 
         ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
         verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(ServiceState.RIL_RADIO_TECHNOLOGY_UMTS), dpCaptor.capture(),
-                eq(false), eq(false), any(Message.class));
+                eq(AccessNetworkType.UTRAN), dpCaptor.capture(), eq(false),
+                eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(), any(Message.class));
 
         assertEquals("spmode.ne.jp", dpCaptor.getValue().getApn());
 
@@ -243,7 +242,7 @@
         waitForMs(100);
 
         verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(eq(1),
-                eq(RILConstants.DEACTIVATE_REASON_NONE), any(Message.class));
+                eq(DataService.REQUEST_REASON_NORMAL), any(Message.class));
 
         assertEquals("DcInactiveState", getCurrentState().getName());
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
index 7434abf..c6a17dd 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
@@ -18,7 +18,6 @@
 
 import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
 import static com.android.internal.telephony.dataconnection.ApnSettingTest.createApnSetting;
-
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -63,6 +62,7 @@
 import android.telephony.TelephonyManager;
 import android.telephony.data.DataCallResponse;
 import android.telephony.data.DataProfile;
+import android.telephony.data.DataService;
 import android.test.mock.MockContentProvider;
 import android.test.mock.MockContentResolver;
 import android.test.suitebuilder.annotation.MediumTest;
@@ -559,8 +559,10 @@
         ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
         // Verify if RIL command was sent properly.
         verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
-                eq(false), eq(false), any(Message.class));
+                eq(ServiceState.rilRadioTechnologyToAccessNetworkType(
+                        mServiceState.getRilDataRadioTechnology())), dpCaptor.capture(),
+                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+                any(Message.class));
         verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, LTE_BEARER_BITMASK);
 
         verifyDataConnected(FAKE_APN1);
@@ -630,8 +632,10 @@
         ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
         // Verify if RIL command was sent properly.
         verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
-                eq(false), eq(false), any(Message.class));
+                eq(ServiceState.rilRadioTechnologyToAccessNetworkType(
+                        mServiceState.getRilDataRadioTechnology())), dpCaptor.capture(),
+                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+                any(Message.class));
         verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, LTE_BEARER_BITMASK);
 
         // Make sure we never notify connected because the data call setup is supposed to fail.
@@ -656,8 +660,10 @@
         dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
         // Verify if RIL command was sent properly.
         verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
-                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
-                eq(false), eq(false), any(Message.class));
+                eq(ServiceState.rilRadioTechnologyToAccessNetworkType(
+                        mServiceState.getRilDataRadioTechnology())), dpCaptor.capture(),
+                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+                any(Message.class));
         verifyDataProfile(dpCaptor.getValue(), FAKE_APN2, 0, 5, 1, LTE_BEARER_BITMASK);
 
         // Verify connected with APN2 setting.
@@ -691,8 +697,10 @@
         waitForMs(200);
         ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
         verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
-                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
-                eq(false), eq(false), any(Message.class));
+                eq(ServiceState.rilRadioTechnologyToAccessNetworkType(
+                        mServiceState.getRilDataRadioTechnology())), dpCaptor.capture(),
+                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+                any(Message.class));
         verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, LTE_BEARER_BITMASK);
 
         logd("Sending DATA_DISABLED_CMD");
@@ -700,7 +708,8 @@
         waitForMs(200);
 
         // expected tear down all metered DataConnections
-        verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(anyInt(), anyInt(),
+        verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(
+                eq(DataService.REQUEST_REASON_NORMAL), anyInt(),
                 any(Message.class));
         assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
         assertEquals(DctConstants.State.IDLE, mDct.getState(PhoneConstants.APN_TYPE_DEFAULT));
@@ -742,8 +751,10 @@
         waitForMs(300);
         ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
         verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
-                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
-                eq(false), eq(false), any(Message.class));
+                eq(ServiceState.rilRadioTechnologyToAccessNetworkType(
+                        mServiceState.getRilDataRadioTechnology())), dpCaptor.capture(),
+                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+                any(Message.class));
         verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, LTE_BEARER_BITMASK);
 
         //user is in roaming
@@ -754,7 +765,8 @@
         waitForMs(200);
 
         // expected tear down all metered DataConnections
-        verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(anyInt(), anyInt(),
+        verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(
+                eq(DataService.REQUEST_REASON_NORMAL), anyInt(),
                 any(Message.class));
         assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
         assertEquals(DctConstants.State.IDLE, mDct.getState(PhoneConstants.APN_TYPE_DEFAULT));
@@ -800,8 +812,10 @@
         waitForMs(200);
         ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
         verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
-                eq(false), eq(false), any(Message.class));
+                eq(ServiceState.rilRadioTechnologyToAccessNetworkType(
+                        mServiceState.getRilDataRadioTechnology())), dpCaptor.capture(),
+                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+                any(Message.class));
         verifyDataProfile(dpCaptor.getValue(), FAKE_APN3, 2, 64, 0, 0);
 
         assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
@@ -906,8 +920,10 @@
 
         ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
         verify(mSimulatedCommandsVerifier, times(2)).setupDataCall(
-                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
-                eq(false), eq(false), any(Message.class));
+                eq(ServiceState.rilRadioTechnologyToAccessNetworkType(
+                        mServiceState.getRilDataRadioTechnology())), dpCaptor.capture(),
+                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+                any(Message.class));
         verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, LTE_BEARER_BITMASK);
         assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
 
@@ -918,7 +934,8 @@
         waitForMs(100);
 
         // Validate all metered data connections have been torn down
-        verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(anyInt(), anyInt(),
+        verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(
+                eq(DataService.REQUEST_REASON_NORMAL), anyInt(),
                 any(Message.class));
         assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
         assertEquals(DctConstants.State.IDLE, mDct.getState(PhoneConstants.APN_TYPE_DEFAULT));
@@ -950,8 +967,10 @@
         waitForMs(200);
 
         verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(mServiceState.getRilDataRadioTechnology()), any(DataProfile.class),
-                eq(false), eq(false), any(Message.class));
+                eq(ServiceState.rilRadioTechnologyToAccessNetworkType(
+                        mServiceState.getRilDataRadioTechnology())), any(DataProfile.class),
+                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+                any(Message.class));
     }
 
     // Test the unmetered APN setup when data is disabled.
@@ -976,8 +995,10 @@
         waitForMs(200);
 
         verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                eq(mServiceState.getRilDataRadioTechnology()), any(DataProfile.class),
-                eq(false), eq(false), any(Message.class));
+                eq(ServiceState.rilRadioTechnologyToAccessNetworkType(
+                        mServiceState.getRilDataRadioTechnology())), any(DataProfile.class),
+                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+                any(Message.class));
     }
 
     // Test the metered APN setup when data is disabled.
@@ -1001,8 +1022,9 @@
         mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
         waitForMs(200);
 
-        verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(
-                anyInt(), any(DataProfile.class), eq(false), eq(false), any(Message.class));
+        verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(anyInt(), any(DataProfile.class),
+                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+                any(Message.class));
     }
 
     // Test the restricted data request when data is disabled.
@@ -1028,8 +1050,9 @@
         mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
         waitForMs(200);
 
-        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(
-                anyInt(), any(DataProfile.class), eq(false), eq(false), any(Message.class));
+        verify(mSimulatedCommandsVerifier, times(1)).setupDataCall(anyInt(), any(DataProfile.class),
+                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+                any(Message.class));
     }
 
     // Test the default data when data is not connectable.
@@ -1054,8 +1077,9 @@
         mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
         waitForMs(200);
 
-        verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(
-                anyInt(), any(DataProfile.class), eq(false), eq(false), any(Message.class));
+        verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(anyInt(), any(DataProfile.class),
+                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+                any(Message.class));
     }
 
     // Test the default data on IWLAN.
@@ -1081,8 +1105,9 @@
         mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
         waitForMs(200);
 
-        verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(
-                anyInt(), any(DataProfile.class), eq(false), eq(false), any(Message.class));
+        verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(anyInt(), any(DataProfile.class),
+                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+                any(Message.class));
     }
 
     // Test the default data when the phone is in ECBM.
@@ -1107,8 +1132,9 @@
         mDct.sendMessage(mDct.obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, mApnContext));
         waitForMs(200);
 
-        verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(
-                anyInt(), any(DataProfile.class), eq(false), eq(false), any(Message.class));
+        verify(mSimulatedCommandsVerifier, times(0)).setupDataCall(anyInt(), any(DataProfile.class),
+                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+                any(Message.class));
     }
 
     // Test update waiting apn list when on data rat change
@@ -1134,8 +1160,10 @@
         ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
         // Verify if RIL command was sent properly.
         verify(mSimulatedCommandsVerifier).setupDataCall(
-                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
-                eq(false), eq(false), any(Message.class));
+                eq(ServiceState.rilRadioTechnologyToAccessNetworkType(
+                        mServiceState.getRilDataRadioTechnology())), dpCaptor.capture(),
+                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+                any(Message.class));
         verifyDataProfile(dpCaptor.getValue(), FAKE_APN4, 0, 5, 2, EHRPD_BEARER_BITMASK);
         assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
 
@@ -1148,7 +1176,8 @@
 
         // Verify the disconnected data call due to rat change and retry manger schedule another
         // data call setup
-        verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(anyInt(), anyInt(),
+        verify(mSimulatedCommandsVerifier, times(1)).deactivateDataCall(
+                eq(DataService.REQUEST_REASON_NORMAL), anyInt(),
                 any(Message.class));
         verify(mAlarmManager, times(1)).setExact(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
                 anyLong(), any(PendingIntent.class));
@@ -1163,8 +1192,10 @@
 
         // Verify if RIL command was sent properly.
         verify(mSimulatedCommandsVerifier).setupDataCall(
-                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
-                eq(false), eq(false), any(Message.class));
+                eq(ServiceState.rilRadioTechnologyToAccessNetworkType(
+                        mServiceState.getRilDataRadioTechnology())), dpCaptor.capture(),
+                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+                any(Message.class));
         verifyDataProfile(dpCaptor.getValue(), FAKE_APN1, 0, 5, 1, LTE_BEARER_BITMASK);
         assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
     }
@@ -1216,8 +1247,10 @@
         ArgumentCaptor<DataProfile> dpCaptor = ArgumentCaptor.forClass(DataProfile.class);
         // Verify if RIL command was sent properly.
         verify(mSimulatedCommandsVerifier).setupDataCall(
-                eq(mServiceState.getRilDataRadioTechnology()), dpCaptor.capture(),
-                eq(false), eq(false), any(Message.class));
+                eq(ServiceState.rilRadioTechnologyToAccessNetworkType(
+                        mServiceState.getRilDataRadioTechnology())), dpCaptor.capture(),
+                eq(false), eq(false), eq(DataService.REQUEST_REASON_NORMAL), any(),
+                any(Message.class));
         verifyDataProfile(dpCaptor.getValue(), FAKE_APN4, 0, 5, 2, EHRPD_BEARER_BITMASK);
         assertEquals(DctConstants.State.CONNECTED, mDct.getOverallState());
 
@@ -1229,7 +1262,8 @@
         waitForMs(200);
 
         // Verify data connection is on
-        verify(mSimulatedCommandsVerifier, times(0)).deactivateDataCall(anyInt(), anyInt(),
+        verify(mSimulatedCommandsVerifier, times(0)).deactivateDataCall(
+                eq(DataService.REQUEST_REASON_NORMAL), anyInt(),
                 any(Message.class));
 
         // Data rat resume from unknown to ehrpd
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
index 501131e..ae66726 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsDispatcherTest.java
@@ -26,6 +26,7 @@
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -51,8 +52,8 @@
 
 import com.android.internal.telephony.ContextFixture;
 import com.android.internal.telephony.ISub;
-import com.android.internal.telephony.ImsSMSDispatcher;
 import com.android.internal.telephony.SMSDispatcher;
+import com.android.internal.telephony.SmsDispatchersController;
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.TelephonyTestUtils;
 import com.android.internal.telephony.TestApplication;
@@ -75,7 +76,7 @@
     @Mock
     private SmsMessage mGsmSmsMessage;
     @Mock
-    private ImsSMSDispatcher mImsSmsDispatcher;
+    private SmsDispatchersController mSmsDispatchersController;
     @Mock
     private GsmInboundSmsHandler mGsmInboundSmsHandler;
     @Mock
@@ -108,8 +109,8 @@
 
         @Override
         public void onLooperPrepared() {
-            mGsmSmsDispatcher = new GsmSMSDispatcher(mPhone, mSmsUsageMonitor,
-                    mImsSmsDispatcher, mGsmInboundSmsHandler);
+            mGsmSmsDispatcher = new GsmSMSDispatcher(mPhone, mSmsDispatchersController,
+                    mGsmInboundSmsHandler);
             setReady(true);
         }
     }
@@ -123,6 +124,7 @@
         // in the cache, a real instance is used.
         mServiceManagerMockedServices.put("isub", mISubStub);
 
+        doReturn(mSmsUsageMonitor).when(mSmsDispatchersController).getUsageMonitor();
         mGsmSmsDispatcherTestHandler = new GsmSmsDispatcherTestHandler(getClass().getSimpleName());
         mGsmSmsDispatcherTestHandler.start();
         waitUntilReady();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
index 591cf42..6d45db0 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
@@ -18,7 +18,6 @@
 
 import static android.telephony.ServiceState.RIL_RADIO_TECHNOLOGY_LTE;
 import static android.telephony.ServiceState.ROAMING_TYPE_DOMESTIC;
-
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_DEACTIVATE_DATA_CALL;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SEND_SMS;
 import static com.android.internal.telephony.RILConstants.RIL_REQUEST_SETUP_DATA_CALL;
@@ -28,7 +27,6 @@
 import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_IFNAME;
 import static com.android.internal.telephony.dataconnection.DcTrackerTest.FAKE_PCSCF_ADDRESS;
 import static com.android.internal.telephony.nano.TelephonyProto.PdpType.PDP_TYPE_IPV4V6;
-
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -534,8 +532,8 @@
     @Test
     @SmallTest
     public void testWriteRilSetupDataCall() throws Exception {
-        mMetrics.writeRilSetupDataCall(
-                mPhone.getPhoneId(), 1, 14, 3, "apn", 0, "IPV4V6");
+        mMetrics.writeSetupDataCall(
+                mPhone.getPhoneId(), 14, 3, "apn", "IPV4V6");
 
         TelephonyLog log = buildProto();
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java
index b41846e..7bd1b08 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/PhoneMock.java
@@ -1208,6 +1208,14 @@
         throw new RuntimeException("not implemented");
     }
 
+    public void getCallBarring(String facility, String password, Message onComplete,
+            int serviceClass) {
+    }
+
+    public void setCallBarring(String facility, boolean lockState, String password,
+            Message onComplete, int serviceClass) {
+    }
+
     public void getOutgoingCallerIdDisplay(Message onComplete) {
         throw new RuntimeException("not implemented");
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java
index bbe2ce9..9dd1af0 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java
@@ -126,8 +126,11 @@
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
-        /* initially there are no application available */
-        mIccCardStatus.mApplications = new IccCardApplicationStatus[]{};
+         /* initially there are no application available, but the array should not be empty. */
+        IccCardApplicationStatus umtsApp = composeUiccApplicationStatus(
+                IccCardApplicationStatus.AppType.APPTYPE_USIM,
+                IccCardApplicationStatus.AppState.APPSTATE_UNKNOWN, "0xA2");
+        mIccCardStatus.mApplications = new IccCardApplicationStatus[]{umtsApp};
         mIccCardStatus.mCdmaSubscriptionAppIndex =
                 mIccCardStatus.mImsSubscriptionAppIndex =
                         mIccCardStatus.mGsmUmtsSubscriptionAppIndex = -1;
@@ -154,12 +157,17 @@
     @Test
     @SmallTest
     public void tesUiccProfileInfoSanity() {
-        assertEquals(0, mUiccProfile.getNumApplications());
+        assertEquals(1, mUiccProfile.getNumApplications());
         assertNull(mUiccProfile.getUniversalPinState());
         assertNull(mUiccProfile.getOperatorBrandOverride());
         for (IccCardApplicationStatus.AppType mAppType :
                 IccCardApplicationStatus.AppType.values()) {
-            assertFalse(mUiccProfile.isApplicationOnIcc(mAppType));
+            if (mAppType == IccCardApplicationStatus.AppType.APPTYPE_USIM) {
+                assertTrue(mUiccProfile.isApplicationOnIcc(mAppType));
+            } else {
+                assertFalse(mUiccProfile.isApplicationOnIcc(mAppType));
+            }
+
         }
     }
 
@@ -347,4 +355,53 @@
         // response for them right away. Ideally applications and records should be mocked.
         assertEquals(State.LOADED, mUiccProfile.getState());
     }
+
+    @Test
+    @SmallTest
+    public void testUpdateUiccProfileApplicationNoApplication() {
+        mIccCardStatus.mApplications = new IccCardApplicationStatus[]{};
+        mIccCardStatus.mCdmaSubscriptionAppIndex = -1;
+        mIccCardStatus.mImsSubscriptionAppIndex = -1;
+        mIccCardStatus.mGsmUmtsSubscriptionAppIndex = -1;
+        Message mProfileUpdate = mHandler.obtainMessage(UICCPROFILE_UPDATE_APPLICATION_EVENT);
+        setReady(false);
+        mProfileUpdate.sendToTarget();
+
+        waitUntilReady();
+
+        /* wait for the carrier privilege rules to be loaded */
+        waitForMs(50);
+        assertEquals(0, mUiccProfile.getNumApplications());
+
+        mUiccProfile.sendMessage(mUiccProfile.obtainMessage(EVENT_APP_READY));
+        waitForMs(SCARY_SLEEP_MS);
+        // state is loaded since there is no applications.
+        assertEquals(State.NOT_READY, mUiccProfile.getState());
+    }
+
+    @Test
+    @SmallTest
+    public void testUpdateUiccProfileApplicationNoSupportApplication() {
+        IccCardApplicationStatus unknownApp = composeUiccApplicationStatus(
+                IccCardApplicationStatus.AppType.APPTYPE_UNKNOWN,
+                IccCardApplicationStatus.AppState.APPSTATE_UNKNOWN, "");
+        mIccCardStatus.mApplications = new IccCardApplicationStatus[]{unknownApp};
+        mIccCardStatus.mCdmaSubscriptionAppIndex = -1;
+        mIccCardStatus.mImsSubscriptionAppIndex = -1;
+        mIccCardStatus.mGsmUmtsSubscriptionAppIndex = -1;
+        Message mProfileUpdate = mHandler.obtainMessage(UICCPROFILE_UPDATE_APPLICATION_EVENT);
+        setReady(false);
+        mProfileUpdate.sendToTarget();
+
+        waitUntilReady();
+
+        /* wait for the carrier privilege rules to be loaded */
+        waitForMs(50);
+        assertEquals(1, mUiccProfile.getNumApplications());
+
+        mUiccProfile.sendMessage(mUiccProfile.obtainMessage(EVENT_APP_READY));
+        waitForMs(SCARY_SLEEP_MS);
+        // state is loaded since there is no applications.
+        assertEquals(State.NOT_READY, mUiccProfile.getState());
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java
index 88a32e5..9a18d8c 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccSlotTest.java
@@ -15,14 +15,10 @@
  */
 package com.android.internal.telephony.uicc;
 
-import static com.android.internal.telephony.TelephonyTestUtils.waitForMs;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.verify;
 
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -34,7 +30,6 @@
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
-import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
 public class UiccSlotTest extends TelephonyTest {
@@ -98,35 +93,6 @@
         super.tearDown();
     }
 
-
-    @Test @SmallTest
-    public void testCardAbsentListener() {
-        mUiccSlot.registerForAbsent(mMockedHandler, UICCCARD_ABSENT, null);
-        /* assume hotswap capable, avoid bootup on card removal */
-        mContextFixture.putBooleanResource(com.android.internal.R.bool.config_hotswapCapable, true);
-        mSimulatedCommands.setRadioPower(true, null);
-
-        /* Mock Card State transition from card_present to card_absent */
-        logd("UICC Card Present update");
-        mIccCardStatus.mCardState = IccCardStatus.CardState.CARDSTATE_PRESENT;
-        // mUiccSlot.update() needs to be called from the handler thread because it creates UiccCard
-        Message mCardUpdate = mTestHandler.obtainMessage(UICCCARD_UPDATE_CARD_STATE_EVENT);
-        setReady(false);
-        mCardUpdate.sendToTarget();
-        waitUntilReady();
-
-        logd("UICC Card absent update");
-        mIccCardStatus.mCardState = IccCardStatus.CardState.CARDSTATE_ABSENT;
-        mUiccSlot.update(mSimulatedCommands, mIccCardStatus, 0 /* phoneId */);
-        waitForMs(50);
-
-        ArgumentCaptor<Message> mCaptorMessage = ArgumentCaptor.forClass(Message.class);
-        ArgumentCaptor<Long> mCaptorLong = ArgumentCaptor.forClass(Long.class);
-        verify(mMockedHandler, atLeast(1)).sendMessageDelayed(mCaptorMessage.capture(),
-                mCaptorLong.capture());
-        assertEquals(UICCCARD_ABSENT, mCaptorMessage.getValue().what);
-    }
-
     @Test
     @SmallTest
     public void testUpdateSlotStatus() {