Merge "Support CDMA call duration reset in telephony connection"
am: cb78596686

Change-Id: I47c7e07142f23784a4a76e6a97919e9bc1930fc4
diff --git a/proto/src/telephony.proto b/proto/src/telephony.proto
index 215d14e..e441f71 100644
--- a/proto/src/telephony.proto
+++ b/proto/src/telephony.proto
@@ -476,6 +476,96 @@
 
   // LCE service not supported
   RIL_E_LCE_NOT_SUPPORTED_NEW = 37;
+
+  // Not sufficient memory to process the request
+  RIL_E_NO_MEMORY = 38;
+
+  // Modem hit unexpected error scenario while handling this request
+  RIL_E_INTERNAL_ERR = 39;
+
+  // Hit platform or system error
+  RIL_E_SYSTEM_ERR = 40;
+
+  // Vendor RIL got unexpected or incorrect response from modem for this request
+  RIL_E_MODEM_ERR = 41;
+
+  // Unexpected request for the current state
+  RIL_E_INVALID_STATE = 42;
+
+  // Not sufficient resource to process the request
+  RIL_E_NO_RESOURCES = 43;
+
+  // Received error from SIM card
+  RIL_E_SIM_ERR = 44;
+
+  // Received invalid arguments in request
+  RIL_E_INVALID_ARGUMENTS = 45;
+
+  // Cannot process the request in current SIM state
+  RIL_E_INVALID_SIM_STATE = 46;
+
+  // Cannot process the request in current Modem state
+  RIL_E_INVALID_MODEM_STATE = 47;
+
+  // Received invalid call id in request
+  RIL_E_INVALID_CALL_ID = 48;
+
+  // ACK received when there is no SMS to ack
+  RIL_E_NO_SMS_TO_ACK = 49;
+
+  // Received error from network
+  RIL_E_NETWORK_ERR = 50;
+
+  // Operation denied due to overly-frequent requests
+  RIL_E_REQUEST_RATE_LIMITED = 51;
+
+  // SIM is busy
+  RIL_E_SIM_BUSY = 52;
+
+  // The target EF is full
+  RIL_E_SIM_FULL = 53;
+
+  // Request is rejected by network
+  RIL_E_NETWORK_REJECT = 54;
+
+  // Not allowed the request now
+  RIL_E_OPERATION_NOT_ALLOWED = 55;
+
+  // The request record is empty
+  RIL_E_EMPTY_RECORD = 56;
+
+  // Invalid sms format
+  RIL_E_INVALID_SMS_FORMAT = 57;
+
+  // Message not encoded properly
+  RIL_E_ENCODING_ERR = 58;
+
+  // SMSC address specified is invalid
+  RIL_E_INVALID_SMSC_ADDRESS = 59;
+
+  // No such entry present to perform the request
+  RIL_E_NO_SUCH_ENTRY = 60;
+
+  // Network is not ready to perform the request
+  RIL_E_NETWORK_NOT_READY = 61;
+
+  // Device does not have this value provisioned
+  RIL_E_NOT_PROVISIONED = 62;
+
+  // Device does not have subscription
+  RIL_E_NO_SUBSCRIPTION = 63;
+
+  // Network cannot be found
+  RIL_E_NO_NETWORK_FOUND = 64;
+
+  // Operation cannot be performed because the device is currently in use
+  RIL_E_DEVICE_IN_USE = 65;
+
+  // Operation aborted
+  RIL_E_ABORTED = 66;
+
+  // Invalid response sent by vendor code
+  RIL_E_INVALID_RESPONSE = 67;
 }
 
 // PDP_type values in TS 27.007 section 10.1.1.
@@ -553,6 +643,9 @@
 
     // Carrier Identification Matching Event
     CARRIER_ID_MATCHING = 13;
+
+    // Carrier Key Change event.
+    CARRIER_KEY_CHANGED = 14;
   }
 
   // Setup a packet data connection
@@ -743,6 +836,26 @@
     optional RilDataCall call = 3;
   }
 
+  // Carrier Key Change Event.
+  message CarrierKeyChange {
+
+    enum KeyType {
+
+      // Key Type Unknown.
+      UNKNOWN = 0;
+      // Key Type for WLAN.
+      WLAN = 1;
+      // Key Type for EPDG.
+      EPDG = 2;
+    }
+
+    // Key type of the Encryption key.
+    optional KeyType key_type = 1;
+
+    // Whether the download was successful or not.
+    optional bool isDownloadSuccessful = 2;
+  }
+
   // Deactivate packet data connection
   message RilDeactivateDataCall {
 
@@ -850,6 +963,9 @@
 
   // Carrier id matching event
   optional CarrierIdMatching carrier_id_matching = 16;
+
+  // Carrier key change
+  optional CarrierKeyChange carrier_key_change = 17;
 }
 
 enum TimeInterval {
@@ -1229,16 +1345,16 @@
 
     message CBMessage {
       // CB message format
-      optional Format msgFormat = 1;
+      optional Format msg_format = 1;
 
       // CB message priority
-      optional CBPriority msgPriority = 2;
+      optional CBPriority msg_priority = 2;
 
       // Type of CB msg
-      optional CBMessageType msgType = 3;
+      optional CBMessageType msg_type = 3;
 
       // Service category of CB message
-      optional int32 serviceCategory = 4;
+      optional int32 service_category = 4;
     }
 
     enum CBMessageType {
diff --git a/src/java/com/android/internal/telephony/CarrierIdentifier.java b/src/java/com/android/internal/telephony/CarrierIdentifier.java
index 69131ca..1ff8971 100644
--- a/src/java/com/android/internal/telephony/CarrierIdentifier.java
+++ b/src/java/com/android/internal/telephony/CarrierIdentifier.java
@@ -211,11 +211,13 @@
                     if (mIccRecords != null) {
                         logd("Removing stale icc objects.");
                         mIccRecords.unregisterForRecordsLoaded(this);
+                        mIccRecords.unregisterForRecordsOverride(this);
                         mIccRecords = null;
                     }
                     if (newIccRecords != null) {
                         logd("new Icc object");
                         newIccRecords.registerForRecordsLoaded(this, SIM_LOAD_EVENT, null);
+                        newIccRecords.registerForRecordsOverride(this, SIM_LOAD_EVENT, null);
                         mIccRecords = newIccRecords;
                     }
                 }
diff --git a/src/java/com/android/internal/telephony/CarrierInfoManager.java b/src/java/com/android/internal/telephony/CarrierInfoManager.java
index 6a6a065..f645746 100644
--- a/src/java/com/android/internal/telephony/CarrierInfoManager.java
+++ b/src/java/com/android/internal/telephony/CarrierInfoManager.java
@@ -29,6 +29,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.internal.telephony.metrics.TelephonyMetrics;
+
 import java.util.Date;
 
 /**
@@ -50,30 +52,30 @@
     /**
      * Returns Carrier specific information that will be used to encrypt the IMSI and IMPI.
      * @param keyType whether the key is being used for WLAN or ePDG.
-     * @param mContext
+     * @param context
      * @return ImsiEncryptionInfo which contains the information, including the public key, to be
      *         used for encryption.
      */
     public static ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType,
-                                                                     Context mContext) {
+                                                                     Context context) {
         String mcc = "";
         String mnc = "";
         final TelephonyManager telephonyManager =
-                (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
-        String networkOperator = telephonyManager.getNetworkOperator();
-        if (!TextUtils.isEmpty(networkOperator)) {
-            mcc = networkOperator.substring(0, 3);
-            mnc = networkOperator.substring(3);
+                (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+        String simOperator = telephonyManager.getSimOperator();
+        if (!TextUtils.isEmpty(simOperator)) {
+            mcc = simOperator.substring(0, 3);
+            mnc = simOperator.substring(3);
             Log.i(LOG_TAG, "using values for mnc, mcc: " + mnc + "," + mcc);
         } else {
-            Log.e(LOG_TAG, "Invalid networkOperator: " + networkOperator);
+            Log.e(LOG_TAG, "Invalid networkOperator: " + simOperator);
             return null;
         }
         Cursor findCursor = null;
         try {
             // In the current design, MVNOs are not supported. If we decide to support them,
             // we'll need to add to this CL.
-            ContentResolver mContentResolver = mContext.getContentResolver();
+            ContentResolver mContentResolver = context.getContentResolver();
             String[] columns = {Telephony.CarrierColumns.PUBLIC_KEY,
                     Telephony.CarrierColumns.EXPIRATION_TIME,
                     Telephony.CarrierColumns.KEY_IDENTIFIER};
@@ -107,12 +109,13 @@
     /**
      * Inserts or update the Carrier Key in the database
      * @param imsiEncryptionInfo ImsiEncryptionInfo object.
-     * @param mContext Context.
+     * @param context Context.
      */
     public static void updateOrInsertCarrierKey(ImsiEncryptionInfo imsiEncryptionInfo,
-                                                Context mContext) {
+                                                Context context, int phoneId) {
         byte[] keyBytes = imsiEncryptionInfo.getPublicKey().getEncoded();
-        ContentResolver mContentResolver = mContext.getContentResolver();
+        ContentResolver mContentResolver = context.getContentResolver();
+        TelephonyMetrics tm = TelephonyMetrics.getInstance();
         // In the current design, MVNOs are not supported. If we decide to support them,
         // we'll need to add to this CL.
         ContentValues contentValues = new ContentValues();
@@ -125,6 +128,7 @@
         contentValues.put(Telephony.CarrierColumns.PUBLIC_KEY, keyBytes);
         contentValues.put(Telephony.CarrierColumns.EXPIRATION_TIME,
                 imsiEncryptionInfo.getExpirationTime().getTime());
+        boolean downloadSuccessfull = true;
         try {
             Log.i(LOG_TAG, "Inserting imsiEncryptionInfo into db");
             mContentResolver.insert(Telephony.CarrierColumns.CONTENT_URI, contentValues);
@@ -145,12 +149,17 @@
                                 String.valueOf(imsiEncryptionInfo.getKeyType())});
                 if (nRows == 0) {
                     Log.d(LOG_TAG, "Error updating values:" + imsiEncryptionInfo);
+                    downloadSuccessfull = false;
                 }
             } catch (Exception ex) {
                 Log.d(LOG_TAG, "Error updating values:" + imsiEncryptionInfo + ex);
+                downloadSuccessfull = false;
             }
         }  catch (Exception e) {
             Log.d(LOG_TAG, "Error inserting/updating values:" + imsiEncryptionInfo + e);
+            downloadSuccessfull = false;
+        } finally {
+            tm.writeCarrierKeyEvent(phoneId, imsiEncryptionInfo.getKeyType(), downloadSuccessfull);
         }
     }
 
@@ -162,12 +171,12 @@
      *        {@link java.security.PublicKey} and the Key Identifier.
      *        The keyIdentifier Attribute value pair that helps a server locate
      *        the private key to decrypt the permanent identity.
-     * @param mContext Context.
+     * @param context Context.
      */
     public static void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo,
-                                                       Context mContext) {
+                                                       Context context, int phoneId) {
         Log.i(LOG_TAG, "inserting carrier key: " + imsiEncryptionInfo);
-        updateOrInsertCarrierKey(imsiEncryptionInfo, mContext);
+        updateOrInsertCarrierKey(imsiEncryptionInfo, context, phoneId);
         //todo send key to modem. Will be done in a subsequent CL.
     }
 
diff --git a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
index 2f57838..c4c5a30 100644
--- a/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/CarrierServiceStateTracker.java
@@ -281,6 +281,7 @@
             CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
                     context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
             PersistableBundle b = carrierConfigManager.getConfigForSubId(mPhone.getSubId());
+
             for (Map.Entry<Integer, NotificationType> entry : mNotificationTypeMap.entrySet()) {
                 NotificationType notificationType = entry.getValue();
                 notificationType.setDelay(b);
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index 5dd36ad..2871e3a 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -29,6 +29,7 @@
 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentValues;
@@ -1387,7 +1388,11 @@
             if (b != null) {
                 String defaultVmNumber =
                         b.getString(CarrierConfigManager.KEY_DEFAULT_VM_NUMBER_STRING);
-                if (!TextUtils.isEmpty(defaultVmNumber)) {
+                String defaultVmNumberRoaming =
+                        b.getString(CarrierConfigManager.KEY_DEFAULT_VM_NUMBER_ROAMING_STRING);
+                if (!TextUtils.isEmpty(defaultVmNumberRoaming) && mSST.mSS.getRoaming()) {
+                    number = defaultVmNumberRoaming;
+                } else {
                     number = defaultVmNumber;
                 }
             }
@@ -1504,15 +1509,20 @@
     }
 
     @Override
+    @Nullable
     public String getSubscriberId() {
-        if (isPhoneTypeGsm()) {
-            IccRecords r = mIccRecords.get();
-            return (r != null) ? r.getIMSI() : null;
-        } else if (isPhoneTypeCdma()) {
-            return mSST.getImsi();
-        } else { //isPhoneTypeCdmaLte()
-            return (mSimRecords != null) ? mSimRecords.getIMSI() : "";
+        String subscriberId = null;
+        if (isPhoneTypeCdma()) {
+            subscriberId = mSST.getImsi();
+        } else {
+            // Both Gsm and CdmaLte get the IMSI from Usim.
+            IccRecords iccRecords = mUiccController.getIccRecords(
+                    mPhoneId, UiccController.APP_FAM_3GPP);
+            if (iccRecords != null) {
+                subscriberId = iccRecords.getIMSI();
+            }
         }
+        return subscriberId;
     }
 
     @Override
@@ -1522,7 +1532,7 @@
 
     @Override
     public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo) {
-        CarrierInfoManager.setCarrierInfoForImsiEncryption(imsiEncryptionInfo, mContext);
+        CarrierInfoManager.setCarrierInfoForImsiEncryption(imsiEncryptionInfo, mContext, mPhoneId);
     }
 
     @Override
@@ -1536,13 +1546,13 @@
     }
 
     @Override
-    public void resetCarrierKeysForImsiEncryption() {
-        mCIM.resetCarrierKeysForImsiEncryption(mContext, mPhoneId);
+    public int getCarrierIdListVersion() {
+        return mCarrerIdentifier.getCarrierListVersion();
     }
 
     @Override
-    public int getCarrierIdListVersion() {
-        return mCarrerIdentifier.getCarrierListVersion();
+    public void resetCarrierKeysForImsiEncryption() {
+        mCIM.resetCarrierKeysForImsiEncryption(mContext, mPhoneId);
     }
 
     @Override
@@ -2339,14 +2349,14 @@
                     int current_cdma_roaming_mode =
                             Settings.Global.getInt(getContext().getContentResolver(),
                             Settings.Global.CDMA_ROAMING_MODE,
-                            CarrierConfigManager.CDMA_ROAMING_MODE_RADIO_DEFAULT);
+                            TelephonyManager.CDMA_ROAMING_MODE_RADIO_DEFAULT);
                     switch (config_cdma_roaming_mode) {
                         // Carrier's cdma_roaming_mode will overwrite the user's previous settings
                         // Keep the user's previous setting in global variable which will be used
                         // when carrier's setting is turn off.
-                        case CarrierConfigManager.CDMA_ROAMING_MODE_HOME:
-                        case CarrierConfigManager.CDMA_ROAMING_MODE_AFFILIATED:
-                        case CarrierConfigManager.CDMA_ROAMING_MODE_ANY:
+                        case TelephonyManager.CDMA_ROAMING_MODE_HOME:
+                        case TelephonyManager.CDMA_ROAMING_MODE_AFFILIATED:
+                        case TelephonyManager.CDMA_ROAMING_MODE_ANY:
                             logd("cdma_roaming_mode is going to changed to "
                                     + config_cdma_roaming_mode);
                             setCdmaRoamingPreference(config_cdma_roaming_mode,
@@ -2355,7 +2365,7 @@
 
                         // When carrier's setting is turn off, change the cdma_roaming_mode to the
                         // previous user's setting
-                        case CarrierConfigManager.CDMA_ROAMING_MODE_RADIO_DEFAULT:
+                        case TelephonyManager.CDMA_ROAMING_MODE_RADIO_DEFAULT:
                             if (current_cdma_roaming_mode != config_cdma_roaming_mode) {
                                 logd("cdma_roaming_mode is going to changed to "
                                         + current_cdma_roaming_mode);
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index b51498e..2c42033 100644
--- a/src/java/com/android/internal/telephony/InboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -794,6 +794,13 @@
         int destPort = tracker.getDestPort();
         boolean block = false;
 
+        // Do not process when the message count is invalid.
+        if (messageCount <= 0) {
+            loge("processMessagePart: returning false due to invalid message count "
+                    + messageCount);
+            return false;
+        }
+
         if (messageCount == 1) {
             // single-part message
             pdus = new byte[][]{tracker.getPdu()};
@@ -829,6 +836,17 @@
                     int index = cursor.getInt(PDU_SEQUENCE_PORT_PROJECTION_INDEX_MAPPING
                             .get(SEQUENCE_COLUMN)) - tracker.getIndexOffset();
 
+                    // The invalid PDUs can be received and stored in the raw table. The range
+                    // check ensures the process not crash even if the seqNumber in the
+                    // UserDataHeader is invalid.
+                    if (index >= pdus.length || index < 0) {
+                        loge(String.format(
+                                "processMessagePart: invalid seqNumber = %d, messageCount = %d",
+                                index + tracker.getIndexOffset(),
+                                messageCount));
+                        continue;
+                    }
+
                     pdus[index] = HexDump.hexStringToByteArray(cursor.getString(
                             PDU_SEQUENCE_PORT_PROJECTION_INDEX_MAPPING.get(PDU_COLUMN)));
 
diff --git a/src/java/com/android/internal/telephony/InboundSmsTracker.java b/src/java/com/android/internal/telephony/InboundSmsTracker.java
index c63ccc8..36c6996 100644
--- a/src/java/com/android/internal/telephony/InboundSmsTracker.java
+++ b/src/java/com/android/internal/telephony/InboundSmsTracker.java
@@ -195,7 +195,7 @@
         mAddress = cursor.getString(InboundSmsHandler.ADDRESS_COLUMN);
         mDisplayAddress = cursor.getString(InboundSmsHandler.DISPLAY_ADDRESS_COLUMN);
 
-        if (cursor.isNull(InboundSmsHandler.COUNT_COLUMN)) {
+        if (cursor.getInt(InboundSmsHandler.COUNT_COLUMN) == 1) {
             // single-part message
             long rowId = cursor.getLong(InboundSmsHandler.ID_COLUMN);
             mReferenceNumber = -1;
@@ -250,8 +250,8 @@
             values.put("display_originating_addr", mDisplayAddress);
             values.put("reference_number", mReferenceNumber);
             values.put("sequence", mSequenceNumber);
-            values.put("count", mMessageCount);
         }
+        values.put("count", mMessageCount);
         values.put("message_body", mMessageBody);
         return values;
     }
diff --git a/src/java/com/android/internal/telephony/LocaleTracker.java b/src/java/com/android/internal/telephony/LocaleTracker.java
index 8977b03..a18e90f 100644
--- a/src/java/com/android/internal/telephony/LocaleTracker.java
+++ b/src/java/com/android/internal/telephony/LocaleTracker.java
@@ -344,6 +344,7 @@
         msg = "getCellInfo: cell info=" + mCellInfo;
         if (DBG) log(msg);
         mLocalLog.log(msg);
+
         if (mCellInfo == null || mCellInfo.size() == 0) {
             // If we can't get a valid cell info. Try it again later.
             long delay = getCellInfoDelayTime(++mFailCellInfoCount);
@@ -401,7 +402,7 @@
             // country of the carrier we see. If we can't see any, reset to 0 so we don't
             // broadcast on forbidden channels.
             ((WifiManager) mPhone.getContext().getSystemService(Context.WIFI_SERVICE))
-                    .setCountryCode(countryIso, false);
+                    .setCountryCode(countryIso);
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 899f1fc..2682cee 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -536,7 +536,7 @@
                 // note this is not persisting
                 WifiManager wM = (WifiManager)
                         mContext.getSystemService(Context.WIFI_SERVICE);
-                wM.setCountryCode(country, false);
+                wM.setCountryCode(country);
             }
         }
 
diff --git a/src/java/com/android/internal/telephony/PhoneInternalInterface.java b/src/java/com/android/internal/telephony/PhoneInternalInterface.java
index 91b7b28..123914e 100644
--- a/src/java/com/android/internal/telephony/PhoneInternalInterface.java
+++ b/src/java/com/android/internal/telephony/PhoneInternalInterface.java
@@ -22,14 +22,14 @@
 import android.os.Message;
 import android.os.ResultReceiver;
 import android.os.WorkSource;
-import android.telephony.CarrierConfigManager;
 import android.telecom.VideoProfile;
 import android.telephony.CellLocation;
 import android.telephony.ImsiEncryptionInfo;
 import android.telephony.NetworkScanRequest;
 import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
 
-import com.android.internal.telephony.PhoneConstants.*; // ????
+import com.android.internal.telephony.PhoneConstants.DataState;
 
 import java.util.List;
 
@@ -212,11 +212,11 @@
 
     // Used for CDMA roaming mode
     // Home Networks only, as defined in PRL
-    static final int CDMA_RM_HOME        = CarrierConfigManager.CDMA_ROAMING_MODE_HOME;
+    int CDMA_RM_HOME        = TelephonyManager.CDMA_ROAMING_MODE_HOME;
     // Roaming an Affiliated networks, as defined in PRL
-    static final int CDMA_RM_AFFILIATED  = CarrierConfigManager.CDMA_ROAMING_MODE_AFFILIATED;
+    int CDMA_RM_AFFILIATED  = TelephonyManager.CDMA_ROAMING_MODE_AFFILIATED;
     // Roaming on Any Network, as defined in PRL
-    static final int CDMA_RM_ANY         = CarrierConfigManager.CDMA_ROAMING_MODE_ANY;
+    int CDMA_RM_ANY         = TelephonyManager.CDMA_ROAMING_MODE_ANY;
 
     // Used for CDMA subscription mode
     static final int CDMA_SUBSCRIPTION_UNKNOWN  =-1; // Unknown
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index 9f7ea05..143b1c9 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2006 The Android Open Source Project
+ * Copyright (C) 2006 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -127,6 +127,7 @@
  */
 public class RIL extends BaseCommands implements CommandsInterface {
     static final String RILJ_LOG_TAG = "RILJ";
+    static final String RILJ_WAKELOCK_TAG = "*telephony-radio*";
     // Have a separate wakelock instance for Ack
     static final String RILJ_ACK_WAKELOCK_NAME = "RILJ_ACK_WL";
     static final boolean RILJ_LOGD = true;
@@ -468,7 +469,7 @@
         mRadioProxyDeathRecipient = new RadioProxyDeathRecipient();
 
         PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
-        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, RILJ_LOG_TAG);
+        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, RILJ_WAKELOCK_TAG);
         mWakeLock.setReferenceCounted(false);
         mAckWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, RILJ_ACK_WAKELOCK_NAME);
         mAckWakeLock.setReferenceCounted(false);
@@ -4259,13 +4260,6 @@
         return workSource;
     }
 
-    private String getWorkSourceClientId(WorkSource workSource) {
-        if (workSource != null) {
-            return String.valueOf(workSource.get(0)) + ":" + workSource.getName(0);
-        }
-
-        return null;
-    }
 
     /**
      * Holds a PARTIAL_WAKE_LOCK whenever
@@ -4289,7 +4283,7 @@
                         mWakeLockCount++;
                         mWlSequenceNum++;
 
-                        String clientId = getWorkSourceClientId(rr.mWorkSource);
+                        String clientId = rr.getWorkSourceClientId();
                         if (!mClientWakelockTracker.isClientActive(clientId)) {
                             if (mActiveWakelockWorkSource != null) {
                                 mActiveWakelockWorkSource.add(rr.mWorkSource);
@@ -4351,7 +4345,7 @@
                         mClientWakelockTracker.stopTracking(rr.mClientId,
                                 rr.mRequest, rr.mSerial,
                                 (mWakeLockCount > 1) ? mWakeLockCount - 1 : 0);
-                        String clientId = getWorkSourceClientId(rr.mWorkSource);;
+                        String clientId = rr.getWorkSourceClientId();
                         if (!mClientWakelockTracker.isClientActive(clientId)
                                 && (mActiveWakelockWorkSource != null)) {
                             mActiveWakelockWorkSource.remove(rr.mWorkSource);
diff --git a/src/java/com/android/internal/telephony/RILRequest.java b/src/java/com/android/internal/telephony/RILRequest.java
index d20ce22..2464cc3 100644
--- a/src/java/com/android/internal/telephony/RILRequest.java
+++ b/src/java/com/android/internal/telephony/RILRequest.java
@@ -20,8 +20,10 @@
 import android.os.Message;
 import android.os.SystemClock;
 import android.os.WorkSource;
+import android.os.WorkSource.WorkChain;
 import android.telephony.Rlog;
 
+import java.util.ArrayList;
 import java.util.Random;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -111,13 +113,14 @@
      * @param workSource WorkSource to track the client
      * @return a RILRequest instance from the pool.
      */
-    static RILRequest obtain(int request, Message result, WorkSource workSource) {
+    // @VisibleForTesting
+    public static RILRequest obtain(int request, Message result, WorkSource workSource) {
         RILRequest rr = null;
 
         rr = obtain(request, result);
         if (workSource != null) {
             rr.mWorkSource = workSource;
-            rr.mClientId = String.valueOf(workSource.get(0)) + ":" + workSource.getName(0);
+            rr.mClientId = rr.getWorkSourceClientId();
         } else {
             Rlog.e(LOG_TAG, "null workSource " + request);
         }
@@ -126,6 +129,28 @@
     }
 
     /**
+     * Generate a String client ID from the WorkSource.
+     */
+    // @VisibleForTesting
+    public String getWorkSourceClientId() {
+        if (mWorkSource == null || mWorkSource.isEmpty()) {
+            return null;
+        }
+
+        if (mWorkSource.size() > 0) {
+            return mWorkSource.get(0) + ":" + mWorkSource.getName(0);
+        }
+
+        final ArrayList<WorkChain> workChains = mWorkSource.getWorkChains();
+        if (workChains != null && !workChains.isEmpty()) {
+            final WorkChain workChain = workChains.get(0);
+            return workChain.getAttributionUid() + ":" + workChain.getTags()[0];
+        }
+
+        return null;
+    }
+
+    /**
      * Returns a RILRequest instance to the pool.
      *
      * Note: This should only be called once per use.
diff --git a/src/java/com/android/internal/telephony/RadioConfig.java b/src/java/com/android/internal/telephony/RadioConfig.java
index 764fc10..c258f6c 100644
--- a/src/java/com/android/internal/telephony/RadioConfig.java
+++ b/src/java/com/android/internal/telephony/RadioConfig.java
@@ -50,7 +50,6 @@
     private static final boolean DBG = true;
     private static final boolean VDBG = false;   //STOPSHIP if true
 
-    private static final String RADIO_CONFIG_SERVICE_NAME = "radioconfig";
     private static final int EVENT_SERVICE_DEAD = 1;
 
     private final boolean mIsMobileNetworkSupported;
@@ -167,7 +166,7 @@
         }
 
         try {
-            mRadioConfigProxy = IRadioConfig.getService(RADIO_CONFIG_SERVICE_NAME, true);
+            mRadioConfigProxy = IRadioConfig.getService(true);
             if (mRadioConfigProxy != null) {
                 mRadioConfigProxy.linkToDeath(mServiceDeathRecipient,
                         mRadioConfigProxyCookie.incrementAndGet());
diff --git a/src/java/com/android/internal/telephony/RatRatcheter.java b/src/java/com/android/internal/telephony/RatRatcheter.java
index 3745182..6b76414 100644
--- a/src/java/com/android/internal/telephony/RatRatcheter.java
+++ b/src/java/com/android/internal/telephony/RatRatcheter.java
@@ -124,9 +124,9 @@
             int newDataRat = ratchetRat(oldSS.getRilDataRadioTechnology(),
                     newSS.getRilDataRadioTechnology());
             newSS.setRilDataRadioTechnology(newDataRat);
-        } else if (oldSS.getRilVoiceRadioTechnology() != newSS.getRilVoiceRadioTechnology()) {
+        } else if (oldSS.getRilDataRadioTechnology() != newSS.getRilDataRadioTechnology()) {
             // resume rat ratchet on following rat change within the same location
-            mVoiceRatchetEnabled = true;
+            mDataRatchetEnabled = true;
         }
 
         boolean newUsingCA = oldSS.isUsingCarrierAggregation()
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index b572934..f05471f 100644
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -79,6 +79,7 @@
 import android.util.LocalLog;
 import android.util.Pair;
 import android.util.SparseArray;
+import android.util.StatsLog;
 import android.util.TimeUtils;
 import android.util.TimestampedValue;
 
@@ -2917,6 +2918,9 @@
 
         if (hasRilDataRadioTechnologyChanged) {
             tm.setDataNetworkTypeForPhone(mPhone.getPhoneId(), mSS.getRilDataRadioTechnology());
+            StatsLog.write(StatsLog.MOBILE_RADIO_TECHNOLOGY_CHANGED,
+                    ServiceState.rilRadioTechnologyToNetworkType(mSS.getRilDataRadioTechnology()),
+                    mPhone.getPhoneId());
 
             if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
                     == mSS.getRilDataRadioTechnology()) {
diff --git a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
index c16ba7d..4a08445 100644
--- a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
+++ b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
@@ -393,20 +393,12 @@
                 ContentResolver contentResolver = mContext.getContentResolver();
 
                 if (msisdn != null) {
-                    ContentValues number = new ContentValues(1);
-                    number.put(SubscriptionManager.NUMBER, msisdn);
-                    contentResolver.update(SubscriptionManager.CONTENT_URI, number,
-                            SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "="
-                                    + Long.toString(subId), null);
-
-                    // refresh Cached Active Subscription Info List
-                    SubscriptionController.getInstance().refreshCachedActiveSubscriptionInfoList();
+                    SubscriptionController.getInstance().setDisplayNumber(msisdn, subId);
                 }
 
                 SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(subId);
                 String nameToSet;
                 String simCarrierName = tm.getSimOperatorName(subId);
-                ContentValues name = new ContentValues(1);
 
                 if (subInfo != null && subInfo.getNameSource() !=
                         SubscriptionManager.NAME_SOURCE_USER_INPUT) {
@@ -415,14 +407,8 @@
                     } else {
                         nameToSet = "CARD " + Integer.toString(slotId + 1);
                     }
-                    name.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
                     logd("sim name = " + nameToSet);
-                    contentResolver.update(SubscriptionManager.CONTENT_URI, name,
-                            SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID
-                                    + "=" + Long.toString(subId), null);
-
-                    // refresh Cached Active Subscription Info List
-                    SubscriptionController.getInstance().refreshCachedActiveSubscriptionInfoList();
+                    SubscriptionController.getInstance().setDisplayName(nameToSet, subId);
                 }
 
                 /* Update preferred network type and network selection mode on SIM change.
diff --git a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
index dc8d9fa..856f39f 100644
--- a/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
+++ b/src/java/com/android/internal/telephony/TelephonyComponentFactory.java
@@ -181,6 +181,7 @@
         return IDeviceIdleController.Stub.asInterface(
                 ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
     }
+
     public LocaleTracker makeLocaleTracker(Phone phone, Looper looper) {
         return new LocaleTracker(phone, looper);
     }
diff --git a/src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java b/src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java
index 1294762..a87cbf1 100644
--- a/src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java
+++ b/src/java/com/android/internal/telephony/VisualVoicemailSmsFilter.java
@@ -95,6 +95,7 @@
      * Wrapper to combine multiple PDU into an SMS message
      */
     private static class FullMessage {
+
         public SmsMessage firstMessage;
         public String fullMessageBody;
     }
@@ -143,7 +144,7 @@
             WrappedMessageData messageData = VisualVoicemailSmsParser
                     .parseAlternativeFormat(asciiMessage);
             if (messageData != null) {
-                sendVvmSmsBroadcast(context, phoneAccountHandle, messageData, null);
+                sendVvmSmsBroadcast(context, settings, phoneAccountHandle, messageData, null);
             }
             // Confidence for what the message actually is is low. Don't remove the message and let
             // system decide. Usually because it is not parsable it will be dropped.
@@ -177,7 +178,7 @@
                 return false;
             }
 
-            sendVvmSmsBroadcast(context, phoneAccountHandle, messageData, null);
+            sendVvmSmsBroadcast(context, settings, phoneAccountHandle, messageData, null);
             return true;
         }
 
@@ -193,7 +194,7 @@
             if (pattern.matcher(messageBody).matches()) {
                 Log.w(TAG, "Incoming SMS matches pattern " + pattern + " but has illegal format, "
                         + "still dropping as VVM SMS");
-                sendVvmSmsBroadcast(context, phoneAccountHandle, null, messageBody);
+                sendVvmSmsBroadcast(context, settings, phoneAccountHandle, null, messageBody);
                 return true;
             }
         }
@@ -233,7 +234,8 @@
         }
     }
 
-    private static void sendVvmSmsBroadcast(Context context, PhoneAccountHandle phoneAccountHandle,
+    private static void sendVvmSmsBroadcast(Context context,
+            VisualVoicemailSmsFilterSettings filterSettings, PhoneAccountHandle phoneAccountHandle,
             @Nullable WrappedMessageData messageData, @Nullable String messageBody) {
         Log.i(TAG, "VVM SMS received");
         Intent intent = new Intent(VoicemailContract.ACTION_VOICEMAIL_SMS_RECEIVED);
@@ -247,6 +249,7 @@
         }
         builder.setPhoneAccountHandle(phoneAccountHandle);
         intent.putExtra(VoicemailContract.EXTRA_VOICEMAIL_SMS, builder.build());
+        intent.putExtra(VoicemailContract.EXTRA_TARGET_PACKAGE, filterSettings.packageName);
         intent.setPackage(TELEPHONY_SERVICE_PACKAGE);
         context.sendBroadcast(intent);
     }
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
index 00af9fe..c392e27 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
@@ -52,6 +52,7 @@
 import android.util.LocalLog;
 import android.util.Pair;
 import android.util.SparseArray;
+import android.util.StatsLog;
 import android.util.TimeUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -1475,6 +1476,12 @@
         public void enter() {
             mTag += 1;
             if (DBG) log("DcInactiveState: enter() mTag=" + mTag);
+            StatsLog.write(StatsLog.MOBILE_CONNECTION_STATE_CHANGED,
+                    StatsLog.MOBILE_CONNECTION_STATE_CHANGED__STATE__INACTIVE,
+                    mPhone.getPhoneId(), mId,
+                    mApnSetting != null ? (long) mApnSetting.getApnTypeBitmask() : 0L,
+                    mApnSetting != null
+                        ? mApnSetting.canHandleType(ApnSetting.TYPE_DEFAULT) : false);
 
             if (mConnectionParams != null) {
                 if (DBG) {
@@ -1565,6 +1572,15 @@
      */
     private class DcActivatingState extends State {
         @Override
+        public void enter() {
+            StatsLog.write(StatsLog.MOBILE_CONNECTION_STATE_CHANGED,
+                    StatsLog.MOBILE_CONNECTION_STATE_CHANGED__STATE__ACTIVATING,
+                    mPhone.getPhoneId(), mId,
+                    mApnSetting != null ? (long) mApnSetting.getApnTypeBitmask() : 0L,
+                    mApnSetting != null
+                        ? mApnSetting.canHandleType(ApnSetting.TYPE_DEFAULT) : false);
+        }
+        @Override
         public boolean processMessage(Message msg) {
             boolean retVal;
             AsyncResult ar;
@@ -1672,6 +1688,12 @@
 
         @Override public void enter() {
             if (DBG) log("DcActiveState: enter dc=" + DataConnection.this);
+            StatsLog.write(StatsLog.MOBILE_CONNECTION_STATE_CHANGED,
+                    StatsLog.MOBILE_CONNECTION_STATE_CHANGED__STATE__ACTIVE,
+                    mPhone.getPhoneId(), mId,
+                    mApnSetting != null ? (long) mApnSetting.getApnTypeBitmask() : 0L,
+                    mApnSetting != null
+                        ? mApnSetting.canHandleType(ApnSetting.TYPE_DEFAULT) : false);
 
             updateNetworkInfo();
 
@@ -1979,6 +2001,15 @@
      */
     private class DcDisconnectingState extends State {
         @Override
+        public void enter() {
+            StatsLog.write(StatsLog.MOBILE_CONNECTION_STATE_CHANGED,
+                    StatsLog.MOBILE_CONNECTION_STATE_CHANGED__STATE__DISCONNECTING,
+                    mPhone.getPhoneId(), mId,
+                    mApnSetting != null ? (long) mApnSetting.getApnTypeBitmask() : 0L,
+                    mApnSetting != null
+                        ? mApnSetting.canHandleType(ApnSetting.TYPE_DEFAULT) : false);
+        }
+        @Override
         public boolean processMessage(Message msg) {
             boolean retVal;
 
@@ -2028,6 +2059,15 @@
      */
     private class DcDisconnectionErrorCreatingConnection extends State {
         @Override
+        public void enter() {
+            StatsLog.write(StatsLog.MOBILE_CONNECTION_STATE_CHANGED,
+                    StatsLog.MOBILE_CONNECTION_STATE_CHANGED__STATE__DISCONNECTION_ERROR_CREATING_CONNECTION,
+                    mPhone.getPhoneId(), mId,
+                    mApnSetting != null ? (long) mApnSetting.getApnTypeBitmask() : 0L,
+                    mApnSetting != null
+                        ? mApnSetting.canHandleType(ApnSetting.TYPE_DEFAULT) : false);
+        }
+        @Override
         public boolean processMessage(Message msg) {
             boolean retVal;
 
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java b/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java
index 8c3f751..5e01686 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataServiceManager.java
@@ -16,10 +16,18 @@
 
 package com.android.internal.telephony.dataconnection;
 
+import static android.telephony.AccessNetworkConstants.TransportType.WLAN;
+import static android.telephony.AccessNetworkConstants.TransportType.WWAN;
+
+import android.annotation.NonNull;
+import android.app.AppOpsManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.net.LinkProperties;
 import android.os.AsyncResult;
 import android.os.Handler;
@@ -28,7 +36,7 @@
 import android.os.PersistableBundle;
 import android.os.RegistrantList;
 import android.os.RemoteException;
-import android.telephony.AccessNetworkConstants;
+import android.os.ServiceManager;
 import android.telephony.CarrierConfigManager;
 import android.telephony.Rlog;
 import android.telephony.data.DataCallResponse;
@@ -41,8 +49,10 @@
 
 import com.android.internal.telephony.Phone;
 
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
 /**
@@ -58,6 +68,8 @@
     private final Phone mPhone;
 
     private final CarrierConfigManager mCarrierConfigManager;
+    private final AppOpsManager mAppOps;
+    private final IPackageManager mPackageManager;
 
     private final int mTransportType;
 
@@ -73,14 +85,10 @@
 
     private final RegistrantList mDataCallListChangedRegistrants = new RegistrantList();
 
+    // not final because it is set by the onServiceConnected method
+    private ComponentName mComponentName;
+
     private class DataServiceManagerDeathRecipient implements IBinder.DeathRecipient {
-
-        private final ComponentName mComponentName;
-
-        DataServiceManagerDeathRecipient(ComponentName name) {
-            mComponentName = name;
-        }
-
         @Override
         public void binderDied() {
             // TODO: try to rebind the service.
@@ -89,12 +97,53 @@
         }
     }
 
+    private void grantPermissionsToService(String packageName) {
+        final String[] pkgToGrant = {packageName};
+        try {
+            mPackageManager.grantDefaultPermissionsToEnabledTelephonyDataServices(
+                    pkgToGrant, mPhone.getContext().getUserId());
+            mAppOps.setMode(AppOpsManager.OP_MANAGE_IPSEC_TUNNELS, mPhone.getContext().getUserId(),
+                    pkgToGrant[0], AppOpsManager.MODE_ALLOWED);
+        } catch (RemoteException e) {
+            loge("Binder to package manager died, permission grant for DataService failed.");
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /**
+     * Loop through all DataServices installed on the system and revoke permissions from any that
+     * are not currently the WWAN or WLAN data service.
+     */
+    private void revokePermissionsFromUnusedDataServices() {
+        // Except the current data services from having their permissions removed.
+        Set<String> dataServices = getAllDataServicePackageNames();
+        for (int transportType : new int[] {WWAN, WLAN}) {
+            dataServices.remove(getDataServicePackageName(transportType));
+        }
+
+        try {
+            String[] dataServicesArray = new String[dataServices.size()];
+            dataServices.toArray(dataServicesArray);
+            mPackageManager.revokeDefaultPermissionsFromDisabledTelephonyDataServices(
+                    dataServicesArray, mPhone.getContext().getUserId());
+            for (String pkg : dataServices) {
+                mAppOps.setMode(AppOpsManager.OP_MANAGE_IPSEC_TUNNELS,
+                        mPhone.getContext().getUserId(),
+                        pkg, AppOpsManager.MODE_ERRORED);
+            }
+        } catch (RemoteException e) {
+            loge("Binder to package manager died; failed to revoke DataService permissions.");
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
     private final class CellularDataServiceConnection implements ServiceConnection {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
             if (DBG) log("onServiceConnected");
+            mComponentName = name;
             mIDataService = IDataService.Stub.asInterface(service);
-            mDeathRecipient = new DataServiceManagerDeathRecipient(name);
+            mDeathRecipient = new DataServiceManagerDeathRecipient();
             mBound = true;
 
             try {
@@ -186,16 +235,22 @@
         mBound = false;
         mCarrierConfigManager = (CarrierConfigManager) phone.getContext().getSystemService(
                 Context.CARRIER_CONFIG_SERVICE);
-
+        mPackageManager = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
+        mAppOps = (AppOpsManager) phone.getContext().getSystemService(Context.APP_OPS_SERVICE);
         bindDataService();
     }
 
     private void bindDataService() {
+        // Start by cleaning up all packages that *shouldn't* have permissions.
+        revokePermissionsFromUnusedDataServices();
+
         String packageName = getDataServicePackageName();
         if (TextUtils.isEmpty(packageName)) {
             loge("Can't find the binding package");
             return;
         }
+        // Then pre-emptively grant the permissions to the package we will bind.
+        grantPermissionsToService(packageName);
 
         try {
             if (!mPhone.getContext().bindService(
@@ -209,18 +264,55 @@
         }
     }
 
+    @NonNull
+    private Set<String> getAllDataServicePackageNames() {
+        // Cowardly using the public PackageManager interface here.
+        // Note: This matches only packages that were installed on the system image. If we ever
+        // expand the permissions model to allow CarrierPrivileged packages, then this will need
+        // to be updated.
+        List<ResolveInfo> dataPackages =
+                mPhone.getContext().getPackageManager().queryIntentServices(
+                        new Intent(DataService.DATA_SERVICE_INTERFACE),
+                                PackageManager.MATCH_SYSTEM_ONLY);
+        HashSet<String> packageNames = new HashSet<>();
+        for (ResolveInfo info : dataPackages) {
+            if (info.serviceInfo == null) continue;
+            packageNames.add(info.serviceInfo.packageName);
+        }
+        return packageNames;
+    }
+
+    /**
+     * Get the data service package name for our current transport type.
+     *
+     * @return package name of the data service package for the the current transportType.
+     */
     private String getDataServicePackageName() {
+        return getDataServicePackageName(mTransportType);
+    }
+
+    /**
+     * Get the data service package by transport type.
+     *
+     * When we bind to a DataService package, we need to revoke permissions from stale
+     * packages; we need to exclude data packages for all transport types, so we need to
+     * to be able to query by transport type.
+     *
+     * @param transportType either WWAN or WLAN
+     * @return package name of the data service package for the specified transportType.
+     */
+    private String getDataServicePackageName(int transportType) {
         String packageName;
         int resourceId;
         String carrierConfig;
 
-        switch (mTransportType) {
-            case AccessNetworkConstants.TransportType.WWAN:
+        switch (transportType) {
+            case WWAN:
                 resourceId = com.android.internal.R.string.config_wwan_data_service_package;
                 carrierConfig = CarrierConfigManager
                         .KEY_CARRIER_DATA_SERVICE_WWAN_PACKAGE_OVERRIDE_STRING;
                 break;
-            case AccessNetworkConstants.TransportType.WLAN:
+            case WLAN:
                 resourceId = com.android.internal.R.string.config_wlan_data_service_package;
                 carrierConfig = CarrierConfigManager
                         .KEY_CARRIER_DATA_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING;
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
index 50130c8..8d9cb59 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony.dataconnection;
 
+import static android.Manifest.permission.READ_PHONE_STATE;
+
 import android.annotation.NonNull;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
@@ -4538,11 +4540,20 @@
         if (VDBG_STALL) log("putRecoveryAction: " + action);
     }
 
+    private void broadcastDataStallDetected(int recoveryAction) {
+        Intent intent = new Intent(TelephonyManager.ACTION_DATA_STALL_DETECTED);
+        SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
+        intent.putExtra(TelephonyManager.EXTRA_RECOVERY_ACTION, recoveryAction);
+        mPhone.getContext().sendBroadcast(intent, READ_PHONE_STATE);
+    }
+
     private void doRecovery() {
         if (getOverallState() == DctConstants.State.CONNECTED) {
             // Go through a series of recovery steps, each action transitions to the next action
             final int recoveryAction = getRecoveryAction();
             TelephonyMetrics.getInstance().writeDataStallEvent(mPhone.getPhoneId(), recoveryAction);
+            broadcastDataStallDetected(recoveryAction);
+
             switch (recoveryAction) {
                 case RecoveryAction.GET_DATA_CALL_LIST:
                     EventLog.writeEvent(EventLogTags.DATA_STALL_RECOVERY_GET_DATA_CALL_LIST,
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccCardController.java b/src/java/com/android/internal/telephony/euicc/EuiccCardController.java
index d3c59a3..5c5d244 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccCardController.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccCardController.java
@@ -220,6 +220,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("getAllProfiles callback onException: ", e);
                     callback.onComplete(getResultCode(e), null);
                 } catch (RemoteException exception) {
                     loge("getAllProfiles callback failure.", exception);
@@ -258,6 +259,7 @@
                     @Override
                     public void onException(Throwable e) {
                         try {
+                            loge("getProfile callback onException: ", e);
                             callback.onComplete(getResultCode(e), null);
                         } catch (RemoteException exception) {
                             loge("getProfile callback failure.", exception);
@@ -296,6 +298,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("disableProfile callback onException: ", e);
                     callback.onComplete(getResultCode(e));
                 } catch (RemoteException exception) {
                     loge("disableProfile callback failure.", exception);
@@ -338,6 +341,7 @@
                     @Override
                     public void onException(Throwable e) {
                         try {
+                            loge("switchToProfile callback onException: ", e);
                             callback.onComplete(getResultCode(e), profile);
                         } catch (RemoteException exception) {
                             loge("switchToProfile callback failure.", exception);
@@ -351,6 +355,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("getProfile in switchToProfile callback onException: ", e);
                     callback.onComplete(getResultCode(e), null);
                 } catch (RemoteException exception) {
                     loge("switchToProfile callback failure.", exception);
@@ -389,6 +394,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("setNickname callback onException: ", e);
                     callback.onComplete(getResultCode(e));
                 } catch (RemoteException exception) {
                     loge("setNickname callback failure.", exception);
@@ -417,19 +423,19 @@
         AsyncResultCallback<Void> cardCb = new AsyncResultCallback<Void>() {
             @Override
             public void onResult(Void result) {
-                SubscriptionController.getInstance().requestEmbeddedSubscriptionInfoListRefresh(
-                        () -> {
-                            try {
-                                callback.onComplete(EuiccCardManager.RESULT_OK);
-                            } catch (RemoteException exception) {
-                                loge("deleteProfile callback failure.", exception);
-                            }
-                        });
+                Log.i(TAG, "Request subscription info list refresh after delete.");
+                SubscriptionController.getInstance().requestEmbeddedSubscriptionInfoListRefresh();
+                try {
+                    callback.onComplete(EuiccCardManager.RESULT_OK);
+                } catch (RemoteException exception) {
+                    loge("deleteProfile callback failure.", exception);
+                }
             };
 
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("deleteProfile callback onException: ", e);
                     callback.onComplete(getResultCode(e));
                 } catch (RemoteException exception) {
                     loge("deleteProfile callback failure.", exception);
@@ -458,19 +464,19 @@
         AsyncResultCallback<Void> cardCb = new AsyncResultCallback<Void>() {
             @Override
             public void onResult(Void result) {
-                SubscriptionController.getInstance().requestEmbeddedSubscriptionInfoListRefresh(
-                        () -> {
-                            try {
-                                callback.onComplete(EuiccCardManager.RESULT_OK);
-                            } catch (RemoteException exception) {
-                                loge("resetMemory callback failure.", exception);
-                            }
-                        });
+                Log.i(TAG, "Request subscription info list refresh after reset memory.");
+                SubscriptionController.getInstance().requestEmbeddedSubscriptionInfoListRefresh();
+                try {
+                    callback.onComplete(EuiccCardManager.RESULT_OK);
+                } catch (RemoteException exception) {
+                    loge("resetMemory callback failure.", exception);
+                }
             }
 
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("resetMemory callback onException: ", e);
                     callback.onComplete(getResultCode(e));
                 } catch (RemoteException exception) {
                     loge("resetMemory callback failure.", exception);
@@ -509,6 +515,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("getDefaultSmdpAddress callback onException: ", e);
                     callback.onComplete(getResultCode(e), null);
                 } catch (RemoteException exception) {
                     loge("getDefaultSmdpAddress callback failure.", exception);
@@ -547,6 +554,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("getSmdsAddress callback onException: ", e);
                     callback.onComplete(getResultCode(e), null);
                 } catch (RemoteException exception) {
                     loge("getSmdsAddress callback failure.", exception);
@@ -585,6 +593,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("setDefaultSmdpAddress callback onException: ", e);
                     callback.onComplete(getResultCode(e));
                 } catch (RemoteException exception) {
                     loge("setDefaultSmdpAddress callback failure.", exception);
@@ -624,6 +633,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("getRulesAuthTable callback onException: ", e);
                     callback.onComplete(getResultCode(e), null);
                 } catch (RemoteException exception) {
                     loge("getRulesAuthTable callback failure.", exception);
@@ -662,6 +672,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("getEuiccChallenge callback onException: ", e);
                     callback.onComplete(getResultCode(e), null);
                 } catch (RemoteException exception) {
                     loge("getEuiccChallenge callback failure.", exception);
@@ -700,6 +711,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("getEuiccInfo1 callback onException: ", e);
                     callback.onComplete(getResultCode(e), null);
                 } catch (RemoteException exception) {
                     loge("getEuiccInfo1 callback failure.", exception);
@@ -738,6 +750,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("getEuiccInfo2 callback onException: ", e);
                     callback.onComplete(getResultCode(e), null);
                 } catch (RemoteException exception) {
                     loge("getEuiccInfo2 callback failure.", exception);
@@ -777,6 +790,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("authenticateServer callback onException: ", e);
                     callback.onComplete(getResultCode(e), null);
                 } catch (RemoteException exception) {
                     loge("authenticateServer callback failure.", exception);
@@ -817,6 +831,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("prepareDownload callback onException: ", e);
                     callback.onComplete(getResultCode(e), null);
                 } catch (RemoteException exception) {
                     loge("prepareDownload callback failure.", exception);
@@ -846,19 +861,19 @@
         AsyncResultCallback<byte[]> cardCb = new AsyncResultCallback<byte[]>() {
             @Override
             public void onResult(byte[] result) {
-                SubscriptionController.getInstance().requestEmbeddedSubscriptionInfoListRefresh(
-                        () -> {
-                            try {
-                                callback.onComplete(EuiccCardManager.RESULT_OK, result);
-                            } catch (RemoteException exception) {
-                                loge("loadBoundProfilePackage callback failure.", exception);
-                            }
-                        });
+                Log.i(TAG, "Request subscription info list refresh after install.");
+                SubscriptionController.getInstance().requestEmbeddedSubscriptionInfoListRefresh();
+                try {
+                    callback.onComplete(EuiccCardManager.RESULT_OK, result);
+                } catch (RemoteException exception) {
+                    loge("loadBoundProfilePackage callback failure.", exception);
+                }
             }
 
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("loadBoundProfilePackage callback onException: ", e);
                     callback.onComplete(getResultCode(e), null);
                 } catch (RemoteException exception) {
                     loge("loadBoundProfilePackage callback failure.", exception);
@@ -897,6 +912,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("cancelSession callback onException: ", e);
                     callback.onComplete(getResultCode(e), null);
                 } catch (RemoteException exception) {
                     loge("cancelSession callback failure.", exception);
@@ -936,6 +952,7 @@
             @Override
             public void onException(Throwable e) {
                 try {
+                    loge("listNotifications callback onException: ", e);
                     callback.onComplete(getResultCode(e), null);
                 } catch (RemoteException exception) {
                     loge("listNotifications callback failure.", exception);
@@ -975,6 +992,7 @@
                     @Override
                     public void onException(Throwable e) {
                         try {
+                            loge("retrieveNotificationList callback onException: ", e);
                             callback.onComplete(getResultCode(e), null);
                         } catch (RemoteException exception) {
                             loge("retrieveNotificationList callback failure.", exception);
@@ -1014,6 +1032,7 @@
                     @Override
                     public void onException(Throwable e) {
                         try {
+                            loge("retrieveNotification callback onException: ", e);
                             callback.onComplete(getResultCode(e), null);
                         } catch (RemoteException exception) {
                             loge("retrieveNotification callback failure.", exception);
@@ -1053,6 +1072,7 @@
                     @Override
                     public void onException(Throwable e) {
                         try {
+                            loge("removeNotificationFromList callback onException: ", e);
                             callback.onComplete(getResultCode(e));
                         } catch (RemoteException exception) {
                             loge("removeNotificationFromList callback failure.", exception);
diff --git a/src/java/com/android/internal/telephony/euicc/EuiccController.java b/src/java/com/android/internal/telephony/euicc/EuiccController.java
index 8c1b81e..ca36cf0 100644
--- a/src/java/com/android/internal/telephony/euicc/EuiccController.java
+++ b/src/java/com/android/internal/telephony/euicc/EuiccController.java
@@ -24,6 +24,7 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ComponentInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.os.Binder;
@@ -984,6 +985,10 @@
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     public void sendOtaStatusChangedBroadcast() {
         Intent intent = new Intent(EuiccManager.ACTION_OTA_STATUS_CHANGED);
+        ComponentInfo bestComponent = mConnector.findBestComponent(mContext.getPackageManager());
+        if (bestComponent != null) {
+            intent.setPackage(bestComponent.packageName);
+        }
         mContext.sendBroadcast(intent, permission.WRITE_EMBEDDED_SUBSCRIPTIONS);
     }
 
diff --git a/src/java/com/android/internal/telephony/gsm/GsmCellBroadcastHandler.java b/src/java/com/android/internal/telephony/gsm/GsmCellBroadcastHandler.java
index dec9805..f30b386 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmCellBroadcastHandler.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmCellBroadcastHandler.java
@@ -78,6 +78,7 @@
                 handleBroadcastSms(cbMessage);
                 return true;
             }
+            if (VDBG) log("Not handled GSM broadcasts.");
         }
         return super.handleSmsMessage(message);
     }
@@ -106,6 +107,7 @@
             }
 
             SmsCbHeader header = new SmsCbHeader(receivedPdu);
+            if (VDBG) log("header=" + header);
             String plmn = TelephonyManager.from(mContext).getNetworkOperatorForPhone(
                     mPhone.getPhoneId());
             int lac = -1;
@@ -154,12 +156,14 @@
                     mSmsCbPageMap.put(concatInfo, pdus);
                 }
 
+                if (VDBG) log("pdus size=" + pdus.length);
                 // Page parameter is one-based
                 pdus[header.getPageIndex() - 1] = receivedPdu;
 
                 for (byte[] pdu : pdus) {
                     if (pdu == null) {
                         // Still missing pages, exit
+                        log("still missing pdu");
                         return null;
                     }
                 }
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index eb77a93..94b771f 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -312,6 +312,7 @@
     @VisibleForTesting
     public void setServiceState(int state) {
         boolean isVoiceRegStateChanged = false;
+
         synchronized (this) {
             isVoiceRegStateChanged = mSS.getVoiceRegState() != state;
             mSS.setVoiceRegState(state);
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
index e573fbd..7361e70 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
@@ -499,9 +499,6 @@
         return false;
     }
 
-    public void saveClirSetting(int commandInterfaceCLIRMode) {
-    }
-
     @Override
     public IccPhoneBookInterfaceManager getIccPhoneBookInterfaceManager(){
         return null;
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index f32b74c..77ee27a 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -25,7 +25,10 @@
 import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
+import android.net.NetworkRequest;
 import android.net.NetworkStats;
 import android.net.Uri;
 import android.os.AsyncResult;
@@ -243,6 +246,24 @@
         }
     };
 
+    /**
+     * Tracks whether we are currently monitoring network connectivity for the purpose of warning
+     * the user of an inability to handover from LTE to WIFI for video calls.
+     */
+    private boolean mIsMonitoringConnectivity = false;
+
+    /**
+     * Network callback used to schedule the handover check when a wireless network connects.
+     */
+    private ConnectivityManager.NetworkCallback mNetworkCallback =
+            new ConnectivityManager.NetworkCallback() {
+                @Override
+                public void onAvailable(Network network) {
+                    Rlog.i(LOG_TAG, "Network available: " + network);
+                    scheduleHandoverCheck();
+                }
+            };
+
     //***** Constants
 
     static final int MAX_CONNECTIONS = 7;
@@ -598,6 +619,22 @@
     private boolean mNotifyHandoverVideoFromWifiToLTE = false;
 
     /**
+     * Carrier configuration option which determines whether the carrier wants to inform the user
+     * when a video call is handed over from LTE to WIFI.
+     * See {@link CarrierConfigManager#KEY_NOTIFY_HANDOVER_VIDEO_FROM_LTE_TO_WIFI_BOOL} for more
+     * information.
+     */
+    private boolean mNotifyHandoverVideoFromLTEToWifi = false;
+
+    /**
+     * When {@code} false, indicates that no handover from LTE to WIFI has occurred during the start
+     * of the call.
+     * When {@code true}, indicates that the start of call handover from LTE to WIFI has been
+     * attempted (it may have suceeded or failed).
+     */
+    private boolean mHasPerformedStartOfCallHandover = false;
+
+    /**
      * Carrier configuration option which determines whether the carrier supports the
      * {@link VideoProfile#STATE_PAUSED} signalling.
      * See {@link CarrierConfigManager#KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL} for more information.
@@ -1032,6 +1069,16 @@
         }
         mCarrierConfigLoaded = true;
 
+        updateCarrierConfigCache(carrierConfig);
+    }
+
+    /**
+     * Updates the local carrier config cache from a bundle obtained from the carrier config
+     * manager.  Also supports unit testing by injecting configuration at test time.
+     * @param carrierConfig The config bundle.
+     */
+    @VisibleForTesting
+    public void updateCarrierConfigCache(PersistableBundle carrierConfig) {
         mAllowEmergencyVideoCalls =
                 carrierConfig.getBoolean(CarrierConfigManager.KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL);
         mTreatDowngradedVideoCallsAsVideoCalls =
@@ -1049,6 +1096,8 @@
                 CarrierConfigManager.KEY_SUPPORT_DOWNGRADE_VT_TO_AUDIO_BOOL);
         mNotifyHandoverVideoFromWifiToLTE = carrierConfig.getBoolean(
                 CarrierConfigManager.KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL);
+        mNotifyHandoverVideoFromLTEToWifi = carrierConfig.getBoolean(
+                CarrierConfigManager.KEY_NOTIFY_HANDOVER_VIDEO_FROM_LTE_TO_WIFI_BOOL);
         mIgnoreDataEnabledChangedForVideoCalls = carrierConfig.getBoolean(
                 CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS);
         mIsViLteDataMetered = carrierConfig.getBoolean(
@@ -2116,13 +2165,18 @@
             processCallStateChange(imsCall, ImsPhoneCall.State.ACTIVE,
                     DisconnectCause.NOT_DISCONNECTED);
 
-            if (mNotifyVtHandoverToWifiFail &&
-                    !imsCall.isWifiCall() && imsCall.isVideoCall() && isWifiConnected()) {
-                // Schedule check to see if handover succeeded.
-                sendMessageDelayed(obtainMessage(EVENT_CHECK_FOR_WIFI_HANDOVER, imsCall),
-                        HANDOVER_TO_WIFI_TIMEOUT_MS);
+            if (mNotifyVtHandoverToWifiFail && imsCall.isVideoCall() && !imsCall.isWifiCall()) {
+                if (isWifiConnected()) {
+                    // Schedule check to see if handover succeeded.
+                    sendMessageDelayed(obtainMessage(EVENT_CHECK_FOR_WIFI_HANDOVER, imsCall),
+                            HANDOVER_TO_WIFI_TIMEOUT_MS);
+                } else {
+                    // No wifi connectivity, so keep track of network availability for potential
+                    // handover.
+                    registerForConnectivityChanges();
+                }
             }
-
+            mHasPerformedStartOfCallHandover = false;
             mMetrics.writeOnImsCallStarted(mPhone.getPhoneId(), imsCall.getCallSession());
         }
 
@@ -2607,18 +2661,35 @@
             boolean isHandoverToWifi = srcAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN
                     && srcAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
                     && targetAccessTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
-            if (isHandoverToWifi) {
-                // If we handed over to wifi successfully, don't check for failure in the future.
-                removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER);
-            }
+            // Only consider it a handover from WIFI if the source and target radio tech is known.
+            boolean isHandoverFromWifi =
+                    srcAccessTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+                            && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN
+                            && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
 
             ImsPhoneConnection conn = findConnection(imsCall);
             if (conn != null) {
-                // Only consider it a handover from WIFI if the source and target radio tech is known.
-                boolean isHandoverFromWifi =
-                        srcAccessTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
-                                && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN
-                                && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
+                if (conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) {
+                    if (isHandoverToWifi) {
+                        removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER);
+
+                        if (mNotifyHandoverVideoFromLTEToWifi && mHasPerformedStartOfCallHandover) {
+                            // This is a handover which happened mid-call (ie not the start of call
+                            // handover from LTE to WIFI), so we'll notify the InCall UI.
+                            conn.onConnectionEvent(
+                                    TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_LTE_TO_WIFI, null);
+                        }
+
+                        // We are on WIFI now so no need to get notified of network availability.
+                        unregisterForConnectivityChanges();
+                    } else if (isHandoverFromWifi && imsCall.isVideoCall()) {
+                        // A video call just dropped from WIFI to LTE; we want to be informed if a
+                        // new WIFI
+                        // network comes into range.
+                        registerForConnectivityChanges();
+                    }
+                }
+
                 if (isHandoverFromWifi && imsCall.isVideoCall()) {
                     if (mNotifyHandoverVideoFromWifiToLTE &&    mIsDataEnabled) {
                         if (conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) {
@@ -2644,6 +2715,9 @@
                 loge("onCallHandover :: connection null.");
             }
 
+            if (!mHasPerformedStartOfCallHandover) {
+                mHasPerformedStartOfCallHandover = true;
+            }
             mMetrics.writeOnImsCallHandoverEvent(mPhone.getPhoneId(),
                     TelephonyCallSession.Event.Type.IMS_CALL_HANDOVER, imsCall.getCallSession(),
                     srcAccessTech, targetAccessTech, reasonInfo);
@@ -2669,11 +2743,20 @@
                 // If we know we failed to handover, don't check for failure in the future.
                 removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER);
 
+                if (imsCall.isVideoCall()
+                        && conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) {
+                    // Start listening for a WIFI network to come into range for potential handover.
+                    registerForConnectivityChanges();
+                }
+
                 if (mNotifyVtHandoverToWifiFail) {
                     // Only notify others if carrier config indicates to do so.
                     conn.onHandoverToWifiFailed();
                 }
             }
+            if (!mHasPerformedStartOfCallHandover) {
+                mHasPerformedStartOfCallHandover = true;
+            }
         }
 
         @Override
@@ -2747,6 +2830,8 @@
         public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) {
             if (DBG) log("mImsUssdListener onCallTerminated reasonCode=" + reasonInfo.getCode());
             removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER);
+            mHasPerformedStartOfCallHandover = false;
+            unregisterForConnectivityChanges();
 
             if (imsCall == mUssdSession) {
                 mUssdSession = null;
@@ -3029,12 +3114,24 @@
             case EVENT_CHECK_FOR_WIFI_HANDOVER:
                 if (msg.obj instanceof ImsCall) {
                     ImsCall imsCall = (ImsCall) msg.obj;
+                    if (imsCall != mForegroundCall.getImsCall()) {
+                        Rlog.i(LOG_TAG, "handoverCheck: no longer FG; check skipped.");
+                        unregisterForConnectivityChanges();
+                        // Handover check and its not the foreground call any more.
+                        return;
+                    }
                     if (!imsCall.isWifiCall()) {
                         // Call did not handover to wifi, notify of handover failure.
                         ImsPhoneConnection conn = findConnection(imsCall);
                         if (conn != null) {
+                            Rlog.i(LOG_TAG, "handoverCheck: handover failed.");
                             conn.onHandoverToWifiFailed();
                         }
+
+                        if (imsCall.isVideoCall()
+                                && conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) {
+                            registerForConnectivityChanges();
+                        }
                     }
                 }
                 break;
@@ -3698,8 +3795,66 @@
     }
 
     /**
-     * @return {@code true} if downgrading of a video call to audio is supported.
+     * Registers for changes to network connectivity.  Specifically requests the availability of new
+     * WIFI networks which an IMS video call could potentially hand over to.
      */
+    private void registerForConnectivityChanges() {
+        if (mIsMonitoringConnectivity || !mNotifyVtHandoverToWifiFail) {
+            return;
+        }
+        ConnectivityManager cm = (ConnectivityManager) mPhone.getContext()
+                .getSystemService(Context.CONNECTIVITY_SERVICE);
+        if (cm != null) {
+            Rlog.i(LOG_TAG, "registerForConnectivityChanges");
+            NetworkCapabilities capabilities = new NetworkCapabilities();
+            capabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+            NetworkRequest.Builder builder = new NetworkRequest.Builder();
+            builder.setCapabilities(capabilities);
+            cm.registerNetworkCallback(builder.build(), mNetworkCallback);
+            mIsMonitoringConnectivity = true;
+        }
+    }
+
+    /**
+     * Unregister for connectivity changes.  Will be called when a call disconnects or if the call
+     * ends up handing over to WIFI.
+     */
+    private void unregisterForConnectivityChanges() {
+        if (!mIsMonitoringConnectivity || !mNotifyVtHandoverToWifiFail) {
+            return;
+        }
+        ConnectivityManager cm = (ConnectivityManager) mPhone.getContext()
+                .getSystemService(Context.CONNECTIVITY_SERVICE);
+        if (cm != null) {
+            Rlog.i(LOG_TAG, "unregisterForConnectivityChanges");
+            cm.unregisterNetworkCallback(mNetworkCallback);
+            mIsMonitoringConnectivity = false;
+        }
+    }
+
+    /**
+     * If the foreground call is a video call, schedule a handover check if one is not already
+     * scheduled.  This method is intended ONLY for use when scheduling to watch for mid-call
+     * handovers.
+     */
+    private void scheduleHandoverCheck() {
+        ImsCall fgCall = mForegroundCall.getImsCall();
+        ImsPhoneConnection conn = mForegroundCall.getFirstConnection();
+        if (!mNotifyVtHandoverToWifiFail || fgCall == null || !fgCall.isVideoCall() || conn == null
+                || conn.getDisconnectCause() != DisconnectCause.NOT_DISCONNECTED) {
+            return;
+        }
+
+        if (!hasMessages(EVENT_CHECK_FOR_WIFI_HANDOVER)) {
+            Rlog.i(LOG_TAG, "scheduleHandoverCheck: schedule");
+            sendMessageDelayed(obtainMessage(EVENT_CHECK_FOR_WIFI_HANDOVER, fgCall),
+                    HANDOVER_TO_WIFI_TIMEOUT_MS);
+        }
+    }
+
+    /**
+     * @return {@code true} if downgrading of a video call to audio is supported.
+         */
     public boolean isCarrierDowngradeOfVtCallSupported() {
         return mSupportDowngradeVtToAudio;
     }
@@ -3719,6 +3874,7 @@
         }
         sb.append(capabilities);
         mMmTelCapabilities = new MmTelFeature.MmTelCapabilities(capabilities);
+
         boolean isVideoEnabled = isVideoCallEnabled();
         boolean isVideoEnabledStatechanged = tmpIsVideoCallEnabled != isVideoEnabled;
         if (DBG) {
diff --git a/src/java/com/android/internal/telephony/metrics/TelephonyEventBuilder.java b/src/java/com/android/internal/telephony/metrics/TelephonyEventBuilder.java
index d28c035..e06ead2 100644
--- a/src/java/com/android/internal/telephony/metrics/TelephonyEventBuilder.java
+++ b/src/java/com/android/internal/telephony/metrics/TelephonyEventBuilder.java
@@ -21,6 +21,7 @@
 import static com.android.internal.telephony.nano.TelephonyProto.RilDataCall;
 import static com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent;
 import static com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.CarrierIdMatching;
+import static com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.CarrierKeyChange;
 import static com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.ModemRestart;
 import static com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilDeactivateDataCall;
 import static com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCall;
@@ -126,4 +127,10 @@
         mEvent.carrierIdMatching = carrierIdMatching;
         return this;
     }
+
+    public TelephonyEventBuilder setCarrierKeyChange(CarrierKeyChange carrierKeyChange) {
+        mEvent.type = TelephonyEvent.Type.CARRIER_KEY_CHANGED;
+        mEvent.carrierKeyChange = carrierKeyChange;
+        return this;
+    }
 }
diff --git a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
index 8b9d580..a436d25 100644
--- a/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
+++ b/src/java/com/android/internal/telephony/metrics/TelephonyMetrics.java
@@ -44,14 +44,14 @@
 import android.telephony.TelephonyManager;
 import android.telephony.data.DataCallResponse;
 import android.telephony.data.DataService;
+import android.telephony.ims.ImsCallSession;
+import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.feature.MmTelFeature;
 import android.telephony.ims.stub.ImsRegistrationImplBase;
 import android.text.TextUtils;
 import android.util.Base64;
 import android.util.SparseArray;
 
-import android.telephony.ims.ImsReasonInfo;
-import android.telephony.ims.ImsCallSession;
 import com.android.internal.telephony.GsmCdmaConnection;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.RIL;
@@ -62,6 +62,7 @@
 import com.android.internal.telephony.nano.TelephonyProto;
 import com.android.internal.telephony.nano.TelephonyProto.ImsCapabilities;
 import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState;
+import com.android.internal.telephony.nano.TelephonyProto.ModemPowerStats;
 import com.android.internal.telephony.nano.TelephonyProto.RilDataCall;
 import com.android.internal.telephony.nano.TelephonyProto.SmsSession;
 import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession;
@@ -71,6 +72,7 @@
 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent;
 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.CarrierIdMatching;
 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.CarrierIdMatchingResult;
+import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.CarrierKeyChange;
 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.ModemRestart;
 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilDeactivateDataCall;
 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilDeactivateDataCall.DeactivateReason;
@@ -78,7 +80,6 @@
 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse;
 import com.android.internal.telephony.nano.TelephonyProto.TelephonyEvent.RilSetupDataCallResponse.RilDataCallFailCause;
 import com.android.internal.telephony.nano.TelephonyProto.TelephonyLog;
-import com.android.internal.telephony.nano.TelephonyProto.ModemPowerStats;
 import com.android.internal.telephony.nano.TelephonyProto.TelephonyServiceState;
 import com.android.internal.telephony.nano.TelephonyProto.TelephonySettings;
 import com.android.internal.telephony.nano.TelephonyProto.TimeInterval;
@@ -534,7 +535,6 @@
         log.endTime = new TelephonyProto.Time();
         log.endTime.systemTimestampMillis = System.currentTimeMillis();
         log.endTime.elapsedTimestampMillis = SystemClock.elapsedRealtime();
-
         return log;
     }
 
@@ -550,6 +550,23 @@
     }
 
     /**
+     * Write the Carrier Key change event
+     *
+     * @param phoneId Phone id
+     * @param keyType type of key
+     * @param isDownloadSuccessful true if the key was successfully downloaded
+     */
+    public void writeCarrierKeyEvent(int phoneId, int keyType,  boolean isDownloadSuccessful) {
+        final CarrierKeyChange carrierKeyChange = new CarrierKeyChange();
+        carrierKeyChange.keyType = keyType;
+        carrierKeyChange.isDownloadSuccessful = isDownloadSuccessful;
+        TelephonyEvent event = new TelephonyEventBuilder(phoneId).setCarrierKeyChange(
+                carrierKeyChange).build();
+        addTelephonyEvent(event);
+    }
+
+
+    /**
      * Get the time interval with reduced prevision
      *
      * @param previousTimestamp Previous timestamp in milliseconds
@@ -1854,4 +1871,4 @@
     public void writeOnImsCallResumeFailed(int phoneId, ImsCallSession session,
                                            ImsReasonInfo reasonInfo) {}
     public void writeOnRilTimeoutResponse(int phoneId, int rilSerial, int rilRequest) {}
-}
\ No newline at end of file
+}
diff --git a/src/java/com/android/internal/telephony/uicc/CarrierAppInstallReceiver.java b/src/java/com/android/internal/telephony/uicc/CarrierAppInstallReceiver.java
new file mode 100644
index 0000000..22e4e2c
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/CarrierAppInstallReceiver.java
@@ -0,0 +1,47 @@
+/*
+ * 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.uicc;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.text.TextUtils;
+import android.util.Log;
+
+/**
+ * Hides the carrier app install notification if the correct packages are installed
+ */
+public class CarrierAppInstallReceiver extends BroadcastReceiver {
+    private static final String LOG_TAG = "CarrierAppInstall";
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())) {
+            Log.d(LOG_TAG, "Received package install intent");
+            String intentPackageName = intent.getData().getSchemeSpecificPart();
+            if (TextUtils.isEmpty(intentPackageName)) {
+                Log.w(LOG_TAG, "Package is empty, ignoring");
+                return;
+            }
+
+            InstallCarrierAppUtils.hideNotification(context, intentPackageName);
+
+            if (!InstallCarrierAppUtils.isPackageInstallNotificationActive(context)) {
+                InstallCarrierAppUtils.unregisterPackageInstallReceiver(context);
+            }
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/IccRecords.java b/src/java/com/android/internal/telephony/uicc/IccRecords.java
index 6a630d6..97d497c 100644
--- a/src/java/com/android/internal/telephony/uicc/IccRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IccRecords.java
@@ -62,6 +62,7 @@
     protected RegistrantList mNewSmsRegistrants = new RegistrantList();
     protected RegistrantList mNetworkSelectionModeAutomaticRegistrants = new RegistrantList();
     protected RegistrantList mSpnUpdatedRegistrants = new RegistrantList();
+    protected RegistrantList mRecordsOverrideRegistrants = new RegistrantList();
 
     protected int mRecordsToLoad;  // number of pending load requests
 
@@ -218,7 +219,7 @@
         mCarrierTestOverride.override(mccmnc, imsi, iccid, gid1, gid2, pnn, spn);
         mTelephonyManager.setSimOperatorNameForPhone(mParentApp.getPhoneId(), spn);
         mTelephonyManager.setSimOperatorNumericForPhone(mParentApp.getPhoneId(), mccmnc);
-        mRecordsLoadedRegistrants.notifyRegistrants();
+        mRecordsOverrideRegistrants.notifyRegistrants();
     }
 
     /**
@@ -308,10 +309,28 @@
             r.notifyRegistrant(new AsyncResult(null, null, null));
         }
     }
+
     public void unregisterForRecordsLoaded(Handler h) {
         mRecordsLoadedRegistrants.remove(h);
     }
 
+    public void unregisterForRecordsOverride(Handler h) {
+        mRecordsOverrideRegistrants.remove(h);
+    }
+
+    public void registerForRecordsOverride(Handler h, int what, Object obj) {
+        if (mDestroyed.get()) {
+            return;
+        }
+
+        Registrant r = new Registrant(h, what, obj);
+        mRecordsOverrideRegistrants.add(r);
+
+        if (getRecordsLoaded()) {
+            r.notifyRegistrant(new AsyncResult(null, null, null));
+        }
+    }
+
     /**
      * Register to be notified when records are loaded for a PIN or PUK locked SIM
      */
diff --git a/src/java/com/android/internal/telephony/uicc/InstallCarrierAppTrampolineActivity.java b/src/java/com/android/internal/telephony/uicc/InstallCarrierAppTrampolineActivity.java
new file mode 100644
index 0000000..2c29266
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/InstallCarrierAppTrampolineActivity.java
@@ -0,0 +1,120 @@
+/*
+ * 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.uicc;
+
+import android.app.Activity;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Trampoline activity used to start the full screen dialog that is shown when a SIM is inserted
+ * and requires a carrier app download
+ */
+public class InstallCarrierAppTrampolineActivity extends Activity {
+    private static final String LOG_TAG = "CarrierAppInstall";
+    private static final int INSTALL_CARRIER_APP_DIALOG_REQUEST = 1;
+
+    // TODO(b/73648962): Move DOWNLOAD_RESULT and CARRIER_NAME to a shared location
+    /**
+     * This must remain in sync with
+     * {@link com.android.simappdialog.InstallCarrierAppActivity#DOWNLOAD_RESULT}
+     */
+    private static final int DOWNLOAD_RESULT = 2;
+
+    /**
+     * This must remain in sync with
+     * {@link com.android.simappdialog.InstallCarrierAppActivity#BUNDLE_KEY_CARRIER_NAME}
+     */
+    private static final String CARRIER_NAME = "carrier_name";
+
+    /** Bundle key for the name of the package to be downloaded */
+    private static final String BUNDLE_KEY_PACKAGE_NAME = "package_name";
+
+    /** Returns intent used to start this activity */
+    public static Intent get(Context context, String packageName) {
+        Intent intent = new Intent(context, InstallCarrierAppTrampolineActivity.class);
+        intent.putExtra(BUNDLE_KEY_PACKAGE_NAME, packageName);
+        return intent;
+    }
+
+    private String mPackageName;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Intent intent = getIntent();
+        if (intent != null) {
+            mPackageName = intent.getStringExtra(BUNDLE_KEY_PACKAGE_NAME);
+        }
+
+        // If this is the first activity creation, show notification after delay regardless of
+        // result code, but only if the app is not installed.
+        if (savedInstanceState == null) {
+            long sleepTimeMillis = Settings.Global.getLong(getContentResolver(),
+                    Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS,
+                    TimeUnit.HOURS.toMillis(24));
+            Log.d(LOG_TAG, "Sleeping carrier app install notification for : " + sleepTimeMillis
+                    + " millis");
+            InstallCarrierAppUtils.showNotificationIfNotInstalledDelayed(
+                    this,
+                    mPackageName,
+                    sleepTimeMillis);
+        }
+
+        // Display dialog activity if available
+        Intent showDialogIntent = new Intent();
+        ComponentName dialogComponentName = ComponentName.unflattenFromString(
+                Resources.getSystem().getString(
+                        com.android.internal.R.string.config_carrierAppInstallDialogComponent));
+        showDialogIntent.setComponent(dialogComponentName);
+        String appName = InstallCarrierAppUtils.getAppNameFromPackageName(this, mPackageName);
+        if (!TextUtils.isEmpty(appName)) {
+            showDialogIntent.putExtra(CARRIER_NAME, appName);
+        }
+
+        if (showDialogIntent.resolveActivity(getPackageManager()) == null) {
+            Log.d(LOG_TAG, "Could not resolve activity for installing the carrier app");
+            finishNoAnimation();
+        } else {
+            startActivityForResult(showDialogIntent, INSTALL_CARRIER_APP_DIALOG_REQUEST);
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        if (requestCode == INSTALL_CARRIER_APP_DIALOG_REQUEST) {
+            if (resultCode == DOWNLOAD_RESULT) {
+                startActivity(InstallCarrierAppUtils.getPlayStoreIntent(mPackageName));
+            }
+            finishNoAnimation();
+        }
+    }
+
+    private void finishNoAnimation() {
+        finish();
+        overridePendingTransition(0, 0);
+    }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/InstallCarrierAppUtils.java b/src/java/com/android/internal/telephony/uicc/InstallCarrierAppUtils.java
new file mode 100644
index 0000000..325b3c6
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/InstallCarrierAppUtils.java
@@ -0,0 +1,208 @@
+/*
+ * 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.uicc;
+
+import static android.content.Context.ALARM_SERVICE;
+
+import android.app.AlarmManager;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.service.notification.StatusBarNotification;
+import android.text.TextUtils;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.util.NotificationChannelController;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Utility methods for installing the carrier app when a SIM is insterted without the carrier app
+ * for that SIM installed.
+ */
+@VisibleForTesting
+public class InstallCarrierAppUtils {
+    // TODO(b/72714040) centralize notification IDs
+    private static final int ACTIVATE_CELL_SERVICE_NOTIFICATION_ID = 12;
+    private static CarrierAppInstallReceiver sCarrierAppInstallReceiver = null;
+
+    static void showNotification(Context context, String pkgName) {
+        Resources res = Resources.getSystem();
+        String title = res.getString(
+                com.android.internal.R.string.install_carrier_app_notification_title);
+        String appName = getAppNameFromPackageName(context, pkgName);
+        String message;
+        if (TextUtils.isEmpty(appName)) {
+            message = res.getString(R.string.install_carrier_app_notification_text);
+        } else {
+            message = res.getString(R.string.install_carrier_app_notification_text_app_name,
+                    appName);
+        }
+        String downloadButtonText = res.getString(R.string.install_carrier_app_notification_button);
+
+        boolean persistent = Settings.Global.getInt(
+                context.getContentResolver(),
+                Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT, 1) == 1;
+
+        PendingIntent goToStore = PendingIntent.getActivity(context, 0,
+                getPlayStoreIntent(pkgName), PendingIntent.FLAG_UPDATE_CURRENT);
+
+        Notification.Action goToStoreAction =
+                new Notification.Action.Builder(null, downloadButtonText, goToStore).build();
+
+        Notification notification = new Notification.Builder(context,
+                NotificationChannelController.CHANNEL_ID_SIM)
+                .setContentTitle(title)
+                .setContentText(message)
+                .setSmallIcon(R.drawable.ic_signal_cellular_alt_24px)
+                .addAction(goToStoreAction)
+                .setOngoing(persistent)
+                .setVisibility(Notification.VISIBILITY_SECRET) // Should not appear on lock screen
+                .build();
+
+        getNotificationManager(context).notify(
+                pkgName,
+                ACTIVATE_CELL_SERVICE_NOTIFICATION_ID,
+                notification);
+    }
+
+    static void hideAllNotifications(Context context) {
+        NotificationManager notificationManager = getNotificationManager(context);
+        StatusBarNotification[] activeNotifications = notificationManager.getActiveNotifications();
+
+        if (activeNotifications == null) {
+            return;
+        }
+
+        for (StatusBarNotification notification : activeNotifications) {
+            if (notification.getId() == ACTIVATE_CELL_SERVICE_NOTIFICATION_ID) {
+                notificationManager.cancel(notification.getTag(), notification.getId());
+            }
+        }
+    }
+
+    static void hideNotification(Context context, String pkgName) {
+        getNotificationManager(context).cancel(pkgName, ACTIVATE_CELL_SERVICE_NOTIFICATION_ID);
+    }
+
+    static Intent getPlayStoreIntent(String pkgName) {
+        // Open play store to download package
+        Intent storeIntent = new Intent(Intent.ACTION_VIEW);
+        storeIntent.setData(Uri.parse("market://details?id=" + pkgName));
+        storeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        return storeIntent;
+    }
+
+    static void showNotificationIfNotInstalledDelayed(Context context,
+            String pkgName, long delayMillis) {
+        AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
+        PendingIntent pendingIntent = PendingIntent.getBroadcast(
+                context,
+                0,
+                ShowInstallAppNotificationReceiver.get(context, pkgName),
+                0);
+        alarmManager.set(AlarmManager.ELAPSED_REALTIME,
+                SystemClock.elapsedRealtime() + delayMillis,
+                pendingIntent);
+
+    }
+
+    static void registerPackageInstallReceiver(Context context) {
+        if (sCarrierAppInstallReceiver == null) {
+            sCarrierAppInstallReceiver = new CarrierAppInstallReceiver();
+            context = context.getApplicationContext();
+            IntentFilter intentFilter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+            intentFilter.addDataScheme("package");
+            context.registerReceiver(sCarrierAppInstallReceiver, intentFilter);
+        }
+    }
+
+    static void unregisterPackageInstallReceiver(Context context) {
+        if (sCarrierAppInstallReceiver == null) {
+            return;
+        }
+        context = context.getApplicationContext();
+        context.unregisterReceiver(sCarrierAppInstallReceiver);
+        sCarrierAppInstallReceiver = null;
+    }
+
+    static boolean isPackageInstallNotificationActive(Context context) {
+        StatusBarNotification[] activeNotifications =
+                getNotificationManager(context).getActiveNotifications();
+
+        for (StatusBarNotification notification : activeNotifications) {
+            if (notification.getId() == ACTIVATE_CELL_SERVICE_NOTIFICATION_ID) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    static String getAppNameFromPackageName(Context context, String packageName) {
+        String whitelistSetting = Settings.Global.getString(
+                context.getContentResolver(),
+                Settings.Global.CARRIER_APP_NAMES);
+        return getAppNameFromPackageName(packageName, whitelistSetting);
+    }
+
+    /**
+     * @param packageName the name of the package.  Will be used as a key into the map
+     * @param mapString map of package name to application name in the format:
+     *                  packageName1:appName1;packageName2:appName2;
+     * @return the name of the application for the package name provided.
+     */
+    @VisibleForTesting
+    public static String getAppNameFromPackageName(String packageName, String mapString) {
+        packageName = packageName.toLowerCase();
+        final String pairDelim = "\\s*;\\s*";
+        final String keyValueDelim = "\\s*:\\s*";
+
+        if (TextUtils.isEmpty(mapString)) {
+            return null;
+        }
+
+        List<String> keyValuePairList = Arrays.asList(mapString.split(pairDelim));
+
+        if (keyValuePairList.isEmpty()) {
+            return null;
+        }
+
+        for (String keyValueString: keyValuePairList) {
+            String[] keyValue = keyValueString.split(keyValueDelim);
+
+            if (keyValue.length == 2) {
+                if (keyValue[0].equals(packageName)) {
+                    return keyValue[1];
+                }
+            }
+        }
+        return null;
+    }
+    private static NotificationManager getNotificationManager(Context context) {
+        return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+    }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/ShowInstallAppNotificationReceiver.java b/src/java/com/android/internal/telephony/uicc/ShowInstallAppNotificationReceiver.java
new file mode 100644
index 0000000..6c89448
--- /dev/null
+++ b/src/java/com/android/internal/telephony/uicc/ShowInstallAppNotificationReceiver.java
@@ -0,0 +1,46 @@
+/*
+ * 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.uicc;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * Receiver used to show a notification that prompts the user to install the package (contained in
+ * string extras) from the play store
+ */
+public class ShowInstallAppNotificationReceiver extends BroadcastReceiver {
+    private static final String EXTRA_PACKAGE_NAME = "package_name";
+
+    /** Returns intent used to send a broadcast to this receiver */
+    public static Intent get(Context context, String pkgName) {
+        Intent intent = new Intent(context, ShowInstallAppNotificationReceiver.class);
+        intent.putExtra(EXTRA_PACKAGE_NAME, pkgName);
+        return intent;
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        String pkgName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
+
+        if (!UiccProfile.isPackageInstalled(context, pkgName)) {
+            InstallCarrierAppUtils.showNotification(context, pkgName);
+            InstallCarrierAppUtils.registerPackageInstallReceiver(context);
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java b/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
index 6620d83..a45c247 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
@@ -36,6 +36,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -250,6 +251,16 @@
     }
 
     /**
+     * Returns list of access rules.
+     */
+    public List<UiccAccessRule> getAccessRules() {
+        if (mAccessRules == null) {
+            return Collections.emptyList();
+        }
+        return Collections.unmodifiableList(mAccessRules);
+    }
+
+    /**
      * Returns the status of the carrier privileges for the input certificate and package name.
      *
      * @param signature The signature of the certificate.
diff --git a/src/java/com/android/internal/telephony/uicc/UiccProfile.java b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
index f7aeaa1..a0eb8d9 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccProfile.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccProfile.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -16,18 +16,16 @@
 
 package com.android.internal.telephony.uicc;
 
-import android.app.AlertDialog;
 import android.app.usage.UsageStatsManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.Signature;
-import android.content.res.Resources;
+import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.AsyncResult;
 import android.os.Binder;
@@ -44,10 +42,11 @@
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.UiccAccessRule;
 import android.text.TextUtils;
-import android.view.WindowManager;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 
-import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.IccCard;
@@ -66,8 +65,10 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.Arrays;
-import java.util.HashSet;
+import java.util.Collections;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * This class represents the carrier profiles in the {@link UiccCard}. Each profile contains
@@ -134,6 +135,17 @@
     private IccRecords mIccRecords = null;
     private IccCardConstants.State mExternalState = IccCardConstants.State.UNKNOWN;
 
+    private final ContentObserver mProvisionCompleteContentObserver =
+            new ContentObserver(new Handler()) {
+                @Override
+                public void onChange(boolean selfChange) {
+                    mContext.getContentResolver().unregisterContentObserver(this);
+                    for (String pkgName : getUninstalledCarrierPackages()) {
+                        InstallCarrierAppUtils.showNotification(mContext, pkgName);
+                        InstallCarrierAppUtils.registerPackageInstallReceiver(mContext);
+                    }
+                }
+            };
 
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
@@ -186,7 +198,7 @@
 
                 case EVENT_CARRIER_PRIVILEGES_LOADED:
                     if (VDBG) log("handleMessage: EVENT_CARRIER_PRIVILEGES_LOADED");
-                    onCarrierPriviligesLoadedMessage();
+                    onCarrierPrivilegesLoadedMessage();
                     updateExternalState();
                     break;
 
@@ -256,6 +268,9 @@
             unregisterAllAppEvents();
             unregisterCurrAppEvents();
 
+            InstallCarrierAppUtils.hideAllNotifications(mContext);
+            InstallCarrierAppUtils.unregisterPackageInstallReceiver(mContext);
+
             mCi.unregisterForOffOrNotAvailable(mHandler);
             mContext.unregisterReceiver(mReceiver);
 
@@ -1085,8 +1100,8 @@
             mCarrierPrivilegeRegistrants.remove(h);
         }
     }
-    
-     /**
+
+    /**
      * Unregister for notifications when operator brand name is overriden.
      *
      * @param h Handler to be removed from the registrant list.
@@ -1097,8 +1112,8 @@
         }
     }
 
-    private boolean isPackageInstalled(String pkgName) {
-        PackageManager pm = mContext.getPackageManager();
+    static boolean isPackageInstalled(Context context, String pkgName) {
+        PackageManager pm = context.getPackageManager();
         try {
             pm.getPackageInfo(pkgName, PackageManager.GET_ACTIVITIES);
             if (DBG) log(pkgName + " is installed.");
@@ -1109,72 +1124,97 @@
         }
     }
 
-    private class ClickListener implements DialogInterface.OnClickListener {
-        String mPkgName;
-        ClickListener(String pkgName) {
-            this.mPkgName = pkgName;
-        }
-        @Override
-        public void onClick(DialogInterface dialog, int which) {
-            synchronized (mLock) {
-                if (which == DialogInterface.BUTTON_POSITIVE) {
-                    Intent market = new Intent(Intent.ACTION_VIEW);
-                    market.setData(Uri.parse("market://details?id=" + mPkgName));
-                    market.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                    mContext.startActivity(market);
-                } else if (which == DialogInterface.BUTTON_NEGATIVE) {
-                    if (DBG) log("Not now clicked for carrier app dialog.");
-                }
-            }
-        }
-    }
-
     private void promptInstallCarrierApp(String pkgName) {
-        DialogInterface.OnClickListener listener = new ClickListener(pkgName);
-
-        Resources r = Resources.getSystem();
-        String message = r.getString(R.string.carrier_app_dialog_message);
-        String buttonTxt = r.getString(R.string.carrier_app_dialog_button);
-        String notNowTxt = r.getString(R.string.carrier_app_dialog_not_now);
-
-        AlertDialog dialog = new AlertDialog.Builder(mContext)
-                .setMessage(message)
-                .setNegativeButton(notNowTxt, listener)
-                .setPositiveButton(buttonTxt, listener)
-                .create();
-        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
-        dialog.show();
+        Intent showDialogIntent = InstallCarrierAppTrampolineActivity.get(mContext, pkgName);
+        mContext.startActivity(showDialogIntent);
     }
 
-    private void onCarrierPriviligesLoadedMessage() {
+    private void onCarrierPrivilegesLoadedMessage() {
         UsageStatsManager usm = (UsageStatsManager) mContext.getSystemService(
                 Context.USAGE_STATS_SERVICE);
         if (usm != null) {
             usm.onCarrierPrivilegedAppsChanged();
         }
+
+        InstallCarrierAppUtils.hideAllNotifications(mContext);
+        InstallCarrierAppUtils.unregisterPackageInstallReceiver(mContext);
+
         synchronized (mLock) {
             mCarrierPrivilegeRegistrants.notifyRegistrants();
-            String whitelistSetting = Settings.Global.getString(mContext.getContentResolver(),
-                    Settings.Global.CARRIER_APP_WHITELIST);
-            if (TextUtils.isEmpty(whitelistSetting)) {
-                return;
-            }
-            HashSet<String> carrierAppSet = new HashSet<String>(
-                    Arrays.asList(whitelistSetting.split("\\s*;\\s*")));
-            if (carrierAppSet.isEmpty()) {
-                return;
-            }
-
-            List<String> pkgNames = mCarrierPrivilegeRules.getPackageNames();
-            for (String pkgName : pkgNames) {
-                if (!TextUtils.isEmpty(pkgName) && carrierAppSet.contains(pkgName)
-                        && !isPackageInstalled(pkgName)) {
+            boolean isProvisioned = Settings.Global.getInt(
+                    mContext.getContentResolver(),
+                    Settings.Global.DEVICE_PROVISIONED, 1) == 1;
+            // Only show dialog if the phone is through with Setup Wizard.  Otherwise, wait for
+            // completion and show a notification instead
+            if (isProvisioned) {
+                for (String pkgName : getUninstalledCarrierPackages()) {
                     promptInstallCarrierApp(pkgName);
                 }
+            } else {
+                final Uri uri = Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED);
+                mContext.getContentResolver().registerContentObserver(
+                        uri,
+                        false,
+                        mProvisionCompleteContentObserver);
             }
         }
     }
 
+    private Set<String> getUninstalledCarrierPackages() {
+        String whitelistSetting = Settings.Global.getString(
+                mContext.getContentResolver(),
+                Settings.Global.CARRIER_APP_WHITELIST);
+        if (TextUtils.isEmpty(whitelistSetting)) {
+            return Collections.emptySet();
+        }
+        Map<String, String> certPackageMap = parseToCertificateToPackageMap(whitelistSetting);
+        if (certPackageMap.isEmpty()) {
+            return Collections.emptySet();
+        }
+
+        Set<String> uninstalledCarrierPackages = new ArraySet<>();
+        List<UiccAccessRule> accessRules = mCarrierPrivilegeRules.getAccessRules();
+        for (UiccAccessRule accessRule : accessRules) {
+            String certHexString = accessRule.getCertificateHexString().toUpperCase();
+            String pkgName = certPackageMap.get(certHexString);
+            if (!TextUtils.isEmpty(pkgName) && !isPackageInstalled(mContext, pkgName)) {
+                uninstalledCarrierPackages.add(pkgName);
+            }
+        }
+        return uninstalledCarrierPackages;
+    }
+
+    /**
+     * Converts a string in the format: key1:value1;key2:value2... into a map where the keys are
+     * hex representations of app certificates - all upper case - and the values are package names
+     * @hide
+     */
+    @VisibleForTesting
+    public static Map<String, String> parseToCertificateToPackageMap(String whitelistSetting) {
+        final String pairDelim = "\\s*;\\s*";
+        final String keyValueDelim = "\\s*:\\s*";
+
+        List<String> keyValuePairList = Arrays.asList(whitelistSetting.split(pairDelim));
+
+        if (keyValuePairList.isEmpty()) {
+            return Collections.emptyMap();
+        }
+
+        Map<String, String> map = new ArrayMap<>(keyValuePairList.size());
+        for (String keyValueString: keyValuePairList) {
+            String[] keyValue = keyValueString.split(keyValueDelim);
+
+            if (keyValue.length == 2) {
+                map.put(keyValue[0].toUpperCase(), keyValue[1]);
+            } else {
+                loge("Incorrect length of key-value pair in carrier app whitelist map.  "
+                        + "Length should be exactly 2");
+            }
+        }
+
+        return map;
+    }
+
     /**
      * Check whether the specified type of application exists in the profile.
      *
@@ -1513,7 +1553,7 @@
         Rlog.d(LOG_TAG, msg);
     }
 
-    private void loge(String msg) {
+    private static void loge(String msg) {
         Rlog.e(LOG_TAG, msg);
     }
 
@@ -1522,6 +1562,15 @@
     }
 
     /**
+     * Reloads carrier privileges as if a change were just detected.  Useful to force a profile
+     * refresh without having to physically insert or remove a SIM card.
+     */
+    @VisibleForTesting
+    public void refresh() {
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARRIER_PRIVILEGES_LOADED));
+    }
+
+    /**
      * Dump
      */
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
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 103c75b..b8b2818 100644
--- a/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java
+++ b/src/java/com/android/internal/telephony/uicc/euicc/EuiccCard.java
@@ -88,7 +88,7 @@
     // Device capabilities.
     private static final String DEV_CAP_GSM = "gsm";
     private static final String DEV_CAP_UTRAN = "utran";
-    private static final String DEV_CAP_CDMA_1X = "cdma_1x";
+    private static final String DEV_CAP_CDMA_1X = "cdma1x";
     private static final String DEV_CAP_HRPD = "hrpd";
     private static final String DEV_CAP_EHRPD = "ehrpd";
     private static final String DEV_CAP_EUTRAN = "eutran";
@@ -162,7 +162,12 @@
 
             @Override
             public void onException(Throwable e) {
-                // Not notifying registrants if getting eid fails.
+                // Still notifying registrants even getting eid fails.
+                if (mEidReadyRegistrants != null) {
+                    mEidReadyRegistrants.notifyRegistrants(new AsyncResult(null, null, null));
+                }
+                mEid = "";
+                mCardId = "";
                 Rlog.e(LOG_TAG, "Failed loading eid", e);
             }
         };
@@ -183,6 +188,15 @@
                 (byte[] response) -> mSpecVersion, callback, handler);
     }
 
+    @Override
+    protected void updateCardId() {
+        if (TextUtils.isEmpty(mEid)) {
+            super.updateCardId();
+        } else {
+            mCardId = mEid;
+        }
+    }
+
     /**
      * Gets a list of user-visible profiles.
      *
@@ -234,7 +248,10 @@
         sendApdu(
                 newRequestProvider((RequestBuilder requestBuilder) ->
                         requestBuilder.addStoreData(Asn1Node.newBuilder(Tags.TAG_GET_PROFILES)
-                                .addChildAsBytes(Tags.TAG_ICCID, IccUtils.bcdToBytes(iccid))
+                                .addChild(Asn1Node.newBuilder(Tags.TAG_CTX_COMP_0)
+                                    .addChildAsBytes(
+                                        Tags.TAG_ICCID, IccUtils.bcdToBytes(padTrailingFs(iccid)))
+                                    .build())
                                 .addChildAsBytes(Tags.TAG_TAG_LIST, Tags.EUICC_PROFILE_TAGS)
                                 .build().toHex())),
                 (byte[] response) -> {
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduSender.java b/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduSender.java
index 05cc78c..7001f6d 100644
--- a/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduSender.java
+++ b/src/java/com/android/internal/telephony/uicc/euicc/apdu/ApduSender.java
@@ -198,10 +198,12 @@
             Handler handler) {
         ByteArrayOutputStream resultBuilder =
                 responseBuilder == null ? new ByteArrayOutputStream() : responseBuilder;
-        try {
-            resultBuilder.write(lastResponse.payload);
-        } catch (IOException e) {
-            // Should never reach here.
+        if (lastResponse.payload != null) {
+            try {
+                resultBuilder.write(lastResponse.payload);
+            } catch (IOException e) {
+                // Should never reach here.
+            }
         }
         if (lastResponse.sw1 != SW1_MORE_RESPONSE) {
             lastResponse.payload = resultBuilder.toByteArray();
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/apdu/RequestBuilder.java b/src/java/com/android/internal/telephony/uicc/euicc/apdu/RequestBuilder.java
index c2a63ee..7a7fa5e 100644
--- a/src/java/com/android/internal/telephony/uicc/euicc/apdu/RequestBuilder.java
+++ b/src/java/com/android/internal/telephony/uicc/euicc/apdu/RequestBuilder.java
@@ -81,13 +81,11 @@
         int totalSubCmds = totalLen == 0 ? 1 : (totalLen + mMaxApduDataLen - 1) / mMaxApduDataLen;
         for (int i = 1; i < totalSubCmds; ++i) {
             String data = cmdHex.substring(startPos, startPos + cmdLen);
-            addApdu(CLA_STORE_DATA, INS_STORE_DATA, P1_STORE_DATA_INTERM, i - 1, mMaxApduDataLen,
-                    data);
+            addApdu(CLA_STORE_DATA, INS_STORE_DATA, P1_STORE_DATA_INTERM, i - 1, data);
             startPos += cmdLen;
         }
         String data = cmdHex.substring(startPos);
-        addApdu(CLA_STORE_DATA, INS_STORE_DATA, P1_STORE_DATA_END, totalSubCmds - 1,
-                totalLen % mMaxApduDataLen, data);
+        addApdu(CLA_STORE_DATA, INS_STORE_DATA, P1_STORE_DATA_END, totalSubCmds - 1, data);
     }
 
     List<ApduCommand> getCommands() {
diff --git a/src/java/com/android/internal/telephony/uicc/euicc/async/AsyncResultCallback.java b/src/java/com/android/internal/telephony/uicc/euicc/async/AsyncResultCallback.java
index 6e93687..b28e7e7 100644
--- a/src/java/com/android/internal/telephony/uicc/euicc/async/AsyncResultCallback.java
+++ b/src/java/com/android/internal/telephony/uicc/euicc/async/AsyncResultCallback.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony.uicc.euicc.async;
 
+import android.telephony.Rlog;
+
 /**
  * Class to deliver the returned value from an asynchronous call. Either {@link #onResult(Result)}
  * or {@link #onException(Throwable)} will be called. You can create an anonymous subclass and
@@ -38,9 +40,13 @@
  */
 public abstract class AsyncResultCallback<Result> {
 
+    private static final String LOG_TAG = "AsyncResultCallback";
+
     /** This will be called when the result is returned. */
     public abstract void onResult(Result result);
 
     /** This will be called when any exception is thrown. */
-    public void onException(Throwable e) {}
+    public void onException(Throwable e) {
+        Rlog.e(LOG_TAG, "Error in onException", e);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/util/NotificationChannelController.java b/src/java/com/android/internal/telephony/util/NotificationChannelController.java
index 3f6da54..bf84c18 100644
--- a/src/java/com/android/internal/telephony/util/NotificationChannelController.java
+++ b/src/java/com/android/internal/telephony/util/NotificationChannelController.java
@@ -39,6 +39,7 @@
     public static final String CHANNEL_ID_ALERT = "alert";
     public static final String CHANNEL_ID_CALL_FORWARD = "callForward";
     public static final String CHANNEL_ID_MOBILE_DATA_STATUS = "mobileDataAlertNew";
+    public static final String CHANNEL_ID_SIM = "sim";
     public static final String CHANNEL_ID_SMS = "sms";
     public static final String CHANNEL_ID_VOICE_MAIL = "voiceMail";
     public static final String CHANNEL_ID_WFC = "wfc";
@@ -67,6 +68,14 @@
         // allow users to block notifications from system
         mobileDataStatusChannel.setBlockableSystem(true);
 
+        final NotificationChannel simChannel = new NotificationChannel(
+                CHANNEL_ID_SIM,
+                context.getText(R.string.notification_channel_sim),
+                NotificationManager.IMPORTANCE_LOW
+        );
+
+        simChannel.setSound(null, null);
+
         context.getSystemService(NotificationManager.class)
                 .createNotificationChannels(Arrays.asList(
                 new NotificationChannel(CHANNEL_ID_CALL_FORWARD,
@@ -79,7 +88,8 @@
                         context.getText(R.string.notification_channel_wfc),
                         NotificationManager.IMPORTANCE_LOW),
                 alertChannel,
-                mobileDataStatusChannel));
+                mobileDataStatusChannel,
+                simChannel));
         // only for update
         if (getChannel(CHANNEL_ID_VOICE_MAIL, context) != null) {
             migrateVoicemailNotificationSettings(context);
diff --git a/tests/telephonytests/Android.mk b/tests/telephonytests/Android.mk
index 6709d46..b9bf282 100644
--- a/tests/telephonytests/Android.mk
+++ b/tests/telephonytests/Android.mk
@@ -5,19 +5,23 @@
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
-#LOCAL_STATIC_JAVA_LIBRARIES := librilproto-java
+LOCAL_JAVA_LIBRARIES := \
+    android.test.runner \
+    ims-common \
+    bouncycastle \
+    android.test.base \
+    android.test.mock
 
-LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common ims-common services.core bouncycastle
 LOCAL_STATIC_JAVA_LIBRARIES := guava \
                                frameworks-base-testutils \
+                               services.core \
+                               telephony-common \
                                mockito-target-minus-junit4 \
                                android-support-test \
-                               platform-test-annotations \
-                               legacy-android-test
+                               platform-test-annotations
 
 LOCAL_PACKAGE_NAME := FrameworksTelephonyTests
 LOCAL_PRIVATE_PLATFORM_APIS := true
-
 LOCAL_COMPATIBILITY_SUITE := device-tests
 
 include $(BUILD_PACKAGE)
diff --git a/tests/telephonytests/AndroidTest.xml b/tests/telephonytests/AndroidTest.xml
index dadff8b..74b2799 100644
--- a/tests/telephonytests/AndroidTest.xml
+++ b/tests/telephonytests/AndroidTest.xml
@@ -23,5 +23,6 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.frameworks.telephonytests" />
         <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/tests/telephonytests/src/com/android/internal/telephony/AppSmsManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/AppSmsManagerTest.java
new file mode 100644
index 0000000..bae1632
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/AppSmsManagerTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.telephony;
+
+import static junit.framework.Assert.fail;
+
+import android.content.Intent;
+import android.provider.Telephony.Sms.Intents;
+import android.telephony.SmsMessage;
+
+import com.android.internal.telephony.uicc.IccUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class AppSmsManagerTest extends TelephonyTest {
+
+    /**
+     * The sms message information represent by the {@link #PDU}.
+     *
+     * SMSC#+31624000000
+     * Sender:+31641600986
+     * TimeStamp:26/08/02 19:37:41
+     * TP_PID:00
+     * TP_DCS:00
+     * TP_DCS-popis:Uncompressed Text
+     * class:0
+     * Alphabet:Default
+     * How are you?
+     * Length:12
+     */
+    private static final String PDU =
+            "07911326040000F0040B911346610089F60000208062917314080CC8F71D14969741F977FD07";
+
+    private AppSmsManager mAppSmsManagerUT;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp("AppSmsManagerTest");
+        mAppSmsManagerUT = new AppSmsManager(mContextFixture.getTestDouble());
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    @Test
+    public void testHandleSmsReceivedIntent() {
+        Intent intent = new Intent();
+        intent.setAction(Intents.SMS_DELIVER_ACTION);
+        intent.putExtra("pdus", new byte[][]{IccUtils.hexStringToBytes(PDU), null, null});
+        intent.putExtra("format", SmsMessage.FORMAT_3GPP);
+
+        try {
+            // Assumes the AppSmsManager can handle the null pdu.
+            mAppSmsManagerUT.handleSmsReceivedIntent(intent);
+        } catch (NullPointerException ex) {
+            fail("Test failed because of null pointer exception " + ex);
+        }
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierAppUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierAppUtilsTest.java
index 9974a6d..54a56df 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierAppUtilsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierAppUtilsTest.java
@@ -27,6 +27,7 @@
 import android.test.mock.MockContentResolver;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 
 import org.junit.Test;
 import org.mockito.Mock;
@@ -38,7 +39,11 @@
 
 public class CarrierAppUtilsTest extends InstrumentationTestCase {
     private static final String CARRIER_APP = "com.example.carrier";
-    private static final String[] CARRIER_APPS = new String[] { CARRIER_APP };
+    private static final ArraySet<String> CARRIER_APPS = new ArraySet<>();
+    static {
+        CARRIER_APPS.add(CARRIER_APP);
+    }
+
     private static final String ASSOCIATED_APP = "com.example.associated";
     private static final ArrayMap<String, List<String>> ASSOCIATED_APPS = new ArrayMap<>();
     static {
@@ -73,7 +78,7 @@
     @Test @SmallTest
     public void testDisableCarrierAppsUntilPrivileged_EmptyList() {
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
-                mTelephonyManager, mContentResolver, USER_ID, new String[0],
+                mTelephonyManager, mContentResolver, USER_ID, new ArraySet<>(),
                 ASSOCIATED_APPS);
         Mockito.verifyNoMoreInteractions(mPackageManager, mTelephonyManager);
     }
@@ -83,9 +88,11 @@
     public void testDisableCarrierAppsUntilPrivileged_MissingApp() throws Exception {
         Mockito.when(mPackageManager.getApplicationInfo("com.example.missing.app",
                 PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(null);
+        ArraySet<String> systemCarrierAppsDisabledUntilUsed = new ArraySet<>();
+        systemCarrierAppsDisabledUntilUsed.add("com.example.missing.app");
         CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
                 mTelephonyManager, mContentResolver, USER_ID,
-                new String[] { "com.example.missing.app" }, ASSOCIATED_APPS);
+                systemCarrierAppsDisabledUntilUsed, ASSOCIATED_APPS);
         Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
                 Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
                 Mockito.anyString());
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
index 59d8872..5de4871 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierServiceStateTrackerTest.java
@@ -52,7 +52,6 @@
 /**
  * Unit tests for {@link com.android.internal.telephony.CarrierServiceStateTracker}.
  */
-@SmallTest
 public class CarrierServiceStateTrackerTest extends TelephonyTest {
     public static final String LOG_TAG = "CSST";
     public static final int TEST_TIMEOUT = 5000;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
index 77eb7dc..60bc61f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/GsmCdmaPhoneTest.java
@@ -32,6 +32,7 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
@@ -61,6 +62,7 @@
 import com.android.internal.telephony.uicc.IccCardApplicationStatus;
 import com.android.internal.telephony.uicc.IccException;
 import com.android.internal.telephony.uicc.IccRecords;
+import com.android.internal.telephony.uicc.UiccController;
 import com.android.internal.telephony.uicc.UiccProfile;
 import com.android.internal.telephony.uicc.UiccSlot;
 
@@ -70,6 +72,7 @@
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 
 import java.util.List;
 
@@ -179,6 +182,57 @@
 
     @Test
     @SmallTest
+    public void testGetSubscriberIdForGsmPhone() {
+        final String subscriberId = "123456789";
+        IccRecords iccRecords = Mockito.mock(IccRecords.class);
+        doReturn(subscriberId).when(iccRecords).getIMSI();
+        doReturn(iccRecords).when(mUiccController)
+                .getIccRecords(anyInt() /* phoneId */, eq(UiccController.APP_FAM_3GPP));
+
+        // Ensure the phone type is GSM
+        GsmCdmaPhone spyPhone = spy(mPhoneUT);
+        doReturn(false).when(spyPhone).isPhoneTypeCdma();
+        doReturn(false).when(spyPhone).isPhoneTypeCdmaLte();
+        doReturn(true).when(spyPhone).isPhoneTypeGsm();
+
+        assertEquals(subscriberId, spyPhone.getSubscriberId());
+    }
+
+    @Test
+    @SmallTest
+    public void testGetSubscriberIdForCdmaLtePhone() {
+        final String subscriberId = "abcdefghijk";
+        IccRecords iccRecords = Mockito.mock(IccRecords.class);
+        doReturn(subscriberId).when(iccRecords).getIMSI();
+        doReturn(iccRecords).when(mUiccController)
+                .getIccRecords(anyInt() /* phoneId */, eq(UiccController.APP_FAM_3GPP));
+
+        // Ensure the phone type is CdmaLte
+        GsmCdmaPhone spyPhone = spy(mPhoneUT);
+        doReturn(false).when(spyPhone).isPhoneTypeCdma();
+        doReturn(true).when(spyPhone).isPhoneTypeCdmaLte();
+        doReturn(false).when(spyPhone).isPhoneTypeGsm();
+
+        assertEquals(subscriberId, spyPhone.getSubscriberId());
+    }
+
+    @Test
+    @SmallTest
+    public void testGetSubscriberIdForCdmaPhone() {
+        final String subscriberId = "987654321";
+        doReturn(subscriberId).when(mSST).getImsi();
+
+        // Ensure the phone type is GSM
+        GsmCdmaPhone spyPhone = spy(mPhoneUT);
+        doReturn(true).when(spyPhone).isPhoneTypeCdma();
+        doReturn(false).when(spyPhone).isPhoneTypeCdmaLte();
+        doReturn(false).when(spyPhone).isPhoneTypeGsm();
+
+        assertEquals(subscriberId, spyPhone.getSubscriberId());
+    }
+
+    @Test
+    @SmallTest
     public void testGetCellLocation() {
         // GSM
         CellLocation cellLocation = new GsmCellLocation();
@@ -368,6 +422,21 @@
                 putString(CarrierConfigManager.KEY_DEFAULT_VM_NUMBER_STRING, voiceMailNumber);
         assertEquals(voiceMailNumber, mPhoneUT.getVoiceMailNumber());
 
+        // voicemail number from config for roaming network
+        String voiceMailNumberForRoaming = "1234567892";
+        mContextFixture.getCarrierConfigBundle()
+                .putString(CarrierConfigManager.KEY_DEFAULT_VM_NUMBER_ROAMING_STRING,
+                        voiceMailNumberForRoaming);
+        //Verify voicemail number for home
+        doReturn(false).when(mSST.mSS).getRoaming();
+        assertEquals(voiceMailNumber, mPhoneUT.getVoiceMailNumber());
+        //Move to roaming condition, verify voicemail number for roaming
+        doReturn(true).when(mSST.mSS).getRoaming();
+        assertEquals(voiceMailNumberForRoaming, mPhoneUT.getVoiceMailNumber());
+        //Move to home condition, verify voicemail number for home
+        doReturn(false).when(mSST.mSS).getRoaming();
+        assertEquals(voiceMailNumber, mPhoneUT.getVoiceMailNumber());
+
         // voicemail number that is explicitly set
         voiceMailNumber = "1234567891";
         mPhoneUT.setVoiceMailNumber("alphaTag", voiceMailNumber, null);
@@ -400,6 +469,21 @@
                 putString(CarrierConfigManager.KEY_DEFAULT_VM_NUMBER_STRING, voiceMailNumber);
         assertEquals(voiceMailNumber, mPhoneUT.getVoiceMailNumber());
 
+        // voicemail number from config for roaming network
+        String voiceMailNumberForRoaming = "1234567892";
+        mContextFixture.getCarrierConfigBundle()
+                .putString(CarrierConfigManager.KEY_DEFAULT_VM_NUMBER_ROAMING_STRING,
+                        voiceMailNumberForRoaming);
+        //Verify voicemail number for home
+        doReturn(false).when(mSST.mSS).getRoaming();
+        assertEquals(voiceMailNumber, mPhoneUT.getVoiceMailNumber());
+        //Move to roaming condition, verify voicemail number for roaming
+        doReturn(true).when(mSST.mSS).getRoaming();
+        assertEquals(voiceMailNumberForRoaming, mPhoneUT.getVoiceMailNumber());
+        //Move to home condition, verify voicemail number for home
+        doReturn(false).when(mSST.mSS).getRoaming();
+        assertEquals(voiceMailNumber, mPhoneUT.getVoiceMailNumber());
+
         // voicemail number from sharedPreference
         mPhoneUT.setVoiceMailNumber("alphaTag", voiceMailNumber, null);
         ArgumentCaptor<Message> messageArgumentCaptor = ArgumentCaptor.forClass(Message.class);
diff --git a/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java
index bcc1730..02f78ab 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/LocaleTrackerTest.java
@@ -98,7 +98,7 @@
     public void testUpdateOperatorNumericSync() throws Exception {
         mLocaleTracker.updateOperatorNumericSync(US_MCC + FAKE_MNC);
         assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
-        verify(mWifiManager).setCountryCode(US_COUNTRY_CODE, false);
+        verify(mWifiManager).setCountryCode(US_COUNTRY_CODE);
     }
 
     @Test
@@ -107,7 +107,7 @@
         mLocaleTracker.updateOperatorNumericAsync(US_MCC + FAKE_MNC);
         waitForMs(100);
         assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
-        verify(mWifiManager).setCountryCode(US_COUNTRY_CODE, false);
+        verify(mWifiManager).setCountryCode(US_COUNTRY_CODE);
     }
 
     @Test
@@ -116,7 +116,7 @@
         mLocaleTracker.updateOperatorNumericAsync("");
         waitForHandlerAction(mLocaleTracker, 100);
         assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
-        verify(mWifiManager).setCountryCode(US_COUNTRY_CODE, false);
+        verify(mWifiManager).setCountryCode(US_COUNTRY_CODE);
     }
 
     @Test
@@ -126,7 +126,7 @@
         mLocaleTracker.updateOperatorNumericAsync("");
         waitForHandlerAction(mLocaleTracker, 100);
         assertEquals(COUNTRY_CODE_UNAVAILABLE, mLocaleTracker.getCurrentCountry());
-        verify(mWifiManager).setCountryCode(COUNTRY_CODE_UNAVAILABLE, false);
+        verify(mWifiManager).setCountryCode(COUNTRY_CODE_UNAVAILABLE);
     }
 
     @Test
@@ -134,18 +134,18 @@
     public void testTogglingAirplaneMode() throws Exception {
         mLocaleTracker.updateOperatorNumericSync(US_MCC + FAKE_MNC);
         assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
-        verify(mWifiManager).setCountryCode(US_COUNTRY_CODE, false);
+        verify(mWifiManager).setCountryCode(US_COUNTRY_CODE);
 
         doReturn(false).when(mSST).getDesiredPowerState();
         mLocaleTracker.updateOperatorNumericAsync("");
         waitForHandlerAction(mLocaleTracker, 100);
         assertEquals(COUNTRY_CODE_UNAVAILABLE, mLocaleTracker.getCurrentCountry());
-        verify(mWifiManager).setCountryCode(COUNTRY_CODE_UNAVAILABLE, false);
+        verify(mWifiManager).setCountryCode(COUNTRY_CODE_UNAVAILABLE);
 
         doReturn(true).when(mSST).getDesiredPowerState();
         mLocaleTracker.updateOperatorNumericSync(US_MCC + FAKE_MNC);
         assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
-        verify(mWifiManager, times(2)).setCountryCode(US_COUNTRY_CODE, false);
+        verify(mWifiManager, times(2)).setCountryCode(US_COUNTRY_CODE);
     }
 
     @Test
@@ -155,12 +155,12 @@
         mLocaleTracker.updateOperatorNumericAsync("");
         waitForHandlerAction(mLocaleTracker, 100);
         assertEquals(COUNTRY_CODE_UNAVAILABLE, mLocaleTracker.getCurrentCountry());
-        verify(mWifiManager).setCountryCode(COUNTRY_CODE_UNAVAILABLE, false);
+        verify(mWifiManager).setCountryCode(COUNTRY_CODE_UNAVAILABLE);
 
         doReturn(Arrays.asList(mCellInfo)).when(mPhone).getAllCellInfo(isNull());
         waitForHandlerActionDelayed(mLocaleTracker, 100, 2500);
         assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
-        verify(mWifiManager).setCountryCode(US_COUNTRY_CODE, false);
+        verify(mWifiManager).setCountryCode(US_COUNTRY_CODE);
     }
 
     @Test
@@ -170,7 +170,7 @@
         mLocaleTracker.updateOperatorNumericAsync("");
         waitForHandlerAction(mLocaleTracker, 100);
         assertEquals(COUNTRY_CODE_UNAVAILABLE, mLocaleTracker.getCurrentCountry());
-        verify(mWifiManager).setCountryCode(COUNTRY_CODE_UNAVAILABLE, false);
+        verify(mWifiManager).setCountryCode(COUNTRY_CODE_UNAVAILABLE);
 
         doReturn(Arrays.asList(mCellInfo)).when(mPhone).getAllCellInfo(isNull());
         ServiceState ss = new ServiceState();
@@ -179,6 +179,6 @@
         mLocaleTracker.sendMessage(mLocaleTracker.obtainMessage(3, ar));
         waitForHandlerAction(mLocaleTracker, 100);
         assertEquals(US_COUNTRY_CODE, mLocaleTracker.getCurrentCountry());
-        verify(mWifiManager).setCountryCode(US_COUNTRY_CODE, false);
+        verify(mWifiManager).setCountryCode(US_COUNTRY_CODE);
     }
 }
diff --git a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
index 10dd6f2..cfc0ca8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/RILTest.java
@@ -804,6 +804,7 @@
         }
     }
 
+    @FlakyTest
     @Test
     public void testIccOpenLogicalChannel() throws Exception {
         String aid = "aid";
@@ -1517,6 +1518,30 @@
         assertEquals(expected, cellInfoCdma);
     }
 
+    @Test
+    public void testGetWorksourceClientId() {
+        RILRequest request = RILRequest.obtain(0, null, null);
+        assertEquals(null, request.getWorkSourceClientId());
+
+        request = RILRequest.obtain(0, null, new WorkSource());
+        assertEquals(null, request.getWorkSourceClientId());
+
+        WorkSource ws = new WorkSource();
+        ws.add(100);
+        request = RILRequest.obtain(0, null, ws);
+        assertEquals("100:null", request.getWorkSourceClientId());
+
+        ws = new WorkSource();
+        ws.add(100, "foo");
+        request = RILRequest.obtain(0, null, ws);
+        assertEquals("100:foo", request.getWorkSourceClientId());
+
+        ws = new WorkSource();
+        ws.createWorkChain().addNode(100, "foo").addNode(200, "bar");
+        request = RILRequest.obtain(0, null, ws);
+        assertEquals("100:foo", request.getWorkSourceClientId());
+    }
+
     private ArrayList<CellInfo> getCellInfoListForLTE(
             String mcc, String mnc, String alphaLong, String alphaShort) {
         android.hardware.radio.V1_2.CellInfoLte lte = new android.hardware.radio.V1_2.CellInfoLte();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
index 33782b2..f3b3197 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SubscriptionInfoUpdaterTest.java
@@ -331,7 +331,6 @@
 
         doReturn(Arrays.asList(mSubInfo)).when(mSubscriptionController)
                 .getSubInfoUsingSlotIndexPrivileged(eq(FAKE_SUB_ID_1), anyBoolean());
-
         mUpdater.updateInternalIccState(
                 IccCardConstants.INTENT_VALUE_ICC_LOCKED, "TESTING", FAKE_SUB_ID_1);
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
index ce5eaeb..dab06d8 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DataConnectionTest.java
@@ -71,6 +71,7 @@
 import com.android.internal.telephony.dataconnection.DataConnection.SetupResult;
 import com.android.internal.util.IState;
 import com.android.internal.util.StateMachine;
+import com.android.server.pm.PackageManagerService;
 
 import org.junit.After;
 import org.junit.Before;
@@ -94,6 +95,8 @@
     ApnContext mApnContext;
     @Mock
     DcFailBringUp mDcFailBringUp;
+    @Mock
+    PackageManagerService mMockPackageManagerInternal;
 
     private DataConnection mDc;
     private DataConnectionTestHandler mDataConnectionTestHandler;
@@ -191,7 +194,7 @@
     public void setUp() throws Exception {
         logd("+Setup!");
         super.setUp(getClass().getSimpleName());
-
+        mServiceManagerMockedServices.put("package", mMockPackageManagerInternal);
         doReturn("fake.action_detached").when(mPhone).getActionDetached();
         replaceInstance(ConnectionParams.class, "mApnContext", mCp, mApnContext);
         replaceInstance(ConnectionParams.class, "mRilRat", mCp,
diff --git a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
index 451571e..a13c951 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/dataconnection/DcTrackerTest.java
@@ -80,6 +80,7 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.TelephonyTest;
+import com.android.server.pm.PackageManagerService;
 
 import org.junit.After;
 import org.junit.Before;
@@ -142,6 +143,8 @@
     ApnSetting mApnSetting;
     @Mock
     DcAsyncChannel mDcac;
+    @Mock
+    PackageManagerService mMockPackageManagerInternal;
 
     private DcTracker mDct;
     private DcTrackerTestHandler mDcTrackerTestHandler;
@@ -461,6 +464,7 @@
         doReturn(1).when(mIsub).getDefaultDataSubId();
         doReturn(mIsub).when(mBinder).queryLocalInterface(anyString());
         mServiceManagerMockedServices.put("isub", mBinder);
+        mServiceManagerMockedServices.put("package", mMockPackageManagerInternal);
 
         mContextFixture.putStringArrayResource(
                 com.android.internal.R.array.config_cell_retries_per_error_code,
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 4e0035c..12453ed 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/gsm/GsmInboundSmsHandlerTest.java
@@ -606,6 +606,50 @@
 
     @Test
     @MediumTest
+    public void testMultiPartSmsWithInvalidSeqNumber() {
+        transitionFromStartupToIdle();
+
+        // prepare SMS part 1 and part 2
+        prepareMultiPartSms(false);
+
+        mSmsHeader.concatRef = new SmsHeader.ConcatRef();
+        doReturn(mSmsHeader).when(mGsmSmsMessage).getUserDataHeader();
+
+        doReturn(mInboundSmsTrackerPart1).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);
+
+        // verify the message is stored in the raw table
+        assertEquals(1, mContentProvider.getNumRows());
+
+        // State machine should go back to idle and wait for second part
+        assertEquals("IdleState", getCurrentState().getName());
+
+        // change seqNumber in part 2 to an invalid value
+        int invalidSeqNumber = -1;
+        mInboundSmsTrackerCVPart2.put("sequence", invalidSeqNumber);
+        doReturn(invalidSeqNumber).when(mInboundSmsTrackerPart2).getSequenceNumber();
+
+        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);
+
+        // verify no broadcasts sent
+        verify(mContext, never()).sendBroadcast(any(Intent.class));
+        // State machine should go back to idle
+        assertEquals("IdleState", getCurrentState().getName());
+    }
+
+    @Test
+    @MediumTest
     public void testMultipartSmsFromBlockedNumber_noBroadcastsSent() {
         mFakeBlockedNumberContentProvider.mBlockedNumbers.add("1234567890");
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsManagerTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsManagerTest.java
index fd19f80..2bf0094 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsManagerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsManagerTest.java
@@ -61,11 +61,8 @@
             ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED;
 
     PersistableBundle mBundle;
-
-    @Mock
-    IBinder mBinder;
-    @Mock
-    ImsConfigImplBase mImsConfigImplBaseMock;
+    @Mock IBinder mBinder;
+    @Mock ImsConfigImplBase mImsConfigImplBaseMock;
     Hashtable<Integer, Integer> mProvisionedIntVals = new Hashtable<>();
     Hashtable<Integer, String> mProvisionedStringVals = new Hashtable<>();
     ImsConfigImplBase.ImsConfigStub mImsConfigStub;
@@ -191,7 +188,6 @@
                 eq(SubscriptionManager.WFC_IMS_ENABLED),
                 eq("1"));
     }
-
     @Test
     public void testGetProvisionedValues() throws Exception {
         ImsManager imsManager = initializeProvisionedValues();
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
index 8914aee..eac6159 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
@@ -42,11 +42,14 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Message;
+import android.os.PersistableBundle;
 import android.support.test.filters.FlakyTest;
 import android.telecom.VideoProfile;
+import android.telephony.CarrierConfigManager;
 import android.telephony.DisconnectCause;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
 import android.telephony.ims.ImsCallProfile;
 import android.telephony.ims.ImsCallSession;
 import android.telephony.ims.ImsReasonInfo;
@@ -111,6 +114,7 @@
                     ImsReasonInfo.CODE_ANSWERED_ELSEWHERE);
             mCTUT.addReasonCodeRemapping(510, "Call answered elsewhere.",
                     ImsReasonInfo.CODE_ANSWERED_ELSEWHERE);
+            mCTUT.setDataEnabled(true);
             mCTHander = new Handler(mCTUT.getLooper());
             setReady(true);
         }
@@ -164,7 +168,7 @@
             }
         }).when(mImsCall).hold();
 
-        doReturn(mImsCallSession).when(mImsCall).getCallSession();
+        mImsCall.attachSession(mImsCallSession);
     }
 
     @Before
@@ -175,6 +179,7 @@
         mImsManagerInstances.put(mImsPhone.getPhoneId(), mImsManager);
         mImsCall = spy(new ImsCall(mContext, mImsCallProfile));
         mSecondImsCall = spy(new ImsCall(mContext, mImsCallProfile));
+        mImsPhoneConnectionListener = mock(ImsPhoneConnection.Listener.class);
         imsCallMocking(mImsCall);
         imsCallMocking(mSecondImsCall);
         doReturn(ImsFeature.STATE_READY).when(mImsManager).getImsServiceState();
@@ -191,6 +196,7 @@
             public ImsCall answer(InvocationOnMock invocation) throws Throwable {
                 mImsCallListener =
                         (ImsCall.Listener) invocation.getArguments()[2];
+                mImsCall.setListener(mImsCallListener);
                 return mImsCall;
             }
         }).when(mImsManager).takeCall(any(), any(), any());
@@ -198,9 +204,9 @@
         doAnswer(new Answer<ImsCall>() {
             @Override
             public ImsCall answer(InvocationOnMock invocation) throws Throwable {
-                mImsCallListener = (ImsCall.Listener) invocation.getArguments()[2];
+                mImsCallListener =
+                        (ImsCall.Listener) invocation.getArguments()[2];
                 mSecondImsCall.setListener(mImsCallListener);
-
                 return mSecondImsCall;
             }
         }).when(mImsManager).makeCall(eq(mImsCallProfile), (String []) any(),
@@ -345,6 +351,9 @@
         assertEquals(PhoneConstants.State.RINGING, mCTUT.getState());
         assertTrue(mCTUT.mRingingCall.isRinging());
         assertEquals(1, mCTUT.mRingingCall.getConnections().size());
+        ImsPhoneConnection connection =
+                (ImsPhoneConnection) mCTUT.mRingingCall.getConnections().get(0);
+        connection.addListener(mImsPhoneConnectionListener);
     }
 
     @Test
@@ -651,6 +660,55 @@
                 nullable(MmTelFeature.Listener.class));
     }
 
+    /**
+     * Test notification of handover from LTE to WIFI and WIFI to LTE and ensure that the expected
+     * connection events are sent.
+     */
+    @Test
+    @SmallTest
+    public void testNotifyHandovers() {
+        setupCarrierConfig();
+
+        //establish a MT call
+        testImsMTCallAccept();
+        ImsPhoneConnection connection =
+                (ImsPhoneConnection) mCTUT.mForegroundCall.getConnections().get(0);
+        ImsCall call = connection.getImsCall();
+        // Needs to be a video call to see this signalling.
+        mImsCallProfile.mCallType = ImsCallProfile.CALL_TYPE_VT;
+
+        // First handover from LTE to WIFI; this takes us into a mid-call state.
+        call.getImsCallSessionListenerProxy().callSessionHandover(call.getCallSession(),
+                ServiceState.RIL_RADIO_TECHNOLOGY_LTE, ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN,
+                new ImsReasonInfo());
+        // Handover back to LTE.
+        call.getImsCallSessionListenerProxy().callSessionHandover(call.getCallSession(),
+                ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN, ServiceState.RIL_RADIO_TECHNOLOGY_LTE,
+                new ImsReasonInfo());
+        verify(mImsPhoneConnectionListener).onConnectionEvent(eq(
+                TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE), isNull());
+
+        // Finally hand back to WIFI
+        call.getImsCallSessionListenerProxy().callSessionHandover(call.getCallSession(),
+                ServiceState.RIL_RADIO_TECHNOLOGY_LTE, ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN,
+                new ImsReasonInfo());
+        verify(mImsPhoneConnectionListener).onConnectionEvent(eq(
+                TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_LTE_TO_WIFI), isNull());
+    }
+
+    /**
+     * Configure carrier config options relevant to the unit test.
+     */
+    public void setupCarrierConfig() {
+        PersistableBundle bundle = new PersistableBundle();
+        bundle.putBoolean(CarrierConfigManager.KEY_NOTIFY_HANDOVER_VIDEO_FROM_LTE_TO_WIFI_BOOL,
+                true);
+        bundle.putBoolean(CarrierConfigManager.KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL,
+                true);
+        bundle.putBoolean(CarrierConfigManager.KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL, true);
+        mCTUT.updateCarrierConfigCache(bundle);
+    }
+
     @Test
     @SmallTest
     public void testLowBatteryDisconnectMidCall() {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java b/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
index 71d0794..95f1b3a 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/metrics/TelephonyMetricsTest.java
@@ -45,8 +45,6 @@
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.Base64;
 
-import android.telephony.ims.ImsReasonInfo;
-import android.telephony.ims.ImsCallSession;
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.GsmCdmaConnection;
 import com.android.internal.telephony.PhoneConstants;
diff --git a/tests/telephonytests/src/com/android/internal/telephony/mocks/ConnectivityServiceMock.java b/tests/telephonytests/src/com/android/internal/telephony/mocks/ConnectivityServiceMock.java
index 9210a08..8b89269 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/mocks/ConnectivityServiceMock.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/mocks/ConnectivityServiceMock.java
@@ -757,6 +757,11 @@
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_FACTORY, messenger));
     }
 
+    @Override
+    public byte[] getNetworkWatchlistConfigHash() {
+        throw new RuntimeException("not implemented");
+    }
+
     private void handleUnregisterNetworkFactory(Messenger messenger) {
         NetworkFactoryInfo nfi = mNetworkFactoryInfos.remove(messenger);
         if (nfi == null) {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/InstallCarrierAppUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/InstallCarrierAppUtilsTest.java
new file mode 100644
index 0000000..bc297a3
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/InstallCarrierAppUtilsTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.uicc;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import android.support.test.filters.SmallTest;
+
+import org.junit.Test;
+
+/**
+ * Tests for {@link InstallCarrierAppUtils}
+ */
+public class InstallCarrierAppUtilsTest {
+    @Test
+    @SmallTest
+    public void testParseAppNameMapFromString_emptyMap() {
+        String appNameKey = "com.app.package1";
+        String mapString = "";
+        String appName =
+                InstallCarrierAppUtils.getAppNameFromPackageName(appNameKey, mapString);
+        assertNull(appName);
+    }
+
+    @Test
+    @SmallTest
+    public void testParseAppNameMapFromString_completeMap() {
+        String appName1Key = "com.app.package1";
+        String appName2Key = "com.app.package2";
+        String expectedAppName1 = "AppName1";
+        String expectedAppName2 = "AppName2";
+        String mapString = appName1Key + ":" + expectedAppName1 + ";"
+                + appName2Key + ":" + expectedAppName2 + ";";
+
+        String appName1 =
+                InstallCarrierAppUtils.getAppNameFromPackageName(appName1Key, mapString);
+        String appName2 =
+                InstallCarrierAppUtils.getAppNameFromPackageName(appName2Key, mapString);
+        assertEquals(expectedAppName1, appName1);
+        assertEquals(expectedAppName2, appName2);
+    }
+
+    @Test
+    @SmallTest
+    public void testParseAppNameMapFromString_packageCaseMismatch() {
+        String appNameKey = "com.app.package1";
+        String expectedAppName = "AppName1";
+        String mapString = appNameKey + ":" + expectedAppName + ";";
+
+        String appNameCaseTestKey = "cOm.ApP.pAcKaGe1";
+        String appName1 =
+                InstallCarrierAppUtils.getAppNameFromPackageName(appNameCaseTestKey, mapString);
+        assertEquals(expectedAppName, appName1);
+    }
+
+    @Test
+    @SmallTest
+    public void testParseAppNameMapFromString_packageNotFound() {
+        String appNameKey = "com.app.package1";
+        String expectedAppName = "AppName1";
+        String mapString = appNameKey + ":" + expectedAppName + ";";
+
+        String missingAppNameKey = "AppName3";
+        String missingAppName =
+                InstallCarrierAppUtils.getAppNameFromPackageName(missingAppNameKey, mapString);
+        assertNull(missingAppName);
+    }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java
index 404754c..b76f2cf 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/UiccProfileTest.java
@@ -48,6 +48,8 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 
+import java.util.Map;
+
 public class UiccProfileTest extends TelephonyTest {
     private UiccProfile mUiccProfile;
 
@@ -177,6 +179,28 @@
 
     @Test
     @SmallTest
+    public void testParseWhitelistMapFromString() {
+        String whitelist = "";
+        Map<String, String> parsedMap = UiccProfile.parseToCertificateToPackageMap(whitelist);
+        assertTrue(parsedMap.isEmpty());
+
+        whitelist = "nokey;value;separation";
+        parsedMap = UiccProfile.parseToCertificateToPackageMap(whitelist);
+        assertTrue(parsedMap.isEmpty());
+
+        whitelist = "KEY1:value1";
+        parsedMap = UiccProfile.parseToCertificateToPackageMap(whitelist);
+        assertEquals("value1", parsedMap.get("KEY1"));
+
+        whitelist = "KEY1:value1;   KEY2:value2  ;KEY3:value3";
+        parsedMap = UiccProfile.parseToCertificateToPackageMap(whitelist);
+        assertEquals("value1", parsedMap.get("KEY1"));
+        assertEquals("value2", parsedMap.get("KEY2"));
+        assertEquals("value3", parsedMap.get("KEY3"));
+    }
+
+    @Test
+    @SmallTest
     public void testUpdateUiccProfileApplication() {
         /* update app status and index */
         IccCardApplicationStatus cdmaApp = composeUiccApplicationStatus(
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 dc621a5..a3d7245 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
@@ -274,7 +274,7 @@
                                 "com.google.android.apps.myapp", 1)
                 },
                 profile.getUiccAccessRules().toArray());
-        verifyStoreData(channel, "BF2D195A0A896700000000004523015C0B5A909192B79F709599BF76");
+        verifyStoreData(channel, "BF2D1BA00C5A0A896700000000004523015C0B5A909192B79F709599BF76");
     }
 
     @Test
@@ -608,11 +608,11 @@
         assertUnexpectedException(resultCaptor.exception);
         assertEquals("BF3802A000", IccUtils.bytesToHexString(resultCaptor.result));
         verifyStoreData(channel,
-                "BF3846" + "A000" + "A100" + "A200" + "A300" + "A03C"
+                "BF384B" + "A000" + "A100" + "A200" + "A300" + "A041"
                         + "800D4131423243332D583459355A36" // Matching id
-                        + "A12B800489674523" // TAC
+                        + "A130800489674523" // TAC
                         // Device capabilities
-                        + "A11980030B000081030B0000830303000084030C000085030B0000"
+                        + "A11E80030B000081030B00008203010000830303000084030C000085030B0000"
                         + "82088967452301214305"); // IMEI
     }
 
@@ -927,7 +927,7 @@
         child = node.getChild(Tags.TAG_CTX_1);
         assertTrue(Arrays.equals(new byte[] {11, 0 , 0}, child.asBytes()));
 
-        devCapItem = "cdma_1x,1";
+        devCapItem = "cdma1x,1";
         mEuiccCard.addDeviceCapability(devCapsBuilder, devCapItem);
         node = devCapsBuilder.build();
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/ApduSenderTest.java b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/ApduSenderTest.java
index 55626a2..2bd995d 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/ApduSenderTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/uicc/euicc/apdu/ApduSenderTest.java
@@ -244,6 +244,50 @@
     }
 
     @Test
+    public void testSendStoreDataLongDataMod0() throws InterruptedException {
+        String aid = "B2C3D4";
+        ApduSender sender = new ApduSender(mMockCi, aid, false /* supportExtendedApdu */);
+
+        int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
+        LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, "9000", "B2222B9000");
+        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel);
+
+        // Each segment has 0xFF (the limit of a single command) bytes.
+        String s1 = new String(new char[0xFF]).replace("\0", "AA");
+        String s2 = new String(new char[0xFF]).replace("\0", "BB");
+        String longData = s1 + s2;
+        sender.send((selectResponse, requestBuilder) -> {
+            requestBuilder.addStoreData(longData);
+        }, mResponseCaptor, mHandler);
+        mResponseCaptor.await();
+
+        assertEquals("B2222B", IccUtils.bytesToHexString(mResponseCaptor.response));
+        verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x11),
+                eq(0), eq(0xFF), eq(s1), any());
+        verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x91),
+                eq(1), eq(0xFF), eq(s2), any());
+    }
+
+    @Test
+    public void testSendStoreDataLen0() throws InterruptedException {
+        String aid = "B2C3D4";
+        ApduSender sender = new ApduSender(mMockCi, aid, false /* supportExtendedApdu */);
+
+        int channel = LogicalChannelMocker.mockOpenLogicalChannelResponse(mMockCi, "9000");
+        LogicalChannelMocker.mockSendToLogicalChannel(mMockCi, channel, "B2222B9000");
+        LogicalChannelMocker.mockCloseLogicalChannel(mMockCi, channel);
+
+        sender.send((selectResponse, requestBuilder) -> {
+            requestBuilder.addStoreData("");
+        }, mResponseCaptor, mHandler);
+        mResponseCaptor.await();
+
+        assertEquals("B2222B", IccUtils.bytesToHexString(mResponseCaptor.response));
+        verify(mMockCi).iccTransmitApduLogicalChannel(eq(channel), eq(0x81), eq(0xE2), eq(0x91),
+                eq(0), eq(0), eq(""), any());
+    }
+
+    @Test
     public void testSendErrorResponseInMiddle() throws InterruptedException {
         String aid = "B2C3D4";
         ApduSender sender = new ApduSender(mMockCi, aid, false /* supportExtendedApdu */);