Merge "using the correct intent extra for subId"
diff --git a/src/java/com/android/internal/telephony/BaseCommands.java b/src/java/com/android/internal/telephony/BaseCommands.java
index c8cea5a..853b85f 100644
--- a/src/java/com/android/internal/telephony/BaseCommands.java
+++ b/src/java/com/android/internal/telephony/BaseCommands.java
@@ -792,15 +792,17 @@
      * RadioState has 3 values : RADIO_OFF, RADIO_UNAVAILABLE, RADIO_ON.
      *
      * @param newState new RadioState decoded from RIL_UNSOL_RADIO_STATE_CHANGED
+     * @param forceNotifyRegistrants boolean indicating if registrants should be notified even if
+     * there is no change in state
      */
-    protected void setRadioState(RadioState newState) {
+    protected void setRadioState(RadioState newState, boolean forceNotifyRegistrants) {
         RadioState oldState;
 
         synchronized (mStateMonitor) {
             oldState = mState;
             mState = newState;
 
-            if (oldState == mState) {
+            if (oldState == mState && !forceNotifyRegistrants) {
                 // no state transition
                 return;
             }
diff --git a/src/java/com/android/internal/telephony/CallManager.java b/src/java/com/android/internal/telephony/CallManager.java
index 78a9324..3a49a12 100644
--- a/src/java/com/android/internal/telephony/CallManager.java
+++ b/src/java/com/android/internal/telephony/CallManager.java
@@ -202,14 +202,6 @@
     }
 
     /**
-     * Returns all the registered phone objects.
-     * @return all the registered phone objects.
-     */
-    public List<Phone> getAllPhones() {
-        return Collections.unmodifiableList(mPhones);
-    }
-
-    /**
      * get Phone object corresponds to subId
      * @return Phone
      */
@@ -346,19 +338,6 @@
         return phone;
     }
 
-    public Phone getPhoneInCall(int subId) {
-        Phone phone = null;
-        if (!getFirstActiveRingingCall(subId).isIdle()) {
-            phone = getFirstActiveRingingCall(subId).getPhone();
-        } else if (!getActiveFgCall(subId).isIdle()) {
-            phone = getActiveFgCall(subId).getPhone();
-        } else {
-            // If BG call is idle, we return default phone
-            phone = getFirstActiveBgCall(subId).getPhone();
-        }
-        return phone;
-    }
-
     /**
      * Register phone to CallManager
      * @param phone to be registered
@@ -447,14 +426,6 @@
     }
 
     /**
-     * @return the phone associated with the background call
-     * of a particular subId
-     */
-    public Phone getBgPhone(int subId) {
-        return getFirstActiveBgCall(subId).getPhone();
-    }
-
-    /**
      * @return the phone associated with the ringing call
      */
     public Phone getRingingPhone() {
@@ -1926,42 +1897,6 @@
     }
 
     /**
-     * @return the connections of active background call
-     * return empty list if there is no active background call
-     */
-    public List<Connection> getBgCallConnections(int subId) {
-        Call bgCall = getFirstActiveBgCall(subId);
-        if ( bgCall != null) {
-            return bgCall.getConnections();
-        }
-        return mEmptyConnections;
-    }
-
-    /**
-     * @return the latest connection of active foreground call
-     * return null if there is no active foreground call
-     */
-    public Connection getFgCallLatestConnection() {
-        Call fgCall = getActiveFgCall();
-        if ( fgCall != null) {
-            return fgCall.getLatestConnection();
-        }
-        return null;
-    }
-
-    /**
-     * @return the latest connection of active foreground call
-     * return null if there is no active foreground call
-     */
-    public Connection getFgCallLatestConnection(int subId) {
-        Call fgCall = getActiveFgCall(subId);
-        if ( fgCall != null) {
-            return fgCall.getLatestConnection();
-        }
-        return null;
-    }
-
-    /**
      * @return true if there is at least one Foreground call in disconnected state
      */
     public boolean hasDisconnectedFgCall() {
@@ -2092,22 +2027,6 @@
         return false;
     }
 
-    /* FIXME Taken from klp-sprout-dev but setAudioMode was removed in L.
-    private boolean isServiceStateInService() {
-        boolean bInService = false;
-
-        for (Phone phone : mPhones) {
-            bInService = (phone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE);
-            if (bInService) {
-                break;
-            }
-        }
-
-        if (VDBG) Rlog.d(LOG_TAG, "[isServiceStateInService] bInService = " + bInService);
-        return bInService;
-    }
-    */
-
     private class CallManagerHandler extends Handler {
         @Override
         public void handleMessage(Message msg) {
@@ -2236,52 +2155,4 @@
             }
         }
     };
-
-    @Override
-    public String toString() {
-        Call call;
-        StringBuilder b = new StringBuilder();
-        for (int i = 0; i < TelephonyManager.getDefault().getPhoneCount(); i++) {
-            b.append("CallManager {");
-            b.append("\nstate = " + getState(i));
-            call = getActiveFgCall(i);
-            if (call != null) {
-                b.append("\n- Foreground: " + getActiveFgCallState(i));
-                b.append(" from " + call.getPhone());
-                b.append("\n  Conn: ").append(getFgCallConnections(i));
-            }
-            call = getFirstActiveBgCall(i);
-            if (call != null) {
-                b.append("\n- Background: " + call.getState());
-                b.append(" from " + call.getPhone());
-                b.append("\n  Conn: ").append(getBgCallConnections(i));
-            }
-            call = getFirstActiveRingingCall(i);
-            if (call != null) {
-                b.append("\n- Ringing: " +call.getState());
-                b.append(" from " + call.getPhone());
-            }
-        }
-
-        for (Phone phone : getAllPhones()) {
-            if (phone != null) {
-                b.append("\nPhone: " + phone + ", name = " + phone.getPhoneName()
-                        + ", state = " + phone.getState());
-                call = phone.getForegroundCall();
-                if (call != null) {
-                    b.append("\n- Foreground: ").append(call);
-                }
-                call = phone.getBackgroundCall();
-                if (call != null) {
-                    b.append(" Background: ").append(call);
-                }
-                call = phone.getRingingCall();
-                if (call != null) {
-                    b.append(" Ringing: ").append(call);
-                }
-            }
-        }
-        b.append("\n}");
-        return b.toString();
-    }
 }
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index d4be7af..66c05cf 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -421,8 +421,7 @@
     public ServiceState getServiceState() {
         if (mSST == null || mSST.mSS.getState() != ServiceState.STATE_IN_SERVICE) {
             if (mImsPhone != null) {
-                return ServiceState.mergeServiceStates(
-                        (mSST == null) ? new ServiceState() : mSST.mSS,
+                return mergeServiceStates((mSST == null) ? new ServiceState() : mSST.mSS,
                         mImsPhone.getServiceState());
             }
         }
@@ -829,6 +828,32 @@
         return mCT.mRingingCall;
     }
 
+    /**
+     * ImsService reports "IN_SERVICE" for its voice registration state even if the device
+     * has lost the physical link to the tower. This helper method merges the IMS and modem
+     * ServiceState, only overriding the voice registration state when we are registered to IMS over
+     * IWLAN. In this case the voice registration state will always be "OUT_OF_SERVICE", so override
+     * the voice registration state with the data registration state.
+     */
+    private ServiceState mergeServiceStates(ServiceState baseSs, ServiceState imsSs) {
+        // "IN_SERVICE" in this case means IMS is registered.
+        if (imsSs.getVoiceRegState() != ServiceState.STATE_IN_SERVICE) {
+            return baseSs;
+        }
+
+        if (imsSs.getRilDataRadioTechnology() == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN) {
+            ServiceState newSs = new ServiceState(baseSs);
+            // Voice override for IWLAN. In this case, voice registration is OUT_OF_SERVICE, but
+            // the data RAT is IWLAN, so use that as a basis for determining whether or not the
+            // physical link is available.
+            newSs.setVoiceRegState(baseSs.getDataRegState());
+            newSs.setEmergencyOnly(false); // only get here if voice is IN_SERVICE
+            return newSs;
+        }
+
+        return baseSs;
+    }
+
     private boolean handleCallDeflectionIncallSupplementaryService(
             String dialString) {
         if (dialString.length() > 1) {
@@ -2854,8 +2879,8 @@
          *  selection is disallowed. So we should force auto select mode.
          */
         if (isManualSelProhibitedInGlobalMode()
-                && ((nwMode == Phone.NT_MODE_LTE_CDMA_EVDO_GSM_WCDMA)
-                        || (nwMode == Phone.NT_MODE_GLOBAL)) ){
+                && ((nwMode == TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA)
+                        || (nwMode == TelephonyManager.NETWORK_MODE_GLOBAL)) ){
             logd("Should force auto network select mode = " + nwMode);
             return true;
         } else {
diff --git a/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java b/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
index 2a35370..42a156a 100644
--- a/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
@@ -75,6 +75,8 @@
                             logd("GET_RECORD_SIZE Size " + mRecordSize[0] +
                                     " total " + mRecordSize[1] +
                                     " #record " + mRecordSize[2]);
+                        } else {
+                            loge("EVENT_GET_SIZE_DONE: failed; ex=" + ar.exception);
                         }
                         notifyPending(ar);
                     }
@@ -83,6 +85,9 @@
                     ar = (AsyncResult) msg.obj;
                     synchronized (mLock) {
                         mSuccess = (ar.exception == null);
+                        if (!mSuccess) {
+                            loge("EVENT_UPDATE_DONE - failed; ex=" + ar.exception);
+                        }
                         notifyPending(ar);
                     }
                     break;
@@ -92,7 +97,8 @@
                         if (ar.exception == null) {
                             mRecords = (List<AdnRecord>) ar.result;
                         } else {
-                            if(DBG) logd("Cannot load ADN records");
+                            loge("EVENT_LOAD_DONE: Cannot load ADN records; ex="
+                                    + ar.exception);
                             mRecords = null;
                         }
                         notifyPending(ar);
diff --git a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
index d7cb490..9b01a0b 100644
--- a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
@@ -184,11 +184,6 @@
         }
     }
 
-    protected void updatePhoneObject(Phone phone) {
-        mPhone = phone;
-        mDispatchersController.updatePhoneObject(phone);
-    }
-
     protected void enforceReceiveAndSend(String message) {
         mContext.enforceCallingOrSelfPermission(
                 Manifest.permission.RECEIVE_SMS, message);
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index ee28cbc..b779299 100644
--- a/src/java/com/android/internal/telephony/InboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -154,14 +154,11 @@
     /** Sent by {@link SmsBroadcastUndelivered} after cleaning the raw table. */
     public static final int EVENT_START_ACCEPTING_SMS = 6;
 
-    /** Update phone object */
-    private static final int EVENT_UPDATE_PHONE_OBJECT = 7;
-
     /** New SMS received as an AsyncResult. */
-    public static final int EVENT_INJECT_SMS = 8;
+    public static final int EVENT_INJECT_SMS = 7;
 
     /** Update the sms tracker */
-    public static final int EVENT_UPDATE_TRACKER = 9;
+    public static final int EVENT_UPDATE_TRACKER = 8;
 
     /** Wakelock release delay when returning to idle state. */
     private static final int WAKELOCK_TIMEOUT = 3000;
@@ -272,13 +269,6 @@
     }
 
     /**
-     * Update the phone object when it changes.
-     */
-    public void updatePhoneObject(Phone phone) {
-        sendMessage(EVENT_UPDATE_PHONE_OBJECT, phone);
-    }
-
-    /**
      * Dispose of the WAP push object and release the wakelock.
      */
     @Override
@@ -303,10 +293,6 @@
         @Override
         public boolean processMessage(Message msg) {
             switch (msg.what) {
-                case EVENT_UPDATE_PHONE_OBJECT: {
-                    onUpdatePhoneObject((Phone) msg.obj);
-                    break;
-                }
                 default: {
                     String errorText = "processMessage: unhandled message type " + msg.what +
                         " currState=" + getCurrentState().getName();
@@ -674,19 +660,6 @@
             int result, Message response);
 
     /**
-     * Called when the phone changes the default method updates mPhone
-     * mStorageMonitor and mCellBroadcastHandler.updatePhoneObject.
-     * Override if different or other behavior is desired.
-     *
-     * @param phone
-     */
-    protected void onUpdatePhoneObject(Phone phone) {
-        mPhone = phone;
-        mStorageMonitor = mPhone.mSmsStorageMonitor;
-        log("onUpdatePhoneObject: phone=" + mPhone.getClass().getSimpleName());
-    }
-
-    /**
      * Notify interested apps if the framework has rejected an incoming SMS,
      * and send an acknowledge message to the network.
      * @param success indicates that last message was successfully received.
diff --git a/src/java/com/android/internal/telephony/PhoneInternalInterface.java b/src/java/com/android/internal/telephony/PhoneInternalInterface.java
index fd99196..a304ab6 100644
--- a/src/java/com/android/internal/telephony/PhoneInternalInterface.java
+++ b/src/java/com/android/internal/telephony/PhoneInternalInterface.java
@@ -176,36 +176,6 @@
     static final int BM_US_2500M    = RILConstants.BAND_MODE_USA_2500M;
     static final int BM_NUM_BAND_MODES = 19; //Total number of band modes
 
-    // Used for preferred network type
-    // Note NT_* substitute RILConstants.NETWORK_MODE_* above the Phone
-    int NT_MODE_WCDMA_PREF   = RILConstants.NETWORK_MODE_WCDMA_PREF;
-    int NT_MODE_GSM_ONLY     = RILConstants.NETWORK_MODE_GSM_ONLY;
-    int NT_MODE_WCDMA_ONLY   = RILConstants.NETWORK_MODE_WCDMA_ONLY;
-    int NT_MODE_GSM_UMTS     = RILConstants.NETWORK_MODE_GSM_UMTS;
-
-    int NT_MODE_CDMA         = RILConstants.NETWORK_MODE_CDMA;
-
-    int NT_MODE_CDMA_NO_EVDO = RILConstants.NETWORK_MODE_CDMA_NO_EVDO;
-    int NT_MODE_EVDO_NO_CDMA = RILConstants.NETWORK_MODE_EVDO_NO_CDMA;
-    int NT_MODE_GLOBAL       = RILConstants.NETWORK_MODE_GLOBAL;
-
-    int NT_MODE_LTE_CDMA_AND_EVDO        = RILConstants.NETWORK_MODE_LTE_CDMA_EVDO;
-    int NT_MODE_LTE_GSM_WCDMA            = RILConstants.NETWORK_MODE_LTE_GSM_WCDMA;
-    int NT_MODE_LTE_CDMA_EVDO_GSM_WCDMA  = RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA;
-    int NT_MODE_LTE_ONLY                 = RILConstants.NETWORK_MODE_LTE_ONLY;
-    int NT_MODE_LTE_WCDMA                = RILConstants.NETWORK_MODE_LTE_WCDMA;
-
-    int NT_MODE_TDSCDMA_ONLY            = RILConstants.NETWORK_MODE_TDSCDMA_ONLY;
-    int NT_MODE_TDSCDMA_WCDMA           = RILConstants.NETWORK_MODE_TDSCDMA_WCDMA;
-    int NT_MODE_LTE_TDSCDMA             = RILConstants.NETWORK_MODE_LTE_TDSCDMA;
-    int NT_MODE_TDSCDMA_GSM             = RILConstants.NETWORK_MODE_TDSCDMA_GSM;
-    int NT_MODE_LTE_TDSCDMA_GSM         = RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM;
-    int NT_MODE_TDSCDMA_GSM_WCDMA       = RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA;
-    int NT_MODE_LTE_TDSCDMA_WCDMA       = RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA;
-    int NT_MODE_LTE_TDSCDMA_GSM_WCDMA   = RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA;
-    int NT_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
-    int NT_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
-
     int PREFERRED_NT_MODE                = RILConstants.PREFERRED_NETWORK_MODE;
 
     // Used for CDMA roaming mode
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index 33f7a47..638fa31 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -353,7 +353,7 @@
         // increment the cookie so that death notification can be ignored
         mRadioProxyCookie.incrementAndGet();
 
-        setRadioState(RadioState.RADIO_UNAVAILABLE);
+        setRadioState(RadioState.RADIO_UNAVAILABLE, true /* forceNotifyRegistrants */);
 
         RILRequest.resetSerial();
         // Clear request list on close
@@ -4188,7 +4188,7 @@
                 }
                 break;
             case RIL_REQUEST_SHUTDOWN:
-                setRadioState(RadioState.RADIO_UNAVAILABLE);
+                setRadioState(RadioState.RADIO_UNAVAILABLE, false /* forceNotifyRegistrants */);
                 break;
         }
 
diff --git a/src/java/com/android/internal/telephony/RadioIndication.java b/src/java/com/android/internal/telephony/RadioIndication.java
index f7a7943..ae76ef3 100644
--- a/src/java/com/android/internal/telephony/RadioIndication.java
+++ b/src/java/com/android/internal/telephony/RadioIndication.java
@@ -126,7 +126,7 @@
                     newState);
         }
 
-        mRil.setRadioState(newState);
+        mRil.setRadioState(newState, false /* forceNotifyRegistrants */);
     }
 
     public void callStateChanged(int indicationType) {
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
index 052178b..2420ac4 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -221,11 +221,6 @@
         }
     }
 
-    protected void updatePhoneObject(Phone phone) {
-        mPhone = phone;
-        Rlog.d(TAG, "Active phone changed to " + mPhone.getPhoneName() );
-    }
-
     /** Unregister for incoming SMS events. */
     public void dispose() {
         mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
diff --git a/src/java/com/android/internal/telephony/SmsDispatchersController.java b/src/java/com/android/internal/telephony/SmsDispatchersController.java
index d8906e7..ff01d5b 100644
--- a/src/java/com/android/internal/telephony/SmsDispatchersController.java
+++ b/src/java/com/android/internal/telephony/SmsDispatchersController.java
@@ -104,15 +104,6 @@
         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);
diff --git a/src/java/com/android/internal/telephony/SubscriptionController.java b/src/java/com/android/internal/telephony/SubscriptionController.java
index 962a1fa..9766cf0 100644
--- a/src/java/com/android/internal/telephony/SubscriptionController.java
+++ b/src/java/com/android/internal/telephony/SubscriptionController.java
@@ -55,12 +55,14 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Collectors;
 
@@ -90,9 +92,15 @@
     private static final int DEPRECATED_SETTING = -1;
     private ScLocalLog mLocalLog = new ScLocalLog(MAX_LOCAL_LOG_LINES);
 
+    // Lock that both mCacheActiveSubInfoList and mCacheOpportunisticSubInfoList use.
+    private Object mSubInfoListLock = new Object();
+
     /* The Cache of Active SubInfoRecord(s) list of currently in use SubInfoRecord(s) */
     private final List<SubscriptionInfo> mCacheActiveSubInfoList = new ArrayList<>();
 
+    /* Similar to mCacheActiveSubInfoList but only caching opportunistic subscriptions. */
+    private List<SubscriptionInfo> mCacheOpportunisticSubInfoList = new ArrayList<>();
+
     /**
      * Copied from android.util.LocalLog with flush() adding flush and line number
      * TODO: Update LocalLog
@@ -149,12 +157,12 @@
     protected static Phone[] sPhones;
     protected Context mContext;
     protected TelephonyManager mTelephonyManager;
-    protected CallManager mCM;
 
     private AppOpsManager mAppOps;
 
-    // Each slot can have multiple subs.
-    private static Map<Integer, ArrayList<Integer>> sSlotIndexToSubIds = new ConcurrentHashMap<>();
+    // FIXME: Does not allow for multiple subs in a slot and change to SparseArray
+    private static Map<Integer, Integer> sSlotIndexToSubId =
+            new ConcurrentHashMap<Integer, Integer>();
     private static int mDefaultFallbackSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     private static int mDefaultPhoneId = SubscriptionManager.DEFAULT_PHONE_INDEX;
 
@@ -199,7 +207,6 @@
 
     protected void init(Context c) {
         mContext = c;
-        mCM = CallManager.getInstance();
         mTelephonyManager = TelephonyManager.from(mContext);
 
         mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);
@@ -213,12 +220,11 @@
     }
 
     private boolean isSubInfoReady() {
-        return sSlotIndexToSubIds.size() > 0;
+        return sSlotIndexToSubId.size() > 0;
     }
 
     private SubscriptionController(Phone phone) {
         mContext = phone.getContext();
-        mCM = CallManager.getInstance();
         mAppOps = mContext.getSystemService(AppOpsManager.class);
 
         if(ServiceManager.getService("isub") == null) {
@@ -601,39 +607,7 @@
      */
     @Override
     public List<SubscriptionInfo> getActiveSubscriptionInfoList(String callingPackage) {
-        if (!isSubInfoReady()) {
-            if (DBG) logdl("[getActiveSubInfoList] Sub Controller not ready");
-            return null;
-        }
-
-        boolean canReadAllPhoneState;
-        try {
-            canReadAllPhoneState = TelephonyPermissions.checkReadPhoneState(mContext,
-                    SubscriptionManager.INVALID_SUBSCRIPTION_ID, Binder.getCallingPid(),
-                    Binder.getCallingUid(), callingPackage, "getActiveSubscriptionInfoList");
-        } catch (SecurityException e) {
-            canReadAllPhoneState = false;
-        }
-
-        synchronized (mCacheActiveSubInfoList) {
-            // If the caller can read all phone state, just return the full list.
-            if (canReadAllPhoneState) {
-                return new ArrayList<>(mCacheActiveSubInfoList);
-            }
-
-            // Filter the list to only include subscriptions which the caller can manage.
-            return mCacheActiveSubInfoList.stream()
-                    .filter(subscriptionInfo -> {
-                        try {
-                            return TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext,
-                                    subscriptionInfo.getSubscriptionId(), callingPackage,
-                                    "getActiveSubscriptionInfoList");
-                        } catch (SecurityException e) {
-                            return false;
-                        }
-                    })
-                    .collect(Collectors.toList());
-        }
+        return getSubscriptionInfoListFromCacheHelper(callingPackage, mCacheActiveSubInfoList);
     }
 
     /**
@@ -649,7 +623,9 @@
             return;
         }
 
-        synchronized (mCacheActiveSubInfoList) {
+        boolean opptSubListChanged = false;
+
+        synchronized (mSubInfoListLock) {
             mCacheActiveSubInfoList.clear();
             List<SubscriptionInfo> activeSubscriptionInfoList = getSubInfo(
                     SubscriptionManager.SIM_SLOT_INDEX + ">=0", null);
@@ -657,6 +633,10 @@
                 activeSubscriptionInfoList.sort(SUBSCRIPTION_INFO_COMPARATOR);
                 mCacheActiveSubInfoList.addAll(activeSubscriptionInfoList);
             }
+
+            // Refresh cached opportunistic sub list and detect whether it's changed.
+            opptSubListChanged = refreshCachedOpportunisticSubscriptionInfoList();
+
             if (DBG_CACHE) {
                 if (!mCacheActiveSubInfoList.isEmpty()) {
                     for (SubscriptionInfo si : mCacheActiveSubInfoList) {
@@ -668,6 +648,11 @@
                 }
             }
         }
+
+        // Send notification outside synchronization.
+        if (opptSubListChanged) {
+            notifyOpportunisticSubscriptionInfoChanged();
+        }
     }
 
     /**
@@ -979,20 +964,24 @@
                     do {
                         int subId = cursor.getInt(cursor.getColumnIndexOrThrow(
                                 SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID));
-                        // If sSlotIndexToSubIds already has the same subId for a slotIndex/phoneId,
+                        // If sSlotIndexToSubId already has the same subId for a slotIndex/phoneId,
                         // do not add it.
-                        if (addToSubIdList(slotIndex, subId)) {
+                        Integer currentSubId = sSlotIndexToSubId.get(slotIndex);
+                        if (currentSubId == null
+                                || currentSubId != subId
+                                || !SubscriptionManager.isValidSubscriptionId(currentSubId)) {
                             // TODO While two subs active, if user deactivats first
                             // one, need to update the default subId with second one.
 
                             // FIXME: Currently we assume phoneId == slotIndex which in the future
                             // may not be true, for instance with multiple subs per slot.
                             // But is true at the moment.
+                            sSlotIndexToSubId.put(slotIndex, subId);
                             int subIdCountMax = getActiveSubInfoCountMax();
                             int defaultSubId = getDefaultSubId();
                             if (DBG) {
                                 logdl("[addSubInfoRecord]"
-                                        + " sSlotIndexToSubIds.size=" + sSlotIndexToSubIds.size()
+                                        + " sSlotIndexToSubId.size=" + sSlotIndexToSubId.size()
                                         + " slotIndex=" + slotIndex + " subId=" + subId
                                         + " defaultSubId=" + defaultSubId + " simCount=" + subIdCountMax);
                             }
@@ -1059,7 +1048,7 @@
             // Once the records are loaded, notify DcTracker
             sPhones[slotIndex].updateDataConnectionTracker();
 
-            if (DBG) logdl("[addSubInfoRecord]- info size=" + sSlotIndexToSubIds.size());
+            if (DBG) logdl("[addSubInfoRecord]- info size=" + sSlotIndexToSubId.size());
 
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -1412,18 +1401,20 @@
             return SubscriptionManager.INVALID_SIM_SLOT_INDEX;
         }
 
-        int size = sSlotIndexToSubIds.size();
+        int size = sSlotIndexToSubId.size();
 
-        if (size == 0) {
+        if (size == 0)
+        {
             if (DBG) logd("[getSlotIndex]- size == 0, return SIM_NOT_INSERTED instead");
             return SubscriptionManager.SIM_NOT_INSERTED;
         }
 
-        for (Entry<Integer, ArrayList<Integer>> entry : sSlotIndexToSubIds.entrySet()) {
+        for (Entry<Integer, Integer> entry: sSlotIndexToSubId.entrySet()) {
             int sim = entry.getKey();
-            ArrayList<Integer> subs = entry.getValue();
+            int sub = entry.getValue();
 
-            if (subs.contains(subId)) {
+            if (subId == sub)
+            {
                 if (VDBG) logv("[getSlotIndex]- return = " + sim);
                 return sim;
             }
@@ -1459,17 +1450,26 @@
         }
 
         // Check if we've got any SubscriptionInfo records using slotIndexToSubId as a surrogate.
-        int size = sSlotIndexToSubIds.size();
+        int size = sSlotIndexToSubId.size();
         if (size == 0) {
             if (VDBG) {
-                logd("[getSubId]- sSlotIndexToSubIds.size == 0, return DummySubIds slotIndex="
+                logd("[getSubId]- sSlotIndexToSubId.size == 0, return DummySubIds slotIndex="
                         + slotIndex);
             }
             return getDummySubIds(slotIndex);
         }
 
+        // Create an array of subIds that are in this slot?
+        ArrayList<Integer> subIds = new ArrayList<Integer>();
+        for (Entry<Integer, Integer> entry: sSlotIndexToSubId.entrySet()) {
+            int slot = entry.getKey();
+            int sub = entry.getValue();
+            if (slotIndex == slot) {
+                subIds.add(sub);
+            }
+        }
+
         // Convert ArrayList to array
-        ArrayList<Integer> subIds = sSlotIndexToSubIds.get(slotIndex);
         int numSubIds = subIds.size();
         if (numSubIds > 0) {
             int[] subIdArr = new int[numSubIds];
@@ -1502,7 +1502,7 @@
             return SubscriptionManager.INVALID_PHONE_INDEX;
         }
 
-        int size = sSlotIndexToSubIds.size();
+        int size = sSlotIndexToSubId.size();
         if (size == 0) {
             phoneId = mDefaultPhoneId;
             if (DBG) logdl("[getPhoneId]- no sims, returning default phoneId=" + phoneId);
@@ -1510,11 +1510,11 @@
         }
 
         // FIXME: Assumes phoneId == slotIndex
-        for (Entry<Integer, ArrayList<Integer>> entry: sSlotIndexToSubIds.entrySet()) {
+        for (Entry<Integer, Integer> entry: sSlotIndexToSubId.entrySet()) {
             int sim = entry.getKey();
-            ArrayList<Integer> subs = entry.getValue();
+            int sub = entry.getValue();
 
-            if (subs.contains(subId)) {
+            if (subId == sub) {
                 if (VDBG) logdl("[getPhoneId]- found subId=" + subId + " phoneId=" + sim);
                 return sim;
             }
@@ -1559,14 +1559,14 @@
         // Now that all security checks passes, perform the operation as ourselves.
         final long identity = Binder.clearCallingIdentity();
         try {
-            int size = sSlotIndexToSubIds.size();
+            int size = sSlotIndexToSubId.size();
 
             if (size == 0) {
                 if (DBG) logdl("[clearSubInfo]- no simInfo size=" + size);
                 return 0;
             }
 
-            sSlotIndexToSubIds.clear();
+            sSlotIndexToSubId.clear();
             if (DBG) logdl("[clearSubInfo]- clear size=" + size);
             return size;
         } finally {
@@ -1927,29 +1927,23 @@
         sPhones = phones;
     }
 
-    private synchronized ArrayList<Integer> getActiveSubIdArrayList() {
-        ArrayList<Integer> allSubs = new ArrayList<>();
-        for (int i : sSlotIndexToSubIds.keySet()) {
-            allSubs.addAll(sSlotIndexToSubIds.get(i));
-        }
-        return allSubs;
-    }
-
     /**
      * @return the list of subId's that are active, is never null but the length maybe 0.
      */
     @Override
     public int[] getActiveSubIdList() {
-        ArrayList<Integer> allSubs = getActiveSubIdArrayList();
-        int[] subIdArr = new int[allSubs.size()];
+        Set<Entry<Integer, Integer>> simInfoSet = new HashSet<>(sSlotIndexToSubId.entrySet());
+
+        int[] subIdArr = new int[simInfoSet.size()];
         int i = 0;
-        for (int sub : allSubs) {
+        for (Entry<Integer, Integer> entry: simInfoSet) {
+            int sub = entry.getValue();
             subIdArr[i] = sub;
             i++;
         }
 
         if (VDBG) {
-            logdl("[getActiveSubIdList] allSubs=" + allSubs + " subIdArr.length="
+            logdl("[getActiveSubIdList] simInfoSet=" + simInfoSet + " subIdArr.length="
                     + subIdArr.length);
         }
         return subIdArr;
@@ -1972,7 +1966,7 @@
     @Deprecated // This should be moved into isActiveSubId(int, String)
     public boolean isActiveSubId(int subId) {
         boolean retVal = SubscriptionManager.isValidSubscriptionId(subId)
-                && getActiveSubIdArrayList().contains(subId);
+                && sSlotIndexToSubId.containsValue(subId);
 
         if (VDBG) logdl("[isActiveSubId]- " + retVal);
         return retVal;
@@ -2175,8 +2169,8 @@
                     .from(mContext).getDefaultSmsPhoneId());
             pw.flush();
 
-            for (Entry<Integer, ArrayList<Integer>> entry : sSlotIndexToSubIds.entrySet()) {
-                pw.println(" sSlotIndexToSubId[" + entry.getKey() + "]: subIds=" + entry);
+            for (Entry<Integer, Integer> entry : sSlotIndexToSubId.entrySet()) {
+                pw.println(" sSlotIndexToSubId[" + entry.getKey() + "]: subId=" + entry.getValue());
             }
             pw.flush();
             pw.println("++++++++++++++++++++++++++++++++");
@@ -2313,44 +2307,81 @@
 
     @Override
     public List<SubscriptionInfo> getOpportunisticSubscriptions(int slotId, String callingPackage) {
-        if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext,
-                SubscriptionManager.INVALID_SUBSCRIPTION_ID, callingPackage,
-                "getOpportunisticSubscriptions")) {
+        return getSubscriptionInfoListFromCacheHelper(
+                callingPackage, mCacheOpportunisticSubInfoList);
+    }
+
+    // Helper function of getOpportunisticSubscriptions and getActiveSubscriptionInfoList.
+    // They are doing similar things except operating on different cache.
+    private List<SubscriptionInfo> getSubscriptionInfoListFromCacheHelper(
+            String callingPackage, List<SubscriptionInfo> cacheSubList) {
+        if (!isSubInfoReady()) {
+            if (DBG) logdl("[getSubscriptionInfoList] Sub Controller not ready");
             return null;
         }
 
-        final long token = Binder.clearCallingIdentity();
-
+        boolean canReadAllPhoneState;
         try {
-            List<SubscriptionInfo> activeSubList = getActiveSubscriptionInfoList(callingPackage);
-            List<SubscriptionInfo> opportunisticSubList = new ArrayList<>();
+            canReadAllPhoneState = TelephonyPermissions.checkReadPhoneState(mContext,
+                    SubscriptionManager.INVALID_SUBSCRIPTION_ID, Binder.getCallingPid(),
+                    Binder.getCallingUid(), callingPackage, "getSubscriptionInfoList");
+        } catch (SecurityException e) {
+            canReadAllPhoneState = false;
+        }
 
-            if (activeSubList != null) {
-                for (SubscriptionInfo subInfo : activeSubList) {
-                    if (subInfo.isOpportunistic() && subInfo.getSimSlotIndex() == slotId) {
-                        opportunisticSubList.add(subInfo);
-                    }
-                }
+        synchronized (mSubInfoListLock) {
+            // If the caller can read all phone state, just return the full list.
+            if (canReadAllPhoneState) {
+                return new ArrayList<>(cacheSubList);
             }
 
-            return opportunisticSubList;
-        } finally {
-            Binder.restoreCallingIdentity(token);
+            // Filter the list to only include subscriptions which the caller can manage.
+            return cacheSubList.stream()
+                    .filter(subscriptionInfo -> {
+                        try {
+                            return TelephonyPermissions.checkCallingOrSelfReadPhoneState(mContext,
+                                    subscriptionInfo.getSubscriptionId(), callingPackage,
+                                    "getOpportunisticSubscriptions");
+                        } catch (SecurityException e) {
+                            return false;
+                        }
+                    })
+                    .collect(Collectors.toList());
         }
     }
 
-    private synchronized boolean addToSubIdList(int slotIndex, int subId) {
-        ArrayList<Integer> subIdsList = sSlotIndexToSubIds.get(slotIndex);
-        if (subIdsList == null) {
-            subIdsList = new ArrayList<>();
-            sSlotIndexToSubIds.put(slotIndex, subIdsList);
+    private void notifyOpportunisticSubscriptionInfoChanged() {
+        ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
+                "telephony.registry"));
+        try {
+            if (DBG) logd("notifyOpptSubscriptionInfoChanged:");
+            tr.notifyOpportunisticSubscriptionInfoChanged();
+        } catch (RemoteException ex) {
+            // Should never happen because its always available.
         }
+    }
 
-        // add the given subId unless it already exists
-        if (!subIdsList.contains(subId)) {
-            subIdsList.add(subId);
-            return true;
+
+    private boolean refreshCachedOpportunisticSubscriptionInfoList() {
+        synchronized (mSubInfoListLock) {
+            List<SubscriptionInfo> oldOpptCachedList = mCacheOpportunisticSubInfoList;
+
+            mCacheOpportunisticSubInfoList = mCacheActiveSubInfoList.stream()
+                    .filter(subscriptionInfo -> subscriptionInfo.isOpportunistic())
+                    .collect(Collectors.toList());
+
+            if (DBG_CACHE) {
+                if (!mCacheOpportunisticSubInfoList.isEmpty()) {
+                    for (SubscriptionInfo si : mCacheOpportunisticSubInfoList) {
+                        logd("[refreshCachedOpptSubscriptionInfoList] Setting Cached info="
+                                + si);
+                    }
+                } else {
+                    logdl("[refreshCachedOpptSubscriptionInfoList]- no info return");
+                }
+            }
+
+            return !oldOpptCachedList.equals(mCacheOpportunisticSubInfoList);
         }
-        return false;
     }
 }
diff --git a/src/java/com/android/internal/telephony/WakeLockStateMachine.java b/src/java/com/android/internal/telephony/WakeLockStateMachine.java
index e2061b9..aca016f 100644
--- a/src/java/com/android/internal/telephony/WakeLockStateMachine.java
+++ b/src/java/com/android/internal/telephony/WakeLockStateMachine.java
@@ -48,8 +48,6 @@
     /** Release wakelock after a short timeout when returning to idle state. */
     static final int EVENT_RELEASE_WAKE_LOCK = 3;
 
-    static final int EVENT_UPDATE_PHONE_OBJECT = 4;
-
     protected Phone mPhone;
 
     protected Context mContext;
@@ -77,10 +75,6 @@
         setInitialState(mIdleState);
     }
 
-    public void updatePhoneObject(Phone phone) {
-        sendMessage(EVENT_UPDATE_PHONE_OBJECT, phone);
-    }
-
     /**
      * Tell the state machine to quit after processing all messages.
      */
@@ -112,11 +106,6 @@
         @Override
         public boolean processMessage(Message msg) {
             switch (msg.what) {
-                case EVENT_UPDATE_PHONE_OBJECT: {
-                    mPhone = (Phone) msg.obj;
-                    log("updatePhoneObject: phone=" + mPhone.getClass().getSimpleName());
-                    break;
-                }
                 default: {
                     String errorText = "processMessage: unhandled message type " + msg.what;
                     if (Build.IS_DEBUGGABLE) {
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java b/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
index e2c178a..46d0243 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
@@ -193,19 +193,6 @@
     }
 
     /**
-     * Called when the phone changes the default method updates mPhone
-     * mStorageMonitor and mCellBroadcastHandler.updatePhoneObject.
-     * Override if different or other behavior is desired.
-     *
-     * @param phone
-     */
-    @Override
-    protected void onUpdatePhoneObject(Phone phone) {
-        super.onUpdatePhoneObject(phone);
-        mCellBroadcastHandler.updatePhoneObject(phone);
-    }
-
-    /**
      * Convert Android result code to CDMA SMS failure cause.
      * @param rc the Android SMS intent result value
      * @return 0 for success, or a CDMA SMS failure cause value
diff --git a/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java b/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
index 3fe2a33..f6efc5f 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
@@ -177,22 +177,6 @@
     }
 
     /**
-     * Called when the phone changes the default method updates mPhone
-     * mStorageMonitor and mCellBroadcastHandler.updatePhoneObject.
-     * Override if different or other behavior is desired.
-     *
-     * @param phone
-     */
-    @Override
-    protected void onUpdatePhoneObject(Phone phone) {
-        super.onUpdatePhoneObject(phone);
-        log("onUpdatePhoneObject: dispose of old CellBroadcastHandler and make a new one");
-        mCellBroadcastHandler.dispose();
-        mCellBroadcastHandler = GsmCellBroadcastHandler
-                .makeGsmCellBroadcastHandler(mContext, phone);
-    }
-
-    /**
      * Convert Android result code to 3GPP SMS failure cause.
      * @param rc the Android SMS intent result value
      * @return 0 for success, or a 3GPP SMS failure cause value
diff --git a/src/java/com/android/internal/telephony/ims/ImsResolver.java b/src/java/com/android/internal/telephony/ims/ImsResolver.java
index f0aee01..4a030b1 100644
--- a/src/java/com/android/internal/telephony/ims/ImsResolver.java
+++ b/src/java/com/android/internal/telephony/ims/ImsResolver.java
@@ -1131,17 +1131,6 @@
         }
     }
 
-    /**
-     * @return true if the ImsResolver is in the process of resolving a dynamic query and should not
-     * be considered available, false if the ImsResolver is idle.
-     */
-    public boolean isResolvingBinding() {
-        return mHandler.hasMessages(HANDLER_START_DYNAMIC_FEATURE_QUERY)
-                // We haven't processed this message yet, so it is still resolving.
-                || mHandler.hasMessages(HANDLER_DYNAMIC_FEATURE_CHANGE)
-                || mFeatureQueryManager.isQueryInProgress();
-    }
-
     private String printFeatures(Set<ImsFeatureConfiguration.FeatureSlotPair> features) {
         StringBuilder featureString = new StringBuilder();
         featureString.append("features: [");
diff --git a/src/java/com/android/internal/telephony/test/SimulatedCommands.java b/src/java/com/android/internal/telephony/test/SimulatedCommands.java
index a241785..e3c147b 100644
--- a/src/java/com/android/internal/telephony/test/SimulatedCommands.java
+++ b/src/java/com/android/internal/telephony/test/SimulatedCommands.java
@@ -158,7 +158,7 @@
 
         simulatedCallState = new SimulatedGsmCallState(looper);
 
-        setRadioState(RadioState.RADIO_ON);
+        setRadioState(RadioState.RADIO_ON, false /* forceNotifyRegistrants */);
         mSimLockedState = INITIAL_LOCK_STATE;
         mSimLockEnabled = (mSimLockedState != SimLockState.NONE);
         mPinCode = DEFAULT_SIM_PIN_CODE;
@@ -1241,9 +1241,9 @@
         }
 
         if(on) {
-            setRadioState(RadioState.RADIO_ON);
+            setRadioState(RadioState.RADIO_ON, false /* forceNotifyRegistrants */);
         } else {
-            setRadioState(RadioState.RADIO_OFF);
+            setRadioState(RadioState.RADIO_OFF, false /* forceNotifyRegistrants */);
         }
         resultSuccess(result, null);
     }
@@ -1599,7 +1599,7 @@
     @Override
     public void
     shutdown() {
-        setRadioState(RadioState.RADIO_UNAVAILABLE);
+        setRadioState(RadioState.RADIO_UNAVAILABLE, false /* forceNotifyRegistrants */);
         Looper looper = mHandlerThread.getLooper();
         if (looper != null) {
             looper.quit();
@@ -2024,7 +2024,7 @@
 
     @Override
     public void requestShutdown(Message result) {
-        setRadioState(RadioState.RADIO_UNAVAILABLE);
+        setRadioState(RadioState.RADIO_UNAVAILABLE, false /* forceNotifyRegistrants */);
     }
 
     @Override
diff --git a/src/java/com/android/internal/telephony/uicc/IccRecords.java b/src/java/com/android/internal/telephony/uicc/IccRecords.java
index 91cc5a0..7bc2acc 100644
--- a/src/java/com/android/internal/telephony/uicc/IccRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IccRecords.java
@@ -29,6 +29,7 @@
 import android.text.TextUtils;
 
 import com.android.internal.telephony.CommandsInterface;
+import com.android.internal.telephony.MccTable;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -46,6 +47,30 @@
     protected static final boolean DBG = true;
     protected static final boolean VDBG = false; // STOPSHIP if true
 
+    // Lookup table for carriers known to produce SIMs which incorrectly indicate MNC length.
+    private static final String[] MCCMNC_CODES_HAVING_3DIGITS_MNC = {
+        "302370", "302720", "310260",
+        "405025", "405026", "405027", "405028", "405029", "405030", "405031", "405032",
+        "405033", "405034", "405035", "405036", "405037", "405038", "405039", "405040",
+        "405041", "405042", "405043", "405044", "405045", "405046", "405047", "405750",
+        "405751", "405752", "405753", "405754", "405755", "405756", "405799", "405800",
+        "405801", "405802", "405803", "405804", "405805", "405806", "405807", "405808",
+        "405809", "405810", "405811", "405812", "405813", "405814", "405815", "405816",
+        "405817", "405818", "405819", "405820", "405821", "405822", "405823", "405824",
+        "405825", "405826", "405827", "405828", "405829", "405830", "405831", "405832",
+        "405833", "405834", "405835", "405836", "405837", "405838", "405839", "405840",
+        "405841", "405842", "405843", "405844", "405845", "405846", "405847", "405848",
+        "405849", "405850", "405851", "405852", "405853", "405854", "405855", "405856",
+        "405857", "405858", "405859", "405860", "405861", "405862", "405863", "405864",
+        "405865", "405866", "405867", "405868", "405869", "405870", "405871", "405872",
+        "405873", "405874", "405875", "405876", "405877", "405878", "405879", "405880",
+        "405881", "405882", "405883", "405884", "405885", "405886", "405908", "405909",
+        "405910", "405911", "405912", "405913", "405914", "405915", "405916", "405917",
+        "405918", "405919", "405920", "405921", "405922", "405923", "405924", "405925",
+        "405926", "405927", "405928", "405929", "405930", "405931", "405932", "502142",
+        "502143", "502145", "502146", "502147", "502148"
+    };
+
     // ***** Instance Variables
     protected AtomicBoolean mDestroyed = new AtomicBoolean(false);
     protected AtomicBoolean mLoaded = new AtomicBoolean(false);
@@ -93,7 +118,7 @@
     protected String mNewVoiceMailNum = null;
     protected String mNewVoiceMailTag = null;
     protected boolean mIsVoiceMailFixed = false;
-    protected String mImsi;
+    protected String mImsi; // IMSI must be only valid numeric characters 0-9 without padding 'f's
     private IccIoResult auth_rsp;
 
     protected int mMncLength = UNINITIALIZED;
@@ -456,15 +481,16 @@
     }
 
     /**
-     * Imsi could be set by ServiceStateTrackers in case of cdma
-     * @param imsi
+     * Update IMSI record and try to extract the PLMN information and notify registrants.
+     * @param inImsi the IMSI value
      */
-    public void setImsi(String imsi) {
+    public void setImsi(String inImsi) {
         // Remove trailing F's if present in IMSI.
-        mImsi = IccUtils.stripTrailingFs(imsi);
-        if (!Objects.equals(mImsi, imsi)) {
+        mImsi = IccUtils.stripTrailingFs(inImsi);
+        if (!Objects.equals(mImsi, inImsi)) {
             loge("Invalid IMSI padding digits received.");
         }
+
         if (TextUtils.isEmpty(mImsi)) mImsi = null;
 
         if (mImsi != null && !mImsi.matches("[0-9]+")) {
@@ -472,9 +498,64 @@
             mImsi = null;
         }
 
+        // IMSI (MCC+MNC+MSIN) is at least 6 digits, but not more
+        // than 15 (and usually 15).
+        // This will also handle un-set IMSI records (all Fs)
+        if (mImsi != null && (mImsi.length() < 6 || mImsi.length() > 15)) {
+            loge("invalid IMSI " + mImsi);
+            mImsi = null;
+        }
+
+        log("IMSI: mMncLength=" + mMncLength);
+
+        if (mImsi != null && mImsi.length() >= 6) {
+            log("IMSI: " + mImsi.substring(0, 6) + Rlog.pii(VDBG, mImsi.substring(6)));
+        }
+
+        // IMSI has changed so the PLMN might have changed as well
+        updateOperatorPlmn();
+
         mImsiReadyRegistrants.notifyRegistrants();
     }
 
+    protected void updateOperatorPlmn() {
+        // In case of a test override, use the test IMSI
+        String imsi = getIMSI();
+
+        if (imsi != null) {
+            // First try to guess the length based on a table of known 3-digit MNCs.
+            if (((mMncLength == UNKNOWN) || (mMncLength == 2)) && imsi.length() >= 6) {
+                String mccmncCode = imsi.substring(0, 6);
+                for (String mccmnc : MCCMNC_CODES_HAVING_3DIGITS_MNC) {
+                    if (mccmnc.equals(mccmncCode)) {
+                        mMncLength = 3;
+                        log("IMSI: setting1 mMncLength=" + mMncLength);
+                        break;
+                    }
+                }
+            }
+
+            // If still unknown, guess using the MCC.
+            if (mMncLength == UNKNOWN) {
+                try {
+                    int mcc = Integer.parseInt(imsi.substring(0, 3));
+                    mMncLength = MccTable.smallestDigitsMccForMnc(mcc);
+                    log("setting2 mMncLength=" + mMncLength);
+                } catch (NumberFormatException e) {
+                    loge("Corrupt IMSI! setting3 mMncLength=" + mMncLength);
+                }
+            }
+
+            if (mMncLength != UNKNOWN && mMncLength != UNINITIALIZED
+                    && imsi.length() >= 3 + mMncLength) {
+                log("update mccmnc=" + imsi.substring(0, 3 + mMncLength));
+                // finally have both the imsi and the mncLength and
+                // can parse the imsi properly
+                MccTable.updateMccMncConfiguration(mContext, imsi.substring(0, 3 + mMncLength));
+            }
+        }
+    }
+
     /**
      * Get the Network Access ID (NAI) on a CSIM for CDMA like networks. Default is null if IMSI is
      * not supported or unavailable.
diff --git a/src/java/com/android/internal/telephony/uicc/SIMRecords.java b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
index 10e651f..4f44f15 100755
--- a/src/java/com/android/internal/telephony/uicc/SIMRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
@@ -40,7 +40,6 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Objects;
 
 /**
  * {@hide}
@@ -172,31 +171,6 @@
     private static final int EVENT_APP_NETWORK_LOCKED = 3 + SYSTEM_EVENT_BASE;
 
 
-    // Lookup table for carriers known to produce SIMs which incorrectly indicate MNC length.
-
-    private static final String[] MCCMNC_CODES_HAVING_3DIGITS_MNC = {
-        "302370", "302720", "310260",
-        "405025", "405026", "405027", "405028", "405029", "405030", "405031", "405032",
-        "405033", "405034", "405035", "405036", "405037", "405038", "405039", "405040",
-        "405041", "405042", "405043", "405044", "405045", "405046", "405047", "405750",
-        "405751", "405752", "405753", "405754", "405755", "405756", "405799", "405800",
-        "405801", "405802", "405803", "405804", "405805", "405806", "405807", "405808",
-        "405809", "405810", "405811", "405812", "405813", "405814", "405815", "405816",
-        "405817", "405818", "405819", "405820", "405821", "405822", "405823", "405824",
-        "405825", "405826", "405827", "405828", "405829", "405830", "405831", "405832",
-        "405833", "405834", "405835", "405836", "405837", "405838", "405839", "405840",
-        "405841", "405842", "405843", "405844", "405845", "405846", "405847", "405848",
-        "405849", "405850", "405851", "405852", "405853", "405854", "405855", "405856",
-        "405857", "405858", "405859", "405860", "405861", "405862", "405863", "405864",
-        "405865", "405866", "405867", "405868", "405869", "405870", "405871", "405872",
-        "405873", "405874", "405875", "405876", "405877", "405878", "405879", "405880",
-        "405881", "405882", "405883", "405884", "405885", "405886", "405908", "405909",
-        "405910", "405911", "405912", "405913", "405914", "405915", "405916", "405917",
-        "405918", "405919", "405920", "405921", "405922", "405923", "405924", "405925",
-        "405926", "405927", "405928", "405929", "405930", "405931", "405932", "502142",
-        "502143", "502145", "502146", "502147", "502148"
-    };
-
     // ***** Constructor
 
     public SIMRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
@@ -656,7 +630,6 @@
                 /* IO events */
                 case EVENT_GET_IMSI_DONE:
                     isRecordLoadResponse = true;
-
                     ar = (AsyncResult) msg.obj;
 
                     if (ar.exception != null) {
@@ -664,69 +637,7 @@
                         break;
                     }
 
-                    String imsi = (String) ar.result;
-                    // Remove trailing F's if present in IMSI.
-                    mImsi = IccUtils.stripTrailingFs(imsi);
-
-                    if (!Objects.equals(mImsi, imsi)) {
-                        loge("Invalid IMSI padding digits received.");
-                    }
-
-                    if (mImsi != null && !mImsi.matches("[0-9]+")) {
-                        loge("Invalid non-numeric IMSI digits received.");
-                        mImsi = null;
-                    }
-
-                    // IMSI (MCC+MNC+MSIN) is at least 6 digits, but not more
-                    // than 15 (and usually 15).
-                    if (mImsi != null && (mImsi.length() < 6 || mImsi.length() > 15)) {
-                        loge("invalid IMSI " + mImsi);
-                        mImsi = null;
-                    }
-
-                    log("IMSI: mMncLength=" + mMncLength);
-
-                    if (mImsi != null && mImsi.length() >= 6) {
-                        log("IMSI: " + mImsi.substring(0, 6)
-                                + Rlog.pii(LOG_TAG, mImsi.substring(6)));
-                    }
-
-                    imsi = getIMSI();
-
-                    if (((mMncLength == UNKNOWN) || (mMncLength == 2))
-                            && ((imsi != null) && (imsi.length() >= 6))) {
-                        String mccmncCode = imsi.substring(0, 6);
-                        for (String mccmnc : MCCMNC_CODES_HAVING_3DIGITS_MNC) {
-                            if (mccmnc.equals(mccmncCode)) {
-                                mMncLength = 3;
-                                log("IMSI: setting1 mMncLength=" + mMncLength);
-                                break;
-                            }
-                        }
-                    }
-
-                    if (mMncLength == UNKNOWN) {
-                        // the SIM has told us all it knows, but it didn't know the mnc length.
-                        // guess using the mcc
-                        try {
-                            int mcc = Integer.parseInt(imsi.substring(0, 3));
-                            mMncLength = MccTable.smallestDigitsMccForMnc(mcc);
-                            log("setting2 mMncLength=" + mMncLength);
-                        } catch (NumberFormatException e) {
-                            mMncLength = UNKNOWN;
-                            loge("Corrupt IMSI! setting3 mMncLength=" + mMncLength);
-                        }
-                    }
-
-                    if (mMncLength != UNKNOWN && mMncLength != UNINITIALIZED
-                            && imsi.length() >= 3 + mMncLength) {
-                        log("update mccmnc=" + imsi.substring(0, 3 + mMncLength));
-                        // finally have both the imsi and the mncLength and
-                        // can parse the imsi properly
-                        MccTable.updateMccMncConfiguration(mContext,
-                                imsi.substring(0, 3 + mMncLength));
-                    }
-                    mImsiReadyRegistrants.notifyRegistrants();
+                    setImsi((String) ar.result);
                     break;
 
                 case EVENT_GET_MBI_DONE:
@@ -914,20 +825,10 @@
                     break;
 
                 case EVENT_GET_AD_DONE:
+                    isRecordLoadResponse = true;
+                    mMncLength = UNKNOWN;
                     try {
-                        isRecordLoadResponse = true;
-
-                        if (mCarrierTestOverride.isInTestMode() && getIMSI() != null) {
-                            imsi = getIMSI();
-                            try {
-                                int mcc = Integer.parseInt(imsi.substring(0, 3));
-                                mMncLength = MccTable.smallestDigitsMccForMnc(mcc);
-                                log("[TestMode] mMncLength=" + mMncLength);
-                            } catch (NumberFormatException e) {
-                                mMncLength = UNKNOWN;
-                                loge("[TestMode] Corrupt IMSI! mMncLength=" + mMncLength);
-                            }
-                        } else {
+                        if (!mCarrierTestOverride.isInTestMode()) {
                             ar = (AsyncResult) msg.obj;
                             data = (byte[]) ar.result;
 
@@ -947,62 +848,15 @@
                                 break;
                             }
 
-                            mMncLength = data[3] & 0xf;
-                            log("setting4 mMncLength=" + mMncLength);
-                        }
-
-                        if (mMncLength == 0xf) {
-                            mMncLength = UNKNOWN;
-                            log("setting5 mMncLength=" + mMncLength);
-                        } else if (mMncLength != 2 && mMncLength != 3) {
-                            mMncLength = UNINITIALIZED;
-                            log("setting5 mMncLength=" + mMncLength);
+                            int len = data[3] & 0xf;
+                            if (len == 2 || len == 3) {
+                                mMncLength = len;
+                            } else {
+                                log("Received invalid or unset MNC Length=" + len);
+                            }
                         }
                     } finally {
-
-                        // IMSI could be a value reading from Sim or a fake IMSI if in the test mode
-                        imsi = getIMSI();
-
-                        if (((mMncLength == UNINITIALIZED) || (mMncLength == UNKNOWN)
-                                    || (mMncLength == 2)) && ((imsi != null)
-                                    && (imsi.length() >= 6))) {
-                            String mccmncCode = imsi.substring(0, 6);
-                            log("mccmncCode=" + mccmncCode);
-                            for (String mccmnc : MCCMNC_CODES_HAVING_3DIGITS_MNC) {
-                                if (mccmnc.equals(mccmncCode)) {
-                                    mMncLength = 3;
-                                    log("setting6 mMncLength=" + mMncLength);
-                                    break;
-                                }
-                            }
-                        }
-
-                        if (mMncLength == UNKNOWN || mMncLength == UNINITIALIZED) {
-                            if (imsi != null) {
-                                try {
-                                    int mcc = Integer.parseInt(imsi.substring(0, 3));
-
-                                    mMncLength = MccTable.smallestDigitsMccForMnc(mcc);
-                                    log("setting7 mMncLength=" + mMncLength);
-                                } catch (NumberFormatException e) {
-                                    mMncLength = UNKNOWN;
-                                    loge("Corrupt IMSI! setting8 mMncLength=" + mMncLength);
-                                }
-                            } else {
-                                // Indicate we got this info, but it didn't contain the length.
-                                mMncLength = UNKNOWN;
-                                log("MNC length not present in EF_AD setting9 "
-                                        + "mMncLength=" + mMncLength);
-                            }
-                        }
-                        if (imsi != null && mMncLength != UNKNOWN
-                                && imsi.length() >= 3 + mMncLength) {
-                            // finally have both imsi and the length of the mnc and can parse
-                            // the imsi properly
-                            log("update mccmnc=" + imsi.substring(0, 3 + mMncLength));
-                            MccTable.updateMccMncConfiguration(mContext,
-                                    imsi.substring(0, 3 + mMncLength));
-                        }
+                        updateOperatorPlmn();
                     }
                     break;
 
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java b/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java
index b8b2818..636aa1c 100644
--- a/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java
+++ b/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java
@@ -660,9 +660,15 @@
                             .addChild(ctxParams1Builder)
                             .build().toHex());
                 }),
-                (byte[] response) ->
-                        parseResponseAndCheckSimpleError(response,
-                                EuiccCardErrorException.OPERATION_AUTHENTICATE_SERVER).toBytes(),
+                (byte[] response) -> {
+                    Asn1Node root = parseResponse(response);
+                    if (root.hasChild(Tags.TAG_CTX_COMP_1, Tags.TAG_UNI_2)) {
+                        throw new EuiccCardErrorException(
+                                EuiccCardErrorException.OPERATION_AUTHENTICATE_SERVER,
+                                root.getChild(Tags.TAG_CTX_COMP_1, Tags.TAG_UNI_2).asInteger());
+                    }
+                    return root.toBytes();
+                },
                 callback, handler);
     }
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
new file mode 100644
index 0000000..86c41c9
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/FakeTelephonyProvider.java
@@ -0,0 +1,146 @@
+/*
+ * 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.content.ContentUris;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.test.InstrumentationRegistry;
+import android.telephony.SubscriptionManager;
+import android.test.mock.MockContentProvider;
+import android.util.Log;
+
+public class FakeTelephonyProvider extends MockContentProvider {
+    static final String TAG = "FakeTelephonyProvider";
+
+    private InMemoryTelephonyProviderDbHelper mDbHelper =
+            new InMemoryTelephonyProviderDbHelper();
+
+    /**
+     * An in memory DB.
+     */
+    private class InMemoryTelephonyProviderDbHelper extends SQLiteOpenHelper {
+        InMemoryTelephonyProviderDbHelper() {
+            super(InstrumentationRegistry.getTargetContext(),
+                    null,    // db file name is null for in-memory db
+                    null,    // CursorFactory is null by default
+                    1);      // db version is no-op for tests
+            Log.d(TAG, "InMemoryTelephonyProviderDbHelper creating in-memory database");
+        }
+
+        // This should always be consistent with TelephonyProvider#getStringForSimInfoTableCreation.
+        private String getStringForSimInfoTableCreation(String tableName) {
+            return "CREATE TABLE " + tableName + "("
+                    + SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID
+                    + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+                    + SubscriptionManager.ICC_ID + " TEXT NOT NULL,"
+                    + SubscriptionManager.SIM_SLOT_INDEX
+                    + " INTEGER DEFAULT " + SubscriptionManager.SIM_NOT_INSERTED + ","
+                    + SubscriptionManager.DISPLAY_NAME + " TEXT,"
+                    + SubscriptionManager.CARRIER_NAME + " TEXT,"
+                    + SubscriptionManager.NAME_SOURCE
+                    + " INTEGER DEFAULT " + SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE + ","
+                    + SubscriptionManager.COLOR + " INTEGER DEFAULT "
+                    + SubscriptionManager.COLOR_DEFAULT + ","
+                    + SubscriptionManager.NUMBER + " TEXT,"
+                    + SubscriptionManager.DISPLAY_NUMBER_FORMAT
+                    + " INTEGER NOT NULL DEFAULT "
+                    + SubscriptionManager.DISPLAY_NUMBER_DEFAULT + ","
+                    + SubscriptionManager.DATA_ROAMING
+                    + " INTEGER DEFAULT " + SubscriptionManager.DATA_ROAMING_DEFAULT + ","
+                    + SubscriptionManager.MCC + " INTEGER DEFAULT 0,"
+                    + SubscriptionManager.MNC + " INTEGER DEFAULT 0,"
+                    + SubscriptionManager.MCC_STRING + " TEXT,"
+                    + SubscriptionManager.MNC_STRING + " TEXT,"
+                    + SubscriptionManager.SIM_PROVISIONING_STATUS
+                    + " INTEGER DEFAULT " + SubscriptionManager.SIM_PROVISIONED + ","
+                    + SubscriptionManager.IS_EMBEDDED + " INTEGER DEFAULT 0,"
+                    + SubscriptionManager.CARD_ID + " TEXT NOT NULL,"
+                    + SubscriptionManager.ACCESS_RULES + " BLOB,"
+                    + SubscriptionManager.IS_REMOVABLE + " INTEGER DEFAULT 0,"
+                    + SubscriptionManager.CB_EXTREME_THREAT_ALERT + " INTEGER DEFAULT 1,"
+                    + SubscriptionManager.CB_SEVERE_THREAT_ALERT + " INTEGER DEFAULT 1,"
+                    + SubscriptionManager.CB_AMBER_ALERT + " INTEGER DEFAULT 1,"
+                    + SubscriptionManager.CB_EMERGENCY_ALERT + " INTEGER DEFAULT 1,"
+                    + SubscriptionManager.CB_ALERT_SOUND_DURATION + " INTEGER DEFAULT 4,"
+                    + SubscriptionManager.CB_ALERT_REMINDER_INTERVAL + " INTEGER DEFAULT 0,"
+                    + SubscriptionManager.CB_ALERT_VIBRATE + " INTEGER DEFAULT 1,"
+                    + SubscriptionManager.CB_ALERT_SPEECH + " INTEGER DEFAULT 1,"
+                    + SubscriptionManager.CB_ETWS_TEST_ALERT + " INTEGER DEFAULT 0,"
+                    + SubscriptionManager.CB_CHANNEL_50_ALERT + " INTEGER DEFAULT 1,"
+                    + SubscriptionManager.CB_CMAS_TEST_ALERT + " INTEGER DEFAULT 0,"
+                    + SubscriptionManager.CB_OPT_OUT_DIALOG + " INTEGER DEFAULT 1,"
+                    + SubscriptionManager.ENHANCED_4G_MODE_ENABLED + " INTEGER DEFAULT -1,"
+                    + SubscriptionManager.VT_IMS_ENABLED + " INTEGER DEFAULT -1,"
+                    + SubscriptionManager.WFC_IMS_ENABLED + " INTEGER DEFAULT -1,"
+                    + SubscriptionManager.WFC_IMS_MODE + " INTEGER DEFAULT -1,"
+                    + SubscriptionManager.WFC_IMS_ROAMING_MODE + " INTEGER DEFAULT -1,"
+                    + SubscriptionManager.WFC_IMS_ROAMING_ENABLED + " INTEGER DEFAULT -1,"
+                    + SubscriptionManager.IS_OPPORTUNISTIC + " INTEGER DEFAULT 0,"
+                    + SubscriptionManager.PARENT_SUB_ID + " INTEGER DEFAULT -1"
+                    + ");";
+        }
+
+        @Override
+        public void onCreate(SQLiteDatabase db) {
+            // TODO: set up other tables when needed.
+            // set up the siminfo table
+            Log.d(TAG, "InMemoryTelephonyProviderDbHelper onCreate creating the siminfo table");
+            db.execSQL(getStringForSimInfoTableCreation("siminfo"));
+        }
+
+        @Override
+        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+            Log.d(TAG, "InMemoryTelephonyProviderDbHelper onUpgrade doing nothing");
+            return;
+        }
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        SQLiteDatabase db = mDbHelper.getWritableDatabase();
+        long id = db.insert("siminfo", null, values);
+        return ContentUris.withAppendedId(SubscriptionManager.CONTENT_URI, id);
+    }
+
+    @Override
+    public synchronized int delete(Uri url, String where, String[] whereArgs) {
+        return mDbHelper.getWritableDatabase().delete("siminfo", where, whereArgs);
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+            String sortOrder) {
+        return mDbHelper.getReadableDatabase().query("siminfo", projection, selection,
+                selectionArgs, null, null, sortOrder);
+    }
+
+    @Override
+    public Bundle call(String method, String request, Bundle args) {
+        return null;
+    }
+
+    @Override
+    public final int update(Uri uri, ContentValues values, String where,
+            String[] selectionArgs) {
+        return mDbHelper.getWritableDatabase().update("siminfo", values, where, selectionArgs);
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java
index 6e6854b..76ead95 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ImsSmsDispatcherTest.java
@@ -26,7 +26,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.os.HandlerThread;
+import android.os.Looper;
 import android.telephony.SmsMessage;
 import android.telephony.ims.stub.ImsSmsImplBase;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -44,33 +44,19 @@
     @Mock private SMSDispatcher.SmsTracker mSmsTracker;
     private HashMap<String, Object> mTrackerData;
     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));
-            when(mSmsDispatchersController.isIms()).thenReturn(true);
-            setReady(true);
-        }
-    }
 
     @Before
     public void setUp() throws Exception {
         super.setUp(getClass().getSimpleName());
+        if (Looper.myLooper() == null) {
+            Looper.prepare();
+        }
 
-        mImsSmsDispatcherTestHandler = new ImsSmsDispatcherTestHandler(TAG);
-        mImsSmsDispatcherTestHandler.start();
+        mImsSmsDispatcher = spy(new ImsSmsDispatcher(mPhone, mSmsDispatchersController));
+        when(mSmsDispatchersController.isIms()).thenReturn(true);
 
-        mTrackerData = new HashMap(1);
+        mTrackerData = new HashMap<>(1);
         when(mSmsTracker.getData()).thenReturn(mTrackerData);
-        waitUntilReady();
     }
 
     /**
@@ -93,7 +79,6 @@
 
         assertEquals(token + 1, mImsSmsDispatcher.mNextToken.get());
         assertEquals(trackersSize + 1, mImsSmsDispatcher.mTrackers.size());
-        ArgumentCaptor<byte[]> byteCaptor = ArgumentCaptor.forClass(byte[].class);
         verify(mImsManager).sendSms(eq(token + 1), anyInt(), eq(SmsMessage.FORMAT_3GPP),
                 nullable(String.class), eq(false), eq(pdu));
     }
@@ -155,8 +140,6 @@
     @After
     public void tearDown() throws Exception {
         mImsSmsDispatcher = null;
-        mImsSmsDispatcherTestHandler.quit();
-        mImsSmsDispatcherTestHandler.join();
         super.tearDown();
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
index 606ed21..489a19f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionControllerTest.java
@@ -15,24 +15,26 @@
  */
 package com.android.internal.telephony;
 
-import static org.junit.Assert.*;
-import static org.mockito.Mockito.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 
-import android.app.AppOpsManager;
-import android.content.ContentValues;
 import android.content.Intent;
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.net.Uri;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
-import android.test.mock.MockContentProvider;
 import android.test.mock.MockContentResolver;
 import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Log;
 
 import org.junit.After;
 import org.junit.Before;
@@ -40,117 +42,15 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
-import java.util.ArrayList;
 import java.util.List;
 
 public class SubscriptionControllerTest extends TelephonyTest {
-
     private static final int SINGLE_SIM = 1;
     private String mCallingPackage;
     private SubscriptionController mSubscriptionControllerUT;
     private MockContentResolver mMockContentResolver;
-
-    @Mock private List<SubscriptionInfo> mSubList;
-    @Mock private AppOpsManager mAppOps;
-
-    public class FakeSubscriptionContentProvider extends MockContentProvider {
-
-        private ArrayList<ContentValues> mSubscriptionArray =
-                new ArrayList<ContentValues>();
-
-        private String[] mKeyMappingSet = new String[]{
-                SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID,
-                SubscriptionManager.ICC_ID, SubscriptionManager.SIM_SLOT_INDEX,
-                SubscriptionManager.DISPLAY_NAME, SubscriptionManager.CARRIER_NAME,
-                SubscriptionManager.NAME_SOURCE, SubscriptionManager.COLOR,
-                SubscriptionManager.NUMBER, SubscriptionManager.DISPLAY_NUMBER_FORMAT,
-                SubscriptionManager.DATA_ROAMING, SubscriptionManager.MCC,
-                SubscriptionManager.MNC, SubscriptionManager.MCC_STRING,
-                SubscriptionManager.MNC_STRING,
-                SubscriptionManager.CB_EXTREME_THREAT_ALERT,
-                SubscriptionManager.CB_SEVERE_THREAT_ALERT, SubscriptionManager.CB_AMBER_ALERT,
-                SubscriptionManager.CB_ALERT_SOUND_DURATION,
-                SubscriptionManager.CB_ALERT_REMINDER_INTERVAL,
-                SubscriptionManager.CB_ALERT_VIBRATE, SubscriptionManager.CB_ALERT_SPEECH,
-                SubscriptionManager.CB_ETWS_TEST_ALERT, SubscriptionManager.CB_CHANNEL_50_ALERT,
-                SubscriptionManager.CB_CMAS_TEST_ALERT, SubscriptionManager.CB_OPT_OUT_DIALOG,
-                SubscriptionManager.SIM_PROVISIONING_STATUS, SubscriptionManager.IS_EMBEDDED,
-                SubscriptionManager.ACCESS_RULES, SubscriptionManager.ENHANCED_4G_MODE_ENABLED,
-                SubscriptionManager.VT_IMS_ENABLED, SubscriptionManager.WFC_IMS_ENABLED,
-                SubscriptionManager.WFC_IMS_MODE, SubscriptionManager.WFC_IMS_ROAMING_MODE,
-                SubscriptionManager.WFC_IMS_ROAMING_ENABLED,
-                SubscriptionManager.CARD_ID, SubscriptionManager.IS_OPPORTUNISTIC,
-                SubscriptionManager.PARENT_SUB_ID
-        };
-
-        /* internal util function */
-        private MatrixCursor convertFromContentToCursor(ContentValues initialValues,
-                String[] projection) {
-            MatrixCursor cursor = null;
-            ArrayList<Object> values = new ArrayList<Object>();
-            if (projection == null) {
-                projection = mKeyMappingSet;
-            }
-            if (initialValues != null && projection.length != 0) {
-                cursor = new MatrixCursor(projection);
-                /* push value from contentValues to matrixCursors */
-                for (String key : projection) {
-                    if (initialValues.containsKey(key)) {
-                        values.add(initialValues.get(key));
-                    } else {
-                        values.add(null);
-                    }
-                }
-            }
-            cursor.addRow(values.toArray());
-            return cursor;
-        }
-
-        @Override
-        public int delete(Uri uri, String selection, String[] selectionArgs) {
-            if (mSubscriptionArray.size() > 0) {
-                mSubscriptionArray.remove(0);
-                return 1;
-            }
-            return -1;
-        }
-
-        @Override
-        public Uri insert(Uri uri, ContentValues values) {
-            values.put(SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID, 0);
-            mSubscriptionArray.add(values);
-            return uri;
-        }
-
-        @Override
-        public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
-                            String sortOrder) {
-            if (mSubscriptionArray.size() > 0) {
-                return convertFromContentToCursor(mSubscriptionArray.get(0), projection);
-            }
-            return null;
-        }
-
-        @Override
-        public Bundle call(String method, String request, Bundle args) {
-            return null;
-        }
-
-        @Override
-        public int update(android.net.Uri uri, android.content.ContentValues values,
-                          java.lang.String selection, java.lang.String[] selectionArgs) {
-            if (mSubscriptionArray.size() > 0) {
-                ContentValues val = mSubscriptionArray.get(0);
-                for (String key : values.keySet()) {
-                    val.put(key, values.getAsString(key));
-                    Log.d(TAG, "update the values..." + key + "..." + values.getAsString(key));
-                }
-                mSubscriptionArray.set(0, val);
-                return 1;
-            }
-            return -1;
-        }
-    }
+    @Mock
+    private ITelephonyRegistry.Stub mTelephonyRegisteryMock;
 
     @Before
     public void setUp() throws Exception {
@@ -171,7 +71,8 @@
         mSubscriptionControllerUT.getInstance().updatePhonesAvailability(new Phone[]{mPhone});
         mMockContentResolver = (MockContentResolver) mContext.getContentResolver();
         mMockContentResolver.addProvider(SubscriptionManager.CONTENT_URI.getAuthority(),
-                new FakeSubscriptionContentProvider());
+                new FakeTelephonyProvider());
+
     }
 
     @After
@@ -179,6 +80,10 @@
         /* should clear fake content provider and resolver here */
         mContext.getContentResolver().delete(SubscriptionManager.CONTENT_URI, null, null);
 
+        /*clear sub info in mSubscriptionControllerUT since they will otherwise be persistent
+         * between each test case. */
+        mSubscriptionControllerUT.clearSubInfo();
+
         /* clear settings for default voice/data/sms sub ID */
         Settings.Global.putInt(mContext.getContentResolver(),
                 Settings.Global.MULTI_SIM_VOICE_CALL_SUBSCRIPTION,
@@ -196,11 +101,10 @@
 
     @Test @SmallTest
     public void testInsertSim() {
-        int slotID = mSubscriptionControllerUT.getAllSubInfoCount(mCallingPackage);
-
         //verify there is no sim inserted in the SubscriptionManager
-        assertEquals(0, slotID);
+        assertEquals(0, mSubscriptionControllerUT.getAllSubInfoCount(mCallingPackage));
 
+        int slotID = 0;
         //insert one Subscription Info
         mSubscriptionControllerUT.addSubInfoRecord("test", slotID);
 
@@ -286,11 +190,11 @@
     @Test @SmallTest
     public void testCleanUpSIM() {
         testInsertSim();
-        assertFalse(mSubscriptionControllerUT.isActiveSubId(1));
+        assertFalse(mSubscriptionControllerUT.isActiveSubId(2));
         mSubscriptionControllerUT.clearSubInfo();
-        assertFalse(mSubscriptionControllerUT.isActiveSubId(0));
+        assertFalse(mSubscriptionControllerUT.isActiveSubId(1));
         assertEquals(SubscriptionManager.SIM_NOT_INSERTED,
-                mSubscriptionControllerUT.getSlotIndex(0));
+                mSubscriptionControllerUT.getSlotIndex(1));
     }
 
     @Test @SmallTest
@@ -316,10 +220,10 @@
     public void testSetGetMCCMNC() {
         testInsertSim();
         String mCcMncVERIZON = "310004";
-        mSubscriptionControllerUT.setMccMnc(mCcMncVERIZON, 0);
+        mSubscriptionControllerUT.setMccMnc(mCcMncVERIZON, 1);
 
         SubscriptionInfo subInfo = mSubscriptionControllerUT
-                .getActiveSubscriptionInfo(0, mCallingPackage);
+                .getActiveSubscriptionInfo(1, mCallingPackage);
         assertNotNull(subInfo);
         assertEquals(Integer.parseInt(mCcMncVERIZON.substring(0, 3)), subInfo.getMcc());
         assertEquals(Integer.parseInt(mCcMncVERIZON.substring(3)), subInfo.getMnc());
@@ -429,4 +333,62 @@
                 SubscriptionManager.WFC_IMS_ROAMING_MODE,
                 mCallingPackage));
     }
+
+
+    @Test
+    @SmallTest
+    public void testOpptSubInfoListChanged() throws Exception {
+        registerMockTelephonyRegistry();
+        verify(mTelephonyRegisteryMock, times(0))
+                .notifyOpportunisticSubscriptionInfoChanged();
+
+        testInsertSim();
+        testInsertSim2();
+
+        // Neither sub1 or sub2 are opportunistic. So getOpportunisticSubscriptions
+        // should return empty list and no callback triggered.
+        List<SubscriptionInfo> opptSubList = mSubscriptionControllerUT
+                .getOpportunisticSubscriptions(0, mCallingPackage);
+
+        assertTrue(opptSubList.isEmpty());
+        verify(mTelephonyRegisteryMock, times(0))
+                .notifyOpportunisticSubscriptionInfoChanged();
+
+        // Setting sub2 as opportunistic should trigger callback.
+        mSubscriptionControllerUT.setOpportunistic(true, 2);
+
+        verify(mTelephonyRegisteryMock, times(1))
+                .notifyOpportunisticSubscriptionInfoChanged();
+        opptSubList = mSubscriptionControllerUT
+                .getOpportunisticSubscriptions(0, mCallingPackage);
+        assertEquals(1, opptSubList.size());
+        assertEquals("test2", opptSubList.get(0).getIccId());
+
+        // Changing non-opportunistic sub1 shouldn't trigger callback.
+        mSubscriptionControllerUT.setDisplayName("DisplayName", 1);
+        verify(mTelephonyRegisteryMock, times(1))
+                .notifyOpportunisticSubscriptionInfoChanged();
+
+        mSubscriptionControllerUT.setDisplayName("DisplayName", 2);
+        verify(mTelephonyRegisteryMock, times(2))
+                .notifyOpportunisticSubscriptionInfoChanged();
+    }
+
+    private void testInsertSim2() {
+        // verify there's already a SIM profile added.
+        assertEquals(1, mSubscriptionControllerUT.getAllSubInfoCount(mCallingPackage));
+
+        int slotID = 0;
+        //insert one Subscription Info
+        mSubscriptionControllerUT.addSubInfoRecord("test2", slotID);
+
+        //verify there is one sim
+        assertEquals(2, mSubscriptionControllerUT.getAllSubInfoCount(mCallingPackage));
+    }
+
+    private void registerMockTelephonyRegistry() {
+        mServiceManagerMockedServices.put("telephony.registry", mTelephonyRegisteryMock);
+        doReturn(mTelephonyRegisteryMock).when(mTelephonyRegisteryMock)
+                .queryLocalInterface(anyString());
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java
index 2498b78..86df4f5 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoTest.java
@@ -16,6 +16,7 @@
 package com.android.internal.telephony;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 
 import android.telephony.SubscriptionInfo;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -78,4 +79,22 @@
         mSubscriptionInfoUT.setIconTint(0);
         assertEquals(0, mSubscriptionInfoUT.getIconTint());
     }
+
+    @Test
+    @SmallTest
+    public void testEquals() {
+        SubscriptionInfo copiedInfo = new SubscriptionInfo(1, "890126042XXXXXXXXXXX", 0,
+                "T-mobile", "T-mobile", 0, 255, "12345", 0, null,
+                "310", "260", "156", false, null, null);
+        SubscriptionInfo differentDisplayName = new SubscriptionInfo(1, "890126042XXXXXXXXXXX", 0,
+                "AT&T", "T-mobile", 0, 255, "12345", 0, null,
+                "310", "260", "156", false, null, null);
+        SubscriptionInfo differentSubId = new SubscriptionInfo(2, "890126042XXXXXXXXXXX", 0,
+                "AT&T", "T-mobile", 0, 255, "12345", 0, null,
+                "310", "260", "156", false, null, null);
+
+        assertEquals(mSubscriptionInfoUT, copiedInfo);
+        assertNotEquals(mSubscriptionInfoUT, differentDisplayName);
+        assertNotEquals(mSubscriptionInfoUT, differentSubId);
+    }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
index ab26c56..6e4257e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/TelephonyTest.java
@@ -19,7 +19,6 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
 import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyLong;
 import static org.mockito.Matchers.nullable;
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doAnswer;
@@ -35,7 +34,6 @@
 import android.content.SharedPreferences;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.database.Cursor;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -165,8 +163,6 @@
     @Mock
     protected IActivityManager mIActivityManager;
     @Mock
-    protected InboundSmsTracker mInboundSmsTracker;
-    @Mock
     protected IIntentSender mIIntentSender;
     @Mock
     protected IBinder mIBinder;
@@ -350,16 +346,6 @@
                 .makeDcTracker(nullable(Phone.class));
         doReturn(mWspTypeDecoder).when(mTelephonyComponentFactory)
                 .makeWspTypeDecoder(nullable(byte[].class));
-        doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
-                .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
-                        anyBoolean(), nullable(String.class), nullable(String.class),
-                        nullable(String.class));
-        doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
-                .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
-                        nullable(String.class), nullable(String.class), anyInt(), anyInt(),
-                        anyInt(), anyBoolean(), nullable(String.class));
-        doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
-                .makeInboundSmsTracker(nullable(Cursor.class), anyBoolean());
         doReturn(mImsCT).when(mTelephonyComponentFactory)
                 .makeImsPhoneCallTracker(nullable(ImsPhone.class));
         doReturn(mCdmaSSM).when(mTelephonyComponentFactory)
diff --git a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java
index 35d2bb0..08e54ac 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaInboundSmsHandlerTest.java
@@ -21,6 +21,8 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.anyBoolean;
 import static org.mockito.Mockito.anyInt;
@@ -29,9 +31,9 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
-import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
+import android.database.Cursor;
 import android.os.AsyncResult;
 import android.os.HandlerThread;
 import android.os.RemoteException;
@@ -44,10 +46,10 @@
 
 import com.android.internal.telephony.FakeSmsContentProvider;
 import com.android.internal.telephony.InboundSmsHandler;
+import com.android.internal.telephony.InboundSmsTracker;
 import com.android.internal.telephony.SmsStorageMonitor;
 import com.android.internal.telephony.TelephonyTest;
 import com.android.internal.telephony.cdma.sms.SmsEnvelope;
-import com.android.internal.util.HexDump;
 import com.android.internal.util.IState;
 import com.android.internal.util.StateMachine;
 
@@ -72,8 +74,9 @@
     private CdmaInboundSmsHandler mCdmaInboundSmsHandler;
     private CdmaInboundSmsHandlerTestHandler mCdmaInboundSmsHandlerTestHandler;
     private SmsEnvelope mSmsEnvelope = new SmsEnvelope();
-    private ContentValues mInboundSmsTrackerCV = new ContentValues();
     private FakeSmsContentProvider mContentProvider;
+    private InboundSmsTracker mInboundSmsTracker;
+    private byte[] mSmsPdu = new byte[]{(byte) 0xFF, (byte) 0xFF, (byte) 0xFF};
 
     private class CdmaInboundSmsHandlerTestHandler extends HandlerThread {
 
@@ -118,28 +121,32 @@
             fail("Unexpected RemoteException: " + re.getStackTrace());
         }
 
-        byte[] smsPdu = new byte[]{(byte)0xFF, (byte)0xFF, (byte)0xFF};
         mSmsMessage.mWrappedSmsMessage = mCdmaSmsMessage;
-        doReturn(smsPdu).when(mCdmaSmsMessage).getPdu();
-
-        mInboundSmsTrackerCV.put("destination_port", 1 << 16);
-        mInboundSmsTrackerCV.put("pdu", HexDump.toHexString(smsPdu));
-        mInboundSmsTrackerCV.put("address", "1234567890");
-        mInboundSmsTrackerCV.put("reference_number", 1);
-        mInboundSmsTrackerCV.put("sequence", 1);
-        mInboundSmsTrackerCV.put("count", 1);
-        mInboundSmsTrackerCV.put("date", System.currentTimeMillis());
-        mInboundSmsTrackerCV.put("message_body", "This is the message body of a single-part " +
-                "message");
+        doReturn(mSmsPdu).when(mCdmaSmsMessage).getPdu();
 
         doReturn(true).when(mTelephonyManager).getSmsReceiveCapableForPhone(anyInt(), anyBoolean());
         doReturn(true).when(mSmsStorageMonitor).isStorageAvailable();
-        doReturn(1).when(mInboundSmsTracker).getMessageCount();
-        doReturn(-1).when(mInboundSmsTracker).getDestPort();
-        doReturn(mInboundSmsTrackerCV).when(mInboundSmsTracker).getContentValues();
-        doReturn(smsPdu).when(mInboundSmsTracker).getPdu();
-        doReturn("This is the message body").when(mInboundSmsTracker).getMessageBody();
-        doReturn("1234567890").when(mInboundSmsTracker).getAddress();
+
+        mInboundSmsTracker = new InboundSmsTracker(
+            mSmsPdu, /* pdu */
+            System.currentTimeMillis(), /* timestamp */
+            -1, /* destPort */
+            true, /* is3gpp2 */
+            false, /* is3gpp2WapPdu */
+            "1234567890", /* address */
+            "1234567890", /* displayAddress */
+            "This is the message body of a single-part message" /* messageBody */);
+
+        doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
+                .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
+                anyBoolean(), nullable(String.class), nullable(String.class),
+                nullable(String.class));
+        doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
+                .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
+                nullable(String.class), nullable(String.class), anyInt(), anyInt(),
+                anyInt(), anyBoolean(), nullable(String.class));
+        doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
+                .makeInboundSmsTracker(nullable(Cursor.class), anyBoolean());
 
         mContentProvider = new FakeSmsContentProvider();
         ((MockContentResolver)mContext.getContentResolver()).addProvider(
@@ -214,7 +221,19 @@
     @MediumTest
     public void testNewSmsFromBlockedNumber_noBroadcastsSent() {
         String blockedNumber = "123456789";
-        doReturn(blockedNumber).when(mInboundSmsTracker).getDisplayAddress();
+        mInboundSmsTracker = new InboundSmsTracker(
+            mSmsPdu, /* pdu */
+            System.currentTimeMillis(), /* timestamp */
+            -1, /* destPort */
+            true, /* is3gpp2 */
+            false, /* is3gpp2WapPdu */
+            "1234567890", /* address */
+            blockedNumber, /* displayAddress */
+            "This is the message body of a single-part message" /* messageBody */);
+        doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
+                .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
+                anyBoolean(), nullable(String.class), nullable(String.class),
+                nullable(String.class));
         mFakeBlockedNumberContentProvider.mBlockedNumbers.add(blockedNumber);
 
         transitionFromStartupToIdle();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
index 12453ed..851a217 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
@@ -64,7 +64,6 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
@@ -80,11 +79,13 @@
     private SmsMessage mGsmSmsMessage;
     @Mock
     private SmsHeader mSmsHeader;
-    @Mock
+    private InboundSmsTracker mInboundSmsTracker;
     private InboundSmsTracker mInboundSmsTrackerPart1;
-    @Mock
     private InboundSmsTracker mInboundSmsTrackerPart2;
     @Mock
+    private InboundSmsTracker mMockInboundSmsTracker;
+    private ContentValues mInboundSmsTrackerCV;
+    @Mock
     private CdmaInboundSmsHandler mCdmaInboundSmsHandler;
 
     private GsmInboundSmsHandler mGsmInboundSmsHandler;
@@ -94,11 +95,8 @@
     private static final String RAW_TABLE_NAME = "raw";
     private static final Uri sRawUri = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI,
             RAW_TABLE_NAME);
+    private static final int TEST_TIMEOUT = 5000;
 
-    private ContentValues mInboundSmsTrackerCV = new ContentValues();
-    // For multi-part SMS
-    private ContentValues mInboundSmsTrackerCVPart1;
-    private ContentValues mInboundSmsTrackerCVPart2;
     private String mMessageBody = "This is the message body of a single-part message";
     private String mMessageBodyPart1 = "This is the first part of a multi-part message";
     private String mMessageBodyPart2 = "This is the second part of a multi-part message";
@@ -130,6 +128,38 @@
         }
     }
 
+    /**
+     * This is used only for InboundSmsTracker constructed through Cursor. For other cases
+     * real objects should be used. This should be used only for tests related to
+     * SmsBroadcastUndelivered.
+     */
+    private void createMockInboundSmsTracker() {
+        mInboundSmsTrackerCV = new ContentValues();
+        mInboundSmsTrackerCV.put("destination_port", InboundSmsTracker.DEST_PORT_FLAG_NO_PORT);
+        mInboundSmsTrackerCV.put("pdu", HexDump.toHexString(mSmsPdu));
+        mInboundSmsTrackerCV.put("address", "1234567890");
+        mInboundSmsTrackerCV.put("reference_number", 1);
+        mInboundSmsTrackerCV.put("sequence", 1);
+        mInboundSmsTrackerCV.put("count", 1);
+        mInboundSmsTrackerCV.put("date", System.currentTimeMillis());
+        mInboundSmsTrackerCV.put("message_body", mMessageBody);
+        mInboundSmsTrackerCV.put("display_originating_addr", "1234567890");
+
+        doReturn(1).when(mMockInboundSmsTracker).getMessageCount();
+        doReturn(1).when(mMockInboundSmsTracker).getReferenceNumber();
+        doReturn("1234567890").when(mMockInboundSmsTracker).getAddress();
+        doReturn(1).when(mMockInboundSmsTracker).getSequenceNumber();
+        doReturn(1).when(mMockInboundSmsTracker).getIndexOffset();
+        doReturn(-1).when(mMockInboundSmsTracker).getDestPort();
+        doReturn(mMessageBody).when(mMockInboundSmsTracker).getMessageBody();
+        doReturn(mSmsPdu).when(mMockInboundSmsTracker).getPdu();
+        doReturn(mInboundSmsTrackerCV.get("date")).when(mMockInboundSmsTracker).getTimestamp();
+        doReturn(mInboundSmsTrackerCV).when(mMockInboundSmsTracker).getContentValues();
+
+        doReturn(mMockInboundSmsTracker).when(mTelephonyComponentFactory)
+            .makeInboundSmsTracker(nullable(Cursor.class), anyBoolean());
+    }
+
     @Before
     public void setUp() throws Exception {
         super.setUp("GsmInboundSmsHandlerTest");
@@ -147,26 +177,22 @@
         }
 
         mSmsMessage.mWrappedSmsMessage = mGsmSmsMessage;
-        mInboundSmsTrackerCV.put("destination_port", InboundSmsTracker.DEST_PORT_FLAG_NO_PORT);
-        mInboundSmsTrackerCV.put("pdu", HexDump.toHexString(mSmsPdu));
-        mInboundSmsTrackerCV.put("address", "1234567890");
-        mInboundSmsTrackerCV.put("reference_number", 1);
-        mInboundSmsTrackerCV.put("sequence", 1);
-        mInboundSmsTrackerCV.put("count", 1);
-        mInboundSmsTrackerCV.put("date", System.currentTimeMillis());
-        mInboundSmsTrackerCV.put("message_body", mMessageBody);
-        mInboundSmsTrackerCV.put("display_originating_addr", "1234567890");
 
-        doReturn(1).when(mInboundSmsTracker).getMessageCount();
-        doReturn(1).when(mInboundSmsTracker).getReferenceNumber();
-        doReturn("1234567890").when(mInboundSmsTracker).getAddress();
-        doReturn(1).when(mInboundSmsTracker).getSequenceNumber();
-        doReturn(1).when(mInboundSmsTracker).getIndexOffset();
-        doReturn(-1).when(mInboundSmsTracker).getDestPort();
-        doReturn(mMessageBody).when(mInboundSmsTracker).getMessageBody();
-        doReturn(mSmsPdu).when(mInboundSmsTracker).getPdu();
-        doReturn(mInboundSmsTrackerCV.get("date")).when(mInboundSmsTracker).getTimestamp();
-        doReturn(mInboundSmsTrackerCV).when(mInboundSmsTracker).getContentValues();
+        mInboundSmsTracker = new InboundSmsTracker(
+            mSmsPdu, /* pdu */
+            System.currentTimeMillis(), /* timestamp */
+            -1, /* destPort */
+            false, /* is3gpp2 */
+            false, /* is3gpp2WapPdu */
+            "1234567890", /* address */
+            "1234567890", /* displayAddress */
+            mMessageBody /* messageBody */);
+        doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
+                .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
+                anyBoolean(), nullable(String.class), nullable(String.class),
+                nullable(String.class));
+
+        createMockInboundSmsTracker();
 
         mContentProvider = new FakeSmsContentProvider();
         ((MockContentResolver)mContext.getContentResolver()).addProvider(
@@ -189,6 +215,7 @@
         mGsmInboundSmsHandler = null;
         mContentProvider.shutdown();
         mGsmInboundSmsHandlerTestHandler.quit();
+        mGsmInboundSmsHandlerTestHandler.join();
         super.tearDown();
     }
 
@@ -198,7 +225,7 @@
 
         // trigger transition to IdleState
         mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_START_ACCEPTING_SMS);
-        waitForMs(50);
+        waitForHandlerAction(mGsmInboundSmsHandler.getHandler(), TEST_TIMEOUT);
 
         assertEquals("IdleState", getCurrentState().getName());
     }
@@ -221,29 +248,37 @@
         assertEquals("WaitingState", getCurrentState().getName());
 
         mContextFixture.sendBroadcastToOrderedBroadcastReceivers();
-        waitForMs(50);
-
+        // handle broadcast complete msg
+        waitForHandlerAction(mGsmInboundSmsHandler.getHandler(), TEST_TIMEOUT);
+        // transition from waiting state to delivering state
+        waitForHandlerAction(mGsmInboundSmsHandler.getHandler(), TEST_TIMEOUT);
+        // transition from delivering state to idle state
+        waitForHandlerAction(mGsmInboundSmsHandler.getHandler(), TEST_TIMEOUT);
         assertEquals("IdleState", getCurrentState().getName());
     }
 
+    private void sendNewSms() {
+        mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS,
+                new AsyncResult(null, mSmsMessage, null));
+        // handle EVENT_NEW_SMS
+        waitForHandlerAction(mGsmInboundSmsHandler.getHandler(), TEST_TIMEOUT);
+        // handle EVENT_BROADCAST_SMS
+        waitForHandlerAction(mGsmInboundSmsHandler.getHandler(), TEST_TIMEOUT);
+    }
+
     @FlakyTest
-    @Ignore
     @Test
     @MediumTest
     public void testNewSms() {
         transitionFromStartupToIdle();
 
         // send new SMS to state machine and verify that triggers SMS_DELIVER_ACTION
-        mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS,
-                new AsyncResult(null, mSmsMessage, null));
-        waitForMs(100);
+        sendNewSms();
 
         verifySmsIntentBroadcasts(0);
 
         // send same SMS again, verify no broadcasts are sent
-        mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS,
-                new AsyncResult(null, mSmsMessage, null));
-        waitForMs(100);
+        sendNewSms();
 
         verify(mContext, times(2)).sendBroadcast(any(Intent.class));
         assertEquals("IdleState", getCurrentState().getName());
@@ -253,14 +288,11 @@
     @MediumTest
     public void testNewSmsFromBlockedNumber_noBroadcastsSent() {
         String blockedNumber = "1234567890";
-        doReturn(blockedNumber).when(mInboundSmsTracker).getDisplayAddress();
         mFakeBlockedNumberContentProvider.mBlockedNumbers.add(blockedNumber);
 
         transitionFromStartupToIdle();
 
-        mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS,
-                new AsyncResult(null, mSmsMessage, null));
-        waitForMs(100);
+        sendNewSms();
 
         verify(mContext, never()).sendBroadcast(any(Intent.class));
         assertEquals("IdleState", getCurrentState().getName());
@@ -275,7 +307,12 @@
         assertEquals("WaitingState", getCurrentState().getName());
 
         mContextFixture.sendBroadcastToOrderedBroadcastReceivers();
-        waitForMs(50);
+        // handle broadcast complete msg
+        waitForHandlerAction(mGsmInboundSmsHandler.getHandler(), TEST_TIMEOUT);
+        // transition from waiting state to delivering state
+        waitForHandlerAction(mGsmInboundSmsHandler.getHandler(), TEST_TIMEOUT);
+        // transition from delivering state to idle state
+        waitForHandlerAction(mGsmInboundSmsHandler.getHandler(), TEST_TIMEOUT);
 
         assertEquals("IdleState", getCurrentState().getName());
     }
@@ -285,23 +322,36 @@
     public void testBroadcastSms() {
         transitionFromStartupToIdle();
 
-        doReturn(0).when(mInboundSmsTracker).getDestPort();
+        mInboundSmsTracker = new InboundSmsTracker(
+                mSmsPdu, /* pdu */
+                System.currentTimeMillis(), /* timestamp */
+                0, /* destPort */
+                false, /* is3gpp2 */
+                false, /* is3gpp2WapPdu */
+                "1234567890", /* address */
+                "1234567890", /* displayAddress */
+                mMessageBody /* messageBody */);
+        doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
+                .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
+                anyBoolean(), nullable(String.class), nullable(String.class),
+                nullable(String.class));
         mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_BROADCAST_SMS,
                 mInboundSmsTracker);
-        waitForMs(100);
+        waitForHandlerAction(mGsmInboundSmsHandler.getHandler(), TEST_TIMEOUT);
+        waitForHandlerAction(mGsmInboundSmsHandler.getHandler(), TEST_TIMEOUT);
 
         verifyDataSmsIntentBroadcasts(0);
 
         // send same data sms again, and since it's not text sms it should be broadcast again
         mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_BROADCAST_SMS,
                 mInboundSmsTracker);
-        waitForMs(100);
+        waitForHandlerAction(mGsmInboundSmsHandler.getHandler(), TEST_TIMEOUT);
+        waitForHandlerAction(mGsmInboundSmsHandler.getHandler(), TEST_TIMEOUT);
 
         verifyDataSmsIntentBroadcasts(1);
     }
 
     @FlakyTest
-    @Ignore
     @Test
     @MediumTest
     public void testInjectSms() {
@@ -309,121 +359,49 @@
 
         mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_INJECT_SMS, new AsyncResult(null,
                 mSmsMessage, null));
-        waitForMs(200);
+        waitForHandlerAction(mGsmInboundSmsHandler.getHandler(), TEST_TIMEOUT);
+        waitForHandlerAction(mGsmInboundSmsHandler.getHandler(), TEST_TIMEOUT);
 
         verifySmsIntentBroadcasts(0);
 
         // inject same SMS again, verify no broadcasts are sent
         mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_INJECT_SMS, new AsyncResult(null,
                 mSmsMessage, null));
-        waitForMs(100);
+        waitForHandlerAction(mGsmInboundSmsHandler.getHandler(), TEST_TIMEOUT);
+        waitForHandlerAction(mGsmInboundSmsHandler.getHandler(), TEST_TIMEOUT);
 
         verify(mContext, times(2)).sendBroadcast(any(Intent.class));
         assertEquals("IdleState", getCurrentState().getName());
     }
 
-    private void prepareMultiPartSms(boolean isWapPush) {
+    private void prepareMultiPartSms(boolean is3gpp2WapPush) {
         // Part 1
-        mInboundSmsTrackerCVPart1 = new ContentValues();
-        mInboundSmsTrackerCVPart1.put("pdu", HexDump.toHexString(mSmsPdu));
-        mInboundSmsTrackerCVPart1.put("address", "1234567890");
-        mInboundSmsTrackerCVPart1.put("reference_number", 1);
-        mInboundSmsTrackerCVPart1.put("sequence", 1);
-        mInboundSmsTrackerCVPart1.put("count", 2);
-        mInboundSmsTrackerCVPart1.put("date", System.currentTimeMillis());
-        mInboundSmsTrackerCVPart1.put("message_body", mMessageBodyPart1);
-        mInboundSmsTrackerCVPart1.put("display_originating_addr", "1234567890");
-
-        doReturn(2).when(mInboundSmsTrackerPart1).getMessageCount();
-        doReturn(1).when(mInboundSmsTrackerPart1).getReferenceNumber();
-        doReturn("1234567890").when(mInboundSmsTrackerPart1).getAddress();
-        doReturn(1).when(mInboundSmsTrackerPart1).getSequenceNumber();
-        doReturn(1).when(mInboundSmsTrackerPart1).getIndexOffset();
-        doReturn(-1).when(mInboundSmsTrackerPart1).getDestPort();
-        doReturn(mMessageBodyPart1).when(mInboundSmsTrackerPart1).getMessageBody();
-        doReturn(mSmsPdu).when(mInboundSmsTrackerPart1).getPdu();
-        doReturn(new String[]{mInboundSmsTrackerPart1.getAddress(),
-                Integer.toString(mInboundSmsTrackerPart1.getReferenceNumber()),
-                Integer.toString(mInboundSmsTrackerPart1.getMessageCount())})
-                .when(mInboundSmsTrackerPart1).getDeleteWhereArgs();
-        doReturn(mInboundSmsTrackerCVPart1.get("date")).when(mInboundSmsTrackerPart1).
-                getTimestamp();
-        doReturn(mInboundSmsTrackerCVPart1).when(mInboundSmsTrackerPart1).getContentValues();
-        if (isWapPush) {
-            mInboundSmsTrackerCVPart1.put("destination_port",
-                    (InboundSmsTracker.DEST_PORT_FLAG_3GPP2 |
-                            InboundSmsTracker.DEST_PORT_FLAG_3GPP2_WAP_PDU |
-                            SmsHeader.PORT_WAP_PUSH));
-            doReturn(InboundSmsTracker.SELECT_BY_REFERENCE_3GPP2WAP).when(mInboundSmsTrackerPart1)
-                    .getQueryForSegments();
-            doReturn(InboundSmsTracker.SELECT_BY_DUPLICATE_REFERENCE_3GPP2WAP)
-                    .when(mInboundSmsTrackerPart1).getQueryForMultiPartDuplicates();
-            doReturn(InboundSmsTracker.SELECT_BY_REFERENCE_3GPP2WAP).when(mInboundSmsTrackerPart1)
-                    .getDeleteWhere();
-            doReturn(SmsHeader.PORT_WAP_PUSH).when(mInboundSmsTrackerPart1).getDestPort();
-            doReturn(true).when(mInboundSmsTrackerPart1).is3gpp2();
-
-        } else {
-            mInboundSmsTrackerCVPart1.put("destination_port",
-                    InboundSmsTracker.DEST_PORT_FLAG_NO_PORT);
-            doReturn(InboundSmsTracker.SELECT_BY_REFERENCE).when(mInboundSmsTrackerPart1)
-                    .getQueryForSegments();
-            doReturn(InboundSmsTracker.SELECT_BY_DUPLICATE_REFERENCE)
-                    .when(mInboundSmsTrackerPart1).getQueryForMultiPartDuplicates();
-            doReturn(InboundSmsTracker.SELECT_BY_REFERENCE).when(mInboundSmsTrackerPart1)
-                    .getDeleteWhere();
-        }
+        mInboundSmsTrackerPart1 = new InboundSmsTracker(
+            mSmsPdu, /* pdu */
+            System.currentTimeMillis(), /* timestamp */
+            -1, /* destPort */
+            is3gpp2WapPush, /* is3gpp2 */
+            "1234567890", /* address */
+            "1234567890", /* displayAddress */
+            1, /* referenceNumber */
+            1, /* sequenceNumber */
+            2, /* messageCount */
+            is3gpp2WapPush, /* is3gpp2WapPdu */
+            mMessageBodyPart1 /* messageBody */);
 
         // Part 2
-        mInboundSmsTrackerCVPart2 = new ContentValues();
-        mInboundSmsTrackerCVPart2.put("pdu", HexDump.toHexString(mSmsPdu));
-        mInboundSmsTrackerCVPart2.put("address", "1234567890");
-        mInboundSmsTrackerCVPart2.put("reference_number", 1);
-        mInboundSmsTrackerCVPart2.put("sequence", 2);
-        mInboundSmsTrackerCVPart2.put("count", 2);
-        mInboundSmsTrackerCVPart2.put("date", System.currentTimeMillis());
-        mInboundSmsTrackerCVPart2.put("message_body", mMessageBodyPart2);
-        mInboundSmsTrackerCVPart2.put("display_originating_addr", "1234567890");
-
-        doReturn(2).when(mInboundSmsTrackerPart2).getMessageCount();
-        doReturn(1).when(mInboundSmsTrackerPart2).getReferenceNumber();
-        doReturn("1234567890").when(mInboundSmsTrackerPart2).getAddress();
-        doReturn(2).when(mInboundSmsTrackerPart2).getSequenceNumber();
-        doReturn(1).when(mInboundSmsTrackerPart2).getIndexOffset();
-        doReturn(-1).when(mInboundSmsTrackerPart2).getDestPort();
-        doReturn(mMessageBodyPart2).when(mInboundSmsTrackerPart2).getMessageBody();
-        doReturn(mSmsPdu).when(mInboundSmsTrackerPart2).getPdu();
-        doReturn(new String[]{mInboundSmsTrackerPart2.getAddress(),
-                Integer.toString(mInboundSmsTrackerPart2.getReferenceNumber()),
-                Integer.toString(mInboundSmsTrackerPart2.getMessageCount())})
-                .when(mInboundSmsTrackerPart2).getDeleteWhereArgs();
-        doReturn(mInboundSmsTrackerCVPart2.get("date")).when(mInboundSmsTrackerPart2).
-                getTimestamp();
-        doReturn(mInboundSmsTrackerCVPart2).when(mInboundSmsTrackerPart2).getContentValues();
-        if (isWapPush) {
-            mInboundSmsTrackerCVPart2.put("destination_port",
-                    (InboundSmsTracker.DEST_PORT_FLAG_3GPP2 |
-                            InboundSmsTracker.DEST_PORT_FLAG_3GPP2_WAP_PDU |
-                            SmsHeader.PORT_WAP_PUSH));
-            doReturn(InboundSmsTracker.SELECT_BY_REFERENCE_3GPP2WAP).when(mInboundSmsTrackerPart2)
-                    .getQueryForSegments();
-            doReturn(InboundSmsTracker.SELECT_BY_DUPLICATE_REFERENCE_3GPP2WAP)
-                    .when(mInboundSmsTrackerPart2).getQueryForMultiPartDuplicates();
-            doReturn(InboundSmsTracker.SELECT_BY_REFERENCE_3GPP2WAP).when(mInboundSmsTrackerPart2)
-                    .getDeleteWhere();
-            doReturn(SmsHeader.PORT_WAP_PUSH).when(mInboundSmsTrackerPart2).getDestPort();
-            doReturn(true).when(mInboundSmsTrackerPart2).is3gpp2();
-
-        } else {
-            mInboundSmsTrackerCVPart2.put("destination_port",
-                    InboundSmsTracker.DEST_PORT_FLAG_NO_PORT);
-            doReturn(InboundSmsTracker.SELECT_BY_REFERENCE).when(mInboundSmsTrackerPart2)
-                    .getQueryForSegments();
-            doReturn(InboundSmsTracker.SELECT_BY_DUPLICATE_REFERENCE)
-                    .when(mInboundSmsTrackerPart2).getQueryForMultiPartDuplicates();
-            doReturn(InboundSmsTracker.SELECT_BY_REFERENCE).when(mInboundSmsTrackerPart2)
-                    .getDeleteWhere();
-        }
+        mInboundSmsTrackerPart2 = new InboundSmsTracker(
+            mSmsPdu, /* pdu */
+            System.currentTimeMillis(), /* timestamp */
+            -1, /* destPort */
+            is3gpp2WapPush, /* is3gpp2 */
+            "1234567890", /* address */
+            "1234567890", /* displayAddress */
+            1, /* referenceNumber */
+            2, /* sequenceNumber */
+            2, /* messageCount */
+            is3gpp2WapPush, /* is3gpp2WapPdu */
+            mMessageBodyPart2 /* messageBody */);
     }
 
     @Test
@@ -447,9 +425,7 @@
                 .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
                         nullable(String.class), nullable(String.class), anyInt(), anyInt(),
                         anyInt(), anyBoolean(), nullable(String.class));
-        mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
-                mSmsMessage, null));
-        waitForMs(200);
+        sendNewSms();
 
         // State machine should go back to idle and wait for second part
         assertEquals("IdleState", getCurrentState().getName());
@@ -460,9 +436,8 @@
                 .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
                         nullable(String.class), nullable(String.class), anyInt(), anyInt(),
                         anyInt(), anyBoolean(), nullable(String.class));
-        mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
-                mSmsMessage, null));
-        waitForMs(200);
+        sendNewSms();
+
         // State machine should go back to idle and wait for second part
         assertEquals("IdleState", getCurrentState().getName());
 
@@ -475,9 +450,7 @@
                 .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
                         nullable(String.class), nullable(String.class), anyInt(), anyInt(),
                         anyInt(), anyBoolean(), nullable(String.class));
-        mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
-                mSmsMessage, null));
-        waitForMs(200);
+        sendNewSms();
 
         // verify broadcast intents
         verifySmsIntentBroadcasts(0);
@@ -488,7 +461,6 @@
     }
 
     @FlakyTest
-    @Ignore
     @Test
     @MediumTest
     public void testMultiPartSms() {
@@ -504,9 +476,7 @@
                 .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
                         nullable(String.class), nullable(String.class), anyInt(), anyInt(),
                         anyInt(), anyBoolean(), nullable(String.class));
-        mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
-                mSmsMessage, null));
-        waitForMs(100);
+        sendNewSms();
 
         // State machine should go back to idle and wait for second part
         assertEquals("IdleState", getCurrentState().getName());
@@ -515,9 +485,7 @@
                 .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
                         nullable(String.class), nullable(String.class), anyInt(), anyInt(),
                         anyInt(), anyBoolean(), nullable(String.class));
-        mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
-                mSmsMessage, null));
-        waitForMs(100);
+        sendNewSms();
 
         // verify broadcast intents
         verifySmsIntentBroadcasts(0);
@@ -531,9 +499,7 @@
                 .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
                         nullable(String.class), nullable(String.class), anyInt(), anyInt(),
                         anyInt(), anyBoolean(), nullable(String.class));
-        mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
-                mSmsMessage, null));
-        waitForMs(100);
+        sendNewSms();
 
         // verify no additional broadcasts sent
         verify(mContext, times(2)).sendBroadcast(any(Intent.class));
@@ -549,9 +515,7 @@
                 .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
                         nullable(String.class), nullable(String.class), anyInt(), anyInt(),
                         anyInt(), anyBoolean(), nullable(String.class));
-        mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
-                mSmsMessage, null));
-        waitForMs(100);
+        sendNewSms();
 
         // verify no additional broadcasts sent
         verify(mContext, times(2)).sendBroadcast(any(Intent.class));
@@ -571,8 +535,18 @@
         // prepare SMS part 1 and part 2
         prepareMultiPartSms(false);
         // change seqNumber in part 2 to 1
-        mInboundSmsTrackerCVPart2.put("sequence", 1);
-        doReturn(1).when(mInboundSmsTrackerPart2).getSequenceNumber();
+        mInboundSmsTrackerPart2 = new InboundSmsTracker(
+            mSmsPdu, /* pdu */
+            System.currentTimeMillis(), /* timestamp */
+            -1, /* destPort */
+            false, /* is3gpp2 */
+            "1234567890", /* address */
+            "1234567890", /* displayAddress */
+            1, /* referenceNumber */
+            1, /* sequenceNumber */
+            2, /* messageCount */
+            false, /* is3gpp2WapPdu */
+            mMessageBodyPart2 /* messageBody */);
 
         mSmsHeader.concatRef = new SmsHeader.ConcatRef();
         doReturn(mSmsHeader).when(mGsmSmsMessage).getUserDataHeader();
@@ -581,9 +555,7 @@
                 .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
                         nullable(String.class), nullable(String.class), anyInt(), anyInt(),
                         anyInt(), anyBoolean(), nullable(String.class));
-        mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
-                mSmsMessage, null));
-        waitForMs(100);
+        sendNewSms();
 
         // State machine should go back to idle and wait for second part
         assertEquals("IdleState", getCurrentState().getName());
@@ -592,9 +564,7 @@
                 .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
                         nullable(String.class), nullable(String.class), anyInt(), anyInt(),
                         anyInt(), anyBoolean(), nullable(String.class));
-        mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
-                mSmsMessage, null));
-        waitForMs(100);
+        sendNewSms();
 
         // verify no broadcasts sent
         verify(mContext, never()).sendBroadcast(any(Intent.class));
@@ -619,9 +589,7 @@
                 .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
                         nullable(String.class), nullable(String.class), anyInt(), anyInt(),
                         anyInt(), anyBoolean(), nullable(String.class));
-        mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
-                mSmsMessage, null));
-        waitForMs(100);
+        sendNewSms();
 
         // verify the message is stored in the raw table
         assertEquals(1, mContentProvider.getNumRows());
@@ -631,16 +599,24 @@
 
         // change seqNumber in part 2 to an invalid value
         int invalidSeqNumber = -1;
-        mInboundSmsTrackerCVPart2.put("sequence", invalidSeqNumber);
-        doReturn(invalidSeqNumber).when(mInboundSmsTrackerPart2).getSequenceNumber();
+        mInboundSmsTrackerPart2 = new InboundSmsTracker(
+            mSmsPdu, /* pdu */
+            System.currentTimeMillis(), /* timestamp */
+            -1, /* destPort */
+            false, /* is3gpp2 */
+            "1234567890", /* address */
+            "1234567890", /* displayAddress */
+            1, /* referenceNumber */
+            invalidSeqNumber, /* sequenceNumber */
+            2, /* messageCount */
+            false, /* is3gpp2WapPdu */
+            mMessageBodyPart2 /* messageBody */);
 
         doReturn(mInboundSmsTrackerPart2).when(mTelephonyComponentFactory)
                 .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
                         nullable(String.class), nullable(String.class), anyInt(), anyInt(),
                         anyInt(), anyBoolean(), nullable(String.class));
-        mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
-                mSmsMessage, null));
-        waitForMs(100);
+        sendNewSms();
 
         // verify no broadcasts sent
         verify(mContext, never()).sendBroadcast(any(Intent.class));
@@ -665,9 +641,7 @@
                         nullable(String.class), nullable(String.class), anyInt(), anyInt(),
                         anyInt(), anyBoolean(), nullable(String.class));
 
-        mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
-                mSmsMessage, null));
-        waitForMs(100);
+        sendNewSms();
 
         // State machine should go back to idle and wait for second part
         assertEquals("IdleState", getCurrentState().getName());
@@ -676,9 +650,7 @@
                 .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
                         nullable(String.class), nullable(String.class), anyInt(), anyInt(),
                         anyInt(), anyBoolean(), nullable(String.class));
-        mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
-                mSmsMessage, null));
-        waitForMs(100);
+        sendNewSms();
 
         verify(mContext, never()).sendBroadcast(any(Intent.class));
         assertEquals("IdleState", getCurrentState().getName());
@@ -694,7 +666,18 @@
         // prepare SMS part 1 and part 2
         prepareMultiPartSms(false);
         // only the first SMS is configured with the display originating email address
-        mInboundSmsTrackerCVPart1.put("display_originating_addr", "1234567890@test.com");
+        mInboundSmsTrackerPart1 = new InboundSmsTracker(
+            mSmsPdu, /* pdu */
+            System.currentTimeMillis(), /* timestamp */
+            -1, /* destPort */
+            false, /* is3gpp2 */
+            "1234567890", /* address */
+            "1234567890@test.com", /* displayAddress */
+            1, /* referenceNumber */
+            1, /* sequenceNumber */
+            2, /* messageCount */
+            false, /* is3gpp2WapPdu */
+            mMessageBodyPart1 /* messageBody */);
 
         mSmsHeader.concatRef = new SmsHeader.ConcatRef();
         doReturn(mSmsHeader).when(mGsmSmsMessage).getUserDataHeader();
@@ -703,9 +686,7 @@
                         nullable(String.class), nullable(String.class), anyInt(), anyInt(),
                         anyInt(), anyBoolean(), nullable(String.class));
 
-        mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
-                mSmsMessage, null));
-        waitForMs(100);
+        sendNewSms();
 
         // State machine should go back to idle and wait for second part
         assertEquals("IdleState", getCurrentState().getName());
@@ -714,9 +695,7 @@
                 .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
                         nullable(String.class), nullable(String.class), anyInt(), anyInt(),
                         anyInt(), anyBoolean(), nullable(String.class));
-        mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, new AsyncResult(null,
-                mSmsMessage, null));
-        waitForMs(100);
+        sendNewSms();
 
         verify(mContext, never()).sendBroadcast(any(Intent.class));
         assertEquals("IdleState", getCurrentState().getName());
@@ -726,13 +705,10 @@
     @MediumTest
     public void testBroadcastUndeliveredUserLocked() throws Exception {
         replaceInstance(SmsBroadcastUndelivered.class, "instance", null, null);
-        doReturn(0).when(mInboundSmsTracker).getDestPort();
+        doReturn(0).when(mMockInboundSmsTracker).getDestPort();
 
         // add a fake entry to db
-        mContentProvider.insert(sRawUri, mInboundSmsTrackerCV);
-
-        // make it a single-part message
-        doReturn(1).when(mInboundSmsTracker).getMessageCount();
+        mContentProvider.insert(sRawUri, mMockInboundSmsTracker.getContentValues());
 
         // user locked
         UserManager userManager = (UserManager)mContext.getSystemService(Context.USER_SERVICE);
@@ -745,6 +721,7 @@
         verify(mContext).registerReceiverAsUser(any(BroadcastReceiver.class), eq((UserHandle)null),
                 any(IntentFilter.class), eq((String)null), eq((Handler)null));
 
+        // wait for ScanRawTableThread
         waitForMs(100);
 
         // verify no broadcasts sent because due to !isUserUnlocked
@@ -753,6 +730,7 @@
         // when user unlocks the device, the message in db should be broadcast
         doReturn(true).when(userManager).isUserUnlocked();
         mContext.sendBroadcast(new Intent(Intent.ACTION_USER_UNLOCKED));
+        // wait for ScanRawTableThread
         waitForMs(100);
 
         verifyDataSmsIntentBroadcasts(1);
@@ -762,15 +740,14 @@
     @MediumTest
     public void testBroadcastUndeliveredUserUnlocked() throws Exception {
         replaceInstance(SmsBroadcastUndelivered.class, "instance", null, null);
-        doReturn(0).when(mInboundSmsTracker).getDestPort();
+        doReturn(0).when(mMockInboundSmsTracker).getDestPort();
 
         // add a fake entry to db
-        mContentProvider.insert(sRawUri, mInboundSmsTrackerCV);
-
-        // make it a single-part message
-        doReturn(1).when(mInboundSmsTracker).getMessageCount();
+        mContentProvider.insert(sRawUri, mMockInboundSmsTracker.getContentValues());
 
         SmsBroadcastUndelivered.initialize(mContext, mGsmInboundSmsHandler, mCdmaInboundSmsHandler);
+
+        // wait for ScanRawTableThread
         waitForMs(100);
 
         // user is unlocked; intent should be broadcast right away
@@ -782,18 +759,28 @@
     public void testBroadcastUndeliveredDeleted() throws Exception {
         replaceInstance(SmsBroadcastUndelivered.class, "instance", null, null);
         SmsBroadcastUndelivered.initialize(mContext, mGsmInboundSmsHandler, mCdmaInboundSmsHandler);
-        doReturn(0).when(mInboundSmsTracker).getDestPort();
+        mInboundSmsTracker = new InboundSmsTracker(
+            mSmsPdu, /* pdu */
+            System.currentTimeMillis(), /* timestamp */
+            0, /* destPort */
+            false, /* is3gpp2 */
+            false, /* is3gpp2WapPdu */
+            "1234567890", /* address */
+            "1234567890", /* displayAddress */
+            mMessageBody /* messageBody */);
+        doReturn(mInboundSmsTracker).when(mTelephonyComponentFactory)
+                .makeInboundSmsTracker(nullable(byte[].class), anyLong(), anyInt(), anyBoolean(),
+                anyBoolean(), nullable(String.class), nullable(String.class),
+                nullable(String.class));
 
         //add a fake entry to db
         ContentValues rawSms = new ContentValues();
         rawSms.put("deleted", 1);
         mContentProvider.insert(sRawUri, rawSms);
 
-        //make it a single-part message
-        doReturn(1).when(mInboundSmsTracker).getMessageCount();
-
         //when user unlocks the device, broadcast should not be sent for new message
         mContext.sendBroadcast(new Intent(Intent.ACTION_USER_UNLOCKED));
+        // wait for ScanRawTableThread
         waitForMs(100);
 
         verify(mContext, times(1)).sendBroadcast(any(Intent.class));
@@ -802,7 +789,6 @@
     }
 
     @FlakyTest
-    @Ignore
     @Test
     @MediumTest
     public void testBroadcastUndeliveredMultiPart() throws Exception {
@@ -812,8 +798,8 @@
         prepareMultiPartSms(false);
 
         //add the 2 SMS parts to db
-        mContentProvider.insert(sRawUri, mInboundSmsTrackerCVPart1);
-        mContentProvider.insert(sRawUri, mInboundSmsTrackerCVPart2);
+        mContentProvider.insert(sRawUri, mInboundSmsTrackerPart1.getContentValues());
+        mContentProvider.insert(sRawUri, mInboundSmsTrackerPart2.getContentValues());
 
         //return InboundSmsTracker objects corresponding to the 2 parts
         doReturn(mInboundSmsTrackerPart1).doReturn(mInboundSmsTrackerPart2).
@@ -821,7 +807,8 @@
                 anyBoolean());
 
         SmsBroadcastUndelivered.initialize(mContext, mGsmInboundSmsHandler, mCdmaInboundSmsHandler);
-        waitForMs(100);
+        // wait for ScanRawTableThread
+        waitForMs(200);
 
         verifySmsIntentBroadcasts(0);
     }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/IccRecordsTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccRecordsTest.java
index 5c847cd..b38b5e8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/IccRecordsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/IccRecordsTest.java
@@ -29,7 +29,7 @@
 
 package com.android.internal.telephony.uicc;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
 import static org.mockito.Mockito.*;
 
 import android.os.HandlerThread;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java
index a3d7245..6f488a8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/EuiccCardTest.java
@@ -564,7 +564,7 @@
                 com.android.internal.R.array.config_telephonyEuiccDeviceCapabilities))
                 .thenReturn(new String[] {});
 
-        int channel = mockLogicalChannelResponses("BF38038101039000");
+        int channel = mockLogicalChannelResponses("BF3805A1030201039000");
 
         ResultCaptor<byte[]> resultCaptor = new ResultCaptor<>();
         mEuiccCard.authenticateServer("A1B2C3-X4Y5Z6", // Matching id