Check permissions on getDeviceId. am: 79eecef63f am: a924220685
am: 9c3a86f07d

* commit '9c3a86f07d00c32f29869b8a24626ef67038acd2':
  Check permissions on getDeviceId.
diff --git a/src/java/android/provider/Telephony.java b/src/java/android/provider/Telephony.java
index b84e94d..f460cdc 100644
--- a/src/java/android/provider/Telephony.java
+++ b/src/java/android/provider/Telephony.java
@@ -988,6 +988,15 @@
                     "android.provider.Telephony.SMS_CB_RECEIVED";
 
             /**
+             * Action: A SMS based carrier provision intent. Used to identify default
+             * carrier provisioning app on the device.
+             * @hide
+             */
+            @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+            public static final String SMS_CARRIER_PROVISION_ACTION =
+                    "android.provider.Telephony.SMS_CARRIER_PROVISION";
+
+            /**
              * Broadcast Action: A new Emergency Broadcast message has been received
              * by the device. The intent will have the following extra
              * values:</p>
@@ -1786,7 +1795,6 @@
          * It's convenient for use with SMS messages.
          * @param context the context object to use.
          * @param recipient the recipient to send to.
-         * @hide
          */
         public static long getOrCreateThreadId(Context context, String recipient) {
             Set<String> recipients = new HashSet<String>();
@@ -1804,7 +1812,6 @@
          * <p>Find the thread ID of the same set of recipients (in any order,
          * without any additions). If one is found, return it. Otherwise,
          * return a unique thread ID.</p>
-         * @hide
          */
         public static long getOrCreateThreadId(
                 Context context, Set<String> recipients) {
@@ -2589,6 +2596,17 @@
         public static final String BEARER = "bearer";
 
         /**
+         * Radio Access Technology bitmask.
+         * To check what values can be contained, refer to {@link android.telephony.ServiceState}.
+         * 0 indicates all techs otherwise first bit refers to RAT/bearer 1, second bit refers to
+         * RAT/bearer 2 and so on.
+         * Bitmask for a radio tech R is (1 << (R - 1))
+         * <P>Type: INTEGER</P>
+         * @hide
+         */
+        public static final String BEARER_BITMASK = "bearer_bitmask";
+
+        /**
          * MVNO type:
          * {@code SPN (Service Provider Name), IMSI, GID (Group Identifier Level 1)}.
          * <P>Type: TEXT</P>
@@ -2654,6 +2672,48 @@
          * @hide
          */
         public static final String MTU = "mtu";
+
+        /**
+         * Is this APN added/edited/deleted by a user or carrier?
+         * <p>Type: INTEGER </p>
+         * @hide
+         */
+        public static final String EDITED = "edited";
+
+        /**
+         * Following are possible values for the EDITED field
+         * @hide
+         */
+        public static final int UNEDITED = 0;
+        /**
+         *  @hide
+         */
+        public static final int USER_EDITED = 1;
+        /**
+         *  @hide
+         */
+        public static final int USER_DELETED = 2;
+        /**
+         * DELETED_BUT_PRESENT is an intermediate value used to indicate that an entry deleted
+         * by the user is still present in the new APN database and therefore must remain tagged
+         * as user deleted rather than completely removed from the database
+         * @hide
+         */
+        public static final int USER_DELETED_BUT_PRESENT_IN_XML = 3;
+        /**
+         *  @hide
+         */
+        public static final int CARRIER_EDITED = 4;
+        /**
+         * CARRIER_DELETED values are currently not used as there is no usecase. If they are used,
+         * delete() will have to change accordingly. Currently it is hardcoded to USER_DELETED.
+         * @hide
+         */
+        public static final int CARRIER_DELETED = 5;
+        /**
+         *  @hide
+         */
+        public static final int CARRIER_DELETED_BUT_PRESENT_IN_XML = 6;
     }
 
     /**
diff --git a/src/java/android/telephony/CellBroadcastMessage.java b/src/java/android/telephony/CellBroadcastMessage.java
index b6bd4bb..80270ea 100644
--- a/src/java/android/telephony/CellBroadcastMessage.java
+++ b/src/java/android/telephony/CellBroadcastMessage.java
@@ -90,6 +90,7 @@
         mSmsCbMessage = new SmsCbMessage(in);
         mDeliveryTime = in.readLong();
         mIsRead = (in.readInt() != 0);
+        mSubId = in.readInt();
     }
 
     /** Parcelable: no special flags. */
@@ -103,6 +104,7 @@
         mSmsCbMessage.writeToParcel(out, flags);
         out.writeLong(mDeliveryTime);
         out.writeInt(mIsRead ? 1 : 0);
+        out.writeInt(mSubId);
     }
 
     public static final Parcelable.Creator<CellBroadcastMessage> CREATOR
diff --git a/src/java/android/telephony/SmsManager.java b/src/java/android/telephony/SmsManager.java
index a07d141..189dc4b 100644
--- a/src/java/android/telephony/SmsManager.java
+++ b/src/java/android/telephony/SmsManager.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
+import android.os.BaseBundle;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -87,131 +88,163 @@
      * Whether to append transaction id to MMS WAP Push M-Notification.ind's content location URI
      * when constructing the download URL of a new MMS (boolean type)
      */
-    public static final String MMS_CONFIG_APPEND_TRANSACTION_ID = "enabledTransID";
+    public static final String MMS_CONFIG_APPEND_TRANSACTION_ID =
+            CarrierConfigManager.KEY_MMS_APPEND_TRANSACTION_ID_BOOL;
     /**
      * Whether MMS is enabled for the current carrier (boolean type)
      */
-    public static final String MMS_CONFIG_MMS_ENABLED = "enabledMMS";
+    public static final String
+        MMS_CONFIG_MMS_ENABLED = CarrierConfigManager.KEY_MMS_MMS_ENABLED_BOOL;
     /**
      * Whether group MMS is enabled for the current carrier (boolean type)
      */
-    public static final String MMS_CONFIG_GROUP_MMS_ENABLED = "enableGroupMms";
+    public static final String
+            MMS_CONFIG_GROUP_MMS_ENABLED = CarrierConfigManager.KEY_MMS_GROUP_MMS_ENABLED_BOOL;
     /**
-     * If this is enabled, M-NotifyResp.ind should be sent to the WAP Push content location
-     * instead of the default MMSC (boolean type)
+     * If this is enabled, M-NotifyResp.ind should be sent to the WAP Push content location instead
+     * of the default MMSC (boolean type)
      */
-    public static final String MMS_CONFIG_NOTIFY_WAP_MMSC_ENABLED = "enabledNotifyWapMMSC";
+    public static final String MMS_CONFIG_NOTIFY_WAP_MMSC_ENABLED =
+            CarrierConfigManager.KEY_MMS_NOTIFY_WAP_MMSC_ENABLED_BOOL;
     /**
      * Whether alias is enabled (boolean type)
      */
-    public static final String MMS_CONFIG_ALIAS_ENABLED = "aliasEnabled";
+    public static final String
+            MMS_CONFIG_ALIAS_ENABLED = CarrierConfigManager.KEY_MMS_ALIAS_ENABLED_BOOL;
     /**
      * Whether audio is allowed to be attached for MMS messages (boolean type)
      */
-    public static final String MMS_CONFIG_ALLOW_ATTACH_AUDIO = "allowAttachAudio";
+    public static final String
+            MMS_CONFIG_ALLOW_ATTACH_AUDIO = CarrierConfigManager.KEY_MMS_ALLOW_ATTACH_AUDIO_BOOL;
     /**
      * Whether multipart SMS is enabled (boolean type)
      */
-    public static final String MMS_CONFIG_MULTIPART_SMS_ENABLED = "enableMultipartSMS";
+    public static final String MMS_CONFIG_MULTIPART_SMS_ENABLED =
+            CarrierConfigManager.KEY_MMS_MULTIPART_SMS_ENABLED_BOOL;
     /**
      * Whether SMS delivery report is enabled (boolean type)
      */
-    public static final String MMS_CONFIG_SMS_DELIVERY_REPORT_ENABLED = "enableSMSDeliveryReports";
+    public static final String MMS_CONFIG_SMS_DELIVERY_REPORT_ENABLED =
+            CarrierConfigManager.KEY_MMS_SMS_DELIVERY_REPORT_ENABLED_BOOL;
     /**
      * Whether content-disposition field should be expected in an MMS PDU (boolean type)
      */
     public static final String MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION =
-            "supportMmsContentDisposition";
+            CarrierConfigManager.KEY_MMS_SUPPORT_MMS_CONTENT_DISPOSITION_BOOL;
     /**
      * Whether multipart SMS should be sent as separate messages
      */
     public static final String MMS_CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES =
-            "sendMultipartSmsAsSeparateMessages";
+            CarrierConfigManager.KEY_MMS_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES_BOOL;
     /**
      * Whether MMS read report is enabled (boolean type)
      */
-    public static final String MMS_CONFIG_MMS_READ_REPORT_ENABLED = "enableMMSReadReports";
+    public static final String MMS_CONFIG_MMS_READ_REPORT_ENABLED =
+            CarrierConfigManager.KEY_MMS_MMS_READ_REPORT_ENABLED_BOOL;
     /**
      * Whether MMS delivery report is enabled (boolean type)
      */
-    public static final String MMS_CONFIG_MMS_DELIVERY_REPORT_ENABLED = "enableMMSDeliveryReports";
+    public static final String MMS_CONFIG_MMS_DELIVERY_REPORT_ENABLED =
+            CarrierConfigManager.KEY_MMS_MMS_DELIVERY_REPORT_ENABLED_BOOL;
     /**
      * Max MMS message size in bytes (int type)
      */
-    public static final String MMS_CONFIG_MAX_MESSAGE_SIZE = "maxMessageSize";
+    public static final String
+            MMS_CONFIG_MAX_MESSAGE_SIZE = CarrierConfigManager.KEY_MMS_MAX_MESSAGE_SIZE_INT;
     /**
      * Max MMS image width (int type)
      */
-    public static final String MMS_CONFIG_MAX_IMAGE_WIDTH = "maxImageWidth";
+    public static final String
+            MMS_CONFIG_MAX_IMAGE_WIDTH = CarrierConfigManager.KEY_MMS_MAX_IMAGE_WIDTH_INT;
     /**
      * Max MMS image height (int type)
      */
-    public static final String MMS_CONFIG_MAX_IMAGE_HEIGHT = "maxImageHeight";
+    public static final String
+            MMS_CONFIG_MAX_IMAGE_HEIGHT = CarrierConfigManager.KEY_MMS_MAX_IMAGE_HEIGHT_INT;
     /**
      * Limit of recipients of MMS messages (int type)
      */
-    public static final String MMS_CONFIG_RECIPIENT_LIMIT = "recipientLimit";
+    public static final String
+            MMS_CONFIG_RECIPIENT_LIMIT = CarrierConfigManager.KEY_MMS_RECIPIENT_LIMIT_INT;
     /**
      * Min alias character count (int type)
      */
-    public static final String MMS_CONFIG_ALIAS_MIN_CHARS = "aliasMinChars";
+    public static final String
+            MMS_CONFIG_ALIAS_MIN_CHARS = CarrierConfigManager.KEY_MMS_ALIAS_MIN_CHARS_INT;
     /**
      * Max alias character count (int type)
      */
-    public static final String MMS_CONFIG_ALIAS_MAX_CHARS = "aliasMaxChars";
+    public static final String
+            MMS_CONFIG_ALIAS_MAX_CHARS = CarrierConfigManager.KEY_MMS_ALIAS_MAX_CHARS_INT;
     /**
-     * When the number of parts of a multipart SMS reaches this threshold, it should be
-     * converted into an MMS (int type)
+     * When the number of parts of a multipart SMS reaches this threshold, it should be converted
+     * into an MMS (int type)
      */
-    public static final String MMS_CONFIG_SMS_TO_MMS_TEXT_THRESHOLD = "smsToMmsTextThreshold";
+    public static final String MMS_CONFIG_SMS_TO_MMS_TEXT_THRESHOLD =
+            CarrierConfigManager.KEY_MMS_SMS_TO_MMS_TEXT_THRESHOLD_INT;
     /**
      * Some carriers require SMS to be converted into MMS when text length reaches this threshold
      * (int type)
      */
     public static final String MMS_CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD =
-            "smsToMmsTextLengthThreshold";
+            CarrierConfigManager.KEY_MMS_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD_INT;
     /**
      * Max message text size (int type)
      */
-    public static final String MMS_CONFIG_MESSAGE_TEXT_MAX_SIZE = "maxMessageTextSize";
+    public static final String MMS_CONFIG_MESSAGE_TEXT_MAX_SIZE =
+            CarrierConfigManager.KEY_MMS_MESSAGE_TEXT_MAX_SIZE_INT;
     /**
      * Max message subject length (int type)
      */
-    public static final String MMS_CONFIG_SUBJECT_MAX_LENGTH = "maxSubjectLength";
+    public static final String
+            MMS_CONFIG_SUBJECT_MAX_LENGTH = CarrierConfigManager.KEY_MMS_SUBJECT_MAX_LENGTH_INT;
     /**
      * MMS HTTP socket timeout in milliseconds (int type)
      */
-    public static final String MMS_CONFIG_HTTP_SOCKET_TIMEOUT = "httpSocketTimeout";
+    public static final String
+            MMS_CONFIG_HTTP_SOCKET_TIMEOUT = CarrierConfigManager.KEY_MMS_HTTP_SOCKET_TIMEOUT_INT;
     /**
      * The name of the UA Prof URL HTTP header for MMS HTTP request (String type)
      */
-    public static final String MMS_CONFIG_UA_PROF_TAG_NAME = "uaProfTagName";
+    public static final String
+            MMS_CONFIG_UA_PROF_TAG_NAME = CarrierConfigManager.KEY_MMS_UA_PROF_TAG_NAME_STRING;
     /**
      * The User-Agent header value for MMS HTTP request (String type)
      */
-    public static final String MMS_CONFIG_USER_AGENT = "userAgent";
+    public static final String
+            MMS_CONFIG_USER_AGENT = CarrierConfigManager.KEY_MMS_USER_AGENT_STRING;
     /**
      * The UA Profile URL header value for MMS HTTP request (String type)
      */
-    public static final String MMS_CONFIG_UA_PROF_URL = "uaProfUrl";
+    public static final String
+            MMS_CONFIG_UA_PROF_URL = CarrierConfigManager.KEY_MMS_UA_PROF_URL_STRING;
     /**
      * A list of HTTP headers to add to MMS HTTP request, separated by "|" (String type)
      */
-    public static final String MMS_CONFIG_HTTP_PARAMS = "httpParams";
+    public static final String
+            MMS_CONFIG_HTTP_PARAMS = CarrierConfigManager.KEY_MMS_HTTP_PARAMS_STRING;
     /**
      * Email gateway number (String type)
      */
-    public static final String MMS_CONFIG_EMAIL_GATEWAY_NUMBER = "emailGatewayNumber";
+    public static final String MMS_CONFIG_EMAIL_GATEWAY_NUMBER =
+            CarrierConfigManager.KEY_MMS_EMAIL_GATEWAY_NUMBER_STRING;
     /**
      * The suffix to append to the NAI header value for MMS HTTP request (String type)
      */
-    public static final String MMS_CONFIG_NAI_SUFFIX = "naiSuffix";
+    public static final String
+            MMS_CONFIG_NAI_SUFFIX = CarrierConfigManager.KEY_MMS_NAI_SUFFIX_STRING;
     /**
-     * If true, show the cell broadcast (amber alert) in the SMS settings. Some carriers
-     * don't want this shown. (Boolean type)
+     * If true, show the cell broadcast (amber alert) in the SMS settings. Some carriers don't want
+     * this shown. (Boolean type)
      */
     public static final String MMS_CONFIG_SHOW_CELL_BROADCAST_APP_LINKS =
-            "config_cellBroadcastAppLinks";
+            CarrierConfigManager.KEY_MMS_SHOW_CELL_BROADCAST_APP_LINKS_BOOL;
+    /**
+     * Whether the carrier MMSC supports charset field in Content-Type header. If this is false,
+     * then we don't add "charset" to "Content-Type"
+     */
+    public static final String MMS_CONFIG_SUPPORT_HTTP_CHARSET_HEADER =
+            CarrierConfigManager.KEY_MMS_SUPPORT_HTTP_CHARSET_HEADER_BOOL;
     /*
      * Forwarded constants from SimDialogActivity.
      */
@@ -257,6 +290,13 @@
     public void sendTextMessage(
             String destinationAddress, String scAddress, String text,
             PendingIntent sentIntent, PendingIntent deliveryIntent) {
+        sendTextMessageInternal(destinationAddress, scAddress, text,
+            sentIntent, deliveryIntent, true /* persistMessageForCarrierApp*/);
+    }
+
+    private void sendTextMessageInternal(String destinationAddress, String scAddress,
+            String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
+            boolean persistMessageForCarrierApp) {
         if (TextUtils.isEmpty(destinationAddress)) {
             throw new IllegalArgumentException("Invalid destinationAddress");
         }
@@ -269,6 +309,50 @@
             ISms iccISms = getISmsServiceOrThrow();
             iccISms.sendTextForSubscriber(getSubscriptionId(), ActivityThread.currentPackageName(),
                     destinationAddress,
+                    scAddress, text, sentIntent, deliveryIntent,
+                    persistMessageForCarrierApp);
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+    }
+
+    /**
+     * Send a text based SMS without writing it into the SMS Provider.
+     *
+     * <p>Only the carrier app can call this method.</p>
+     *
+     * @see #sendTextMessage(String, String, String, PendingIntent, PendingIntent)
+     * @hide
+     */
+    public void sendTextMessageWithoutPersisting(
+            String destinationAddress, String scAddress, String text,
+            PendingIntent sentIntent, PendingIntent deliveryIntent) {
+        sendTextMessageInternal(destinationAddress, scAddress, text,
+            sentIntent, deliveryIntent, false /* persistMessageForCarrierApp*/);
+    }
+
+    /**
+     * A variant of {@link SmsManager#sendTextMessage} that allows self to be the caller. This is
+     * for internal use only.
+     *
+     * @hide
+     */
+    public void sendTextMessageWithSelfPermissions(
+            String destinationAddress, String scAddress, String text,
+            PendingIntent sentIntent, PendingIntent deliveryIntent) {
+        if (TextUtils.isEmpty(destinationAddress)) {
+            throw new IllegalArgumentException("Invalid destinationAddress");
+        }
+
+        if (TextUtils.isEmpty(text)) {
+            throw new IllegalArgumentException("Invalid message body");
+        }
+
+        try {
+            ISms iccISms = getISmsServiceOrThrow();
+            iccISms.sendTextForSubscriberWithSelfPermissions(getSubscriptionId(),
+                    ActivityThread.currentPackageName(),
+                    destinationAddress,
                     scAddress, text, sentIntent, deliveryIntent);
         } catch (RemoteException ex) {
             // ignore it
@@ -279,7 +363,7 @@
      * Inject an SMS PDU into the android application framework.
      *
      * The caller should have carrier privileges.
-     * @see android.telephony.TelephonyManager.hasCarrierPrivileges
+     * @see android.telephony.TelephonyManager#hasCarrierPrivileges
      *
      * @param pdu is the byte array of pdu to be injected into android application framework
      * @param format is the format of SMS pdu (3gpp or 3gpp2)
@@ -301,7 +385,8 @@
         try {
             ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
             if (iccISms != null) {
-                iccISms.injectSmsPdu(pdu, format, receivedIntent);
+                iccISms.injectSmsPduForSubscriber(
+                        getSubscriptionId(), pdu, format, receivedIntent);
             }
         } catch (RemoteException ex) {
           // ignore it
@@ -369,6 +454,14 @@
     public void sendMultipartTextMessage(
             String destinationAddress, String scAddress, ArrayList<String> parts,
             ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
+        sendMultipartTextMessageInternal(destinationAddress, scAddress, parts,
+              sentIntents, deliveryIntents, true /* persistMessageForCarrierApp*/);
+    }
+
+    private void sendMultipartTextMessageInternal(
+            String destinationAddress, String scAddress, ArrayList<String> parts,
+            ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents,
+            boolean persistMessageForCarrierApp) {
         if (TextUtils.isEmpty(destinationAddress)) {
             throw new IllegalArgumentException("Invalid destinationAddress");
         }
@@ -382,7 +475,7 @@
                 iccISms.sendMultipartTextForSubscriber(getSubscriptionId(),
                         ActivityThread.currentPackageName(),
                         destinationAddress, scAddress, parts,
-                        sentIntents, deliveryIntents);
+                        sentIntents, deliveryIntents, persistMessageForCarrierApp);
             } catch (RemoteException ex) {
                 // ignore it
             }
@@ -401,6 +494,21 @@
     }
 
     /**
+     * Send a multi-part text based SMS without writing it into the SMS Provider.
+     *
+     * <p>Only the carrier app can call this method.</p>
+     *
+     * @see #sendMultipartTextMessage(String, String, ArrayList, ArrayList, ArrayList)
+     * @hide
+     **/
+    public void sendMultipartTextMessageWithoutPersisting(
+            String destinationAddress, String scAddress, ArrayList<String> parts,
+            ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) {
+        sendMultipartTextMessageInternal(destinationAddress, scAddress, parts,
+            sentIntents, deliveryIntents, false /* persistMessageForCarrierApp*/);
+    }
+
+    /**
      * Send a data based SMS to a specific application port.
      *
      * <p class="note"><strong>Note:</strong> Using this method requires that your app has the
@@ -452,6 +560,35 @@
     }
 
     /**
+     * A variant of {@link SmsManager#sendDataMessage} that allows self to be the caller. This is
+     * for internal use only.
+     *
+     * @hide
+     */
+    public void sendDataMessageWithSelfPermissions(
+            String destinationAddress, String scAddress, short destinationPort,
+            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+        if (TextUtils.isEmpty(destinationAddress)) {
+            throw new IllegalArgumentException("Invalid destinationAddress");
+        }
+
+        if (data == null || data.length == 0) {
+            throw new IllegalArgumentException("Invalid message data");
+        }
+
+        try {
+            ISms iccISms = getISmsServiceOrThrow();
+            iccISms.sendDataForSubscriberWithSelfPermissions(getSubscriptionId(),
+                    ActivityThread.currentPackageName(), destinationAddress, scAddress,
+                    destinationPort & 0xFFFF, data, sentIntent, deliveryIntent);
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+    }
+
+
+
+    /**
      * Get the SmsManager associated with the default subscription id. The instance will always be
      * associated with the default subscription id, even if the default subscription id is changed.
      *
@@ -1443,4 +1580,70 @@
         return null;
     }
 
+    /**
+     * Filters a bundle to only contain MMS config variables.
+     *
+     * This is for use with bundles returned by {@link CarrierConfigManager} which contain MMS
+     * config and unrelated config. It is assumed that all MMS_CONFIG_* keys are present in the
+     * supplied bundle.
+     *
+     * @param config a Bundle that contains MMS config variables and possibly more.
+     * @return a new Bundle that only contains the MMS_CONFIG_* keys defined above.
+     * @hide
+     */
+    public static Bundle getMmsConfig(BaseBundle config) {
+        Bundle filtered = new Bundle();
+        filtered.putBoolean(MMS_CONFIG_APPEND_TRANSACTION_ID,
+                config.getBoolean(MMS_CONFIG_APPEND_TRANSACTION_ID));
+        filtered.putBoolean(MMS_CONFIG_MMS_ENABLED, config.getBoolean(MMS_CONFIG_MMS_ENABLED));
+        filtered.putBoolean(MMS_CONFIG_GROUP_MMS_ENABLED,
+                config.getBoolean(MMS_CONFIG_GROUP_MMS_ENABLED));
+        filtered.putBoolean(MMS_CONFIG_NOTIFY_WAP_MMSC_ENABLED,
+                config.getBoolean(MMS_CONFIG_NOTIFY_WAP_MMSC_ENABLED));
+        filtered.putBoolean(MMS_CONFIG_ALIAS_ENABLED, config.getBoolean(MMS_CONFIG_ALIAS_ENABLED));
+        filtered.putBoolean(MMS_CONFIG_ALLOW_ATTACH_AUDIO,
+                config.getBoolean(MMS_CONFIG_ALLOW_ATTACH_AUDIO));
+        filtered.putBoolean(MMS_CONFIG_MULTIPART_SMS_ENABLED,
+                config.getBoolean(MMS_CONFIG_MULTIPART_SMS_ENABLED));
+        filtered.putBoolean(MMS_CONFIG_SMS_DELIVERY_REPORT_ENABLED,
+                config.getBoolean(MMS_CONFIG_SMS_DELIVERY_REPORT_ENABLED));
+        filtered.putBoolean(MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION,
+                config.getBoolean(MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION));
+        filtered.putBoolean(MMS_CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES,
+                config.getBoolean(MMS_CONFIG_SEND_MULTIPART_SMS_AS_SEPARATE_MESSAGES));
+        filtered.putBoolean(MMS_CONFIG_MMS_READ_REPORT_ENABLED,
+                config.getBoolean(MMS_CONFIG_MMS_READ_REPORT_ENABLED));
+        filtered.putBoolean(MMS_CONFIG_MMS_DELIVERY_REPORT_ENABLED,
+                config.getBoolean(MMS_CONFIG_MMS_DELIVERY_REPORT_ENABLED));
+        filtered.putInt(MMS_CONFIG_MAX_MESSAGE_SIZE, config.getInt(MMS_CONFIG_MAX_MESSAGE_SIZE));
+        filtered.putInt(MMS_CONFIG_MAX_IMAGE_WIDTH, config.getInt(MMS_CONFIG_MAX_IMAGE_WIDTH));
+        filtered.putInt(MMS_CONFIG_MAX_IMAGE_HEIGHT, config.getInt(MMS_CONFIG_MAX_IMAGE_HEIGHT));
+        filtered.putInt(MMS_CONFIG_RECIPIENT_LIMIT, config.getInt(MMS_CONFIG_RECIPIENT_LIMIT));
+        filtered.putInt(MMS_CONFIG_ALIAS_MIN_CHARS, config.getInt(MMS_CONFIG_ALIAS_MIN_CHARS));
+        filtered.putInt(MMS_CONFIG_ALIAS_MAX_CHARS, config.getInt(MMS_CONFIG_ALIAS_MAX_CHARS));
+        filtered.putInt(MMS_CONFIG_SMS_TO_MMS_TEXT_THRESHOLD,
+                config.getInt(MMS_CONFIG_SMS_TO_MMS_TEXT_THRESHOLD));
+        filtered.putInt(MMS_CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD,
+                config.getInt(MMS_CONFIG_SMS_TO_MMS_TEXT_LENGTH_THRESHOLD));
+        filtered.putInt(MMS_CONFIG_MESSAGE_TEXT_MAX_SIZE,
+                config.getInt(MMS_CONFIG_MESSAGE_TEXT_MAX_SIZE));
+        filtered.putInt(MMS_CONFIG_SUBJECT_MAX_LENGTH,
+                config.getInt(MMS_CONFIG_SUBJECT_MAX_LENGTH));
+        filtered.putInt(MMS_CONFIG_HTTP_SOCKET_TIMEOUT,
+                config.getInt(MMS_CONFIG_HTTP_SOCKET_TIMEOUT));
+        filtered.putString(MMS_CONFIG_UA_PROF_TAG_NAME,
+                config.getString(MMS_CONFIG_UA_PROF_TAG_NAME));
+        filtered.putString(MMS_CONFIG_USER_AGENT, config.getString(MMS_CONFIG_USER_AGENT));
+        filtered.putString(MMS_CONFIG_UA_PROF_URL, config.getString(MMS_CONFIG_UA_PROF_URL));
+        filtered.putString(MMS_CONFIG_HTTP_PARAMS, config.getString(MMS_CONFIG_HTTP_PARAMS));
+        filtered.putString(MMS_CONFIG_EMAIL_GATEWAY_NUMBER,
+                config.getString(MMS_CONFIG_EMAIL_GATEWAY_NUMBER));
+        filtered.putString(MMS_CONFIG_NAI_SUFFIX, config.getString(MMS_CONFIG_NAI_SUFFIX));
+        filtered.putBoolean(MMS_CONFIG_SHOW_CELL_BROADCAST_APP_LINKS,
+                config.getBoolean(MMS_CONFIG_SHOW_CELL_BROADCAST_APP_LINKS));
+        filtered.putBoolean(MMS_CONFIG_SUPPORT_HTTP_CHARSET_HEADER,
+                config.getBoolean(MMS_CONFIG_SUPPORT_HTTP_CHARSET_HEADER));
+        return filtered;
+    }
+
 }
diff --git a/src/java/android/telephony/SmsMessage.java b/src/java/android/telephony/SmsMessage.java
index 1dba484..3323078 100644
--- a/src/java/android/telephony/SmsMessage.java
+++ b/src/java/android/telephony/SmsMessage.java
@@ -149,17 +149,18 @@
     }
 
     /**
-     * Create an SmsMessage from a raw PDU.
-     *
-     * <p><b>This method will soon be deprecated</b> and all applications which handle
+     * Create an SmsMessage from a raw PDU. Guess format based on Voice
+     * technology first, if it fails use other format.
+     * All applications which handle
      * incoming SMS messages by processing the {@code SMS_RECEIVED_ACTION} broadcast
      * intent <b>must</b> now pass the new {@code format} String extra from the intent
      * into the new method {@code createFromPdu(byte[], String)} which takes an
      * extra format parameter. This is required in order to correctly decode the PDU on
      * devices that require support for both 3GPP and 3GPP2 formats at the same time,
-     * such as dual-mode GSM/CDMA and CDMA/LTE phones.  Guess format based on Voice
-     * technology first, if it fails use other format.
+     * such as dual-mode GSM/CDMA and CDMA/LTE phones.
+     * @deprecated Use {@link #createFromPdu(byte[], String)} instead.
      */
+    @Deprecated
     public static SmsMessage createFromPdu(byte[] pdu) {
          SmsMessage message = null;
 
@@ -181,13 +182,15 @@
 
     /**
      * Create an SmsMessage from a raw PDU with the specified message format. The
-     * message format is passed in the {@code SMS_RECEIVED_ACTION} as the {@code format}
+     * message format is passed in the
+     * {@link android.provider.Telephony.Sms.Intents#SMS_RECEIVED_ACTION} as the {@code format}
      * String extra, and will be either "3gpp" for GSM/UMTS/LTE messages in 3GPP format
      * or "3gpp2" for CDMA/LTE messages in 3GPP2 format.
      *
-     * @param pdu the message PDU from the SMS_RECEIVED_ACTION intent
-     * @param format the format extra from the SMS_RECEIVED_ACTION intent
-     * @hide pending API council approval
+     * @param pdu the message PDU from the
+     * {@link android.provider.Telephony.Sms.Intents#SMS_RECEIVED_ACTION} intent
+     * @param format the format extra from the
+     * {@link android.provider.Telephony.Sms.Intents#SMS_RECEIVED_ACTION} intent
      */
     public static SmsMessage createFromPdu(byte[] pdu, String format) {
         SmsMessageBase wrappedMessage;
@@ -388,7 +391,7 @@
                             ted.languageTable, ted.languageShiftTable);
                 }
             } else {  // Assume unicode.
-                nextPos = pos + Math.min(limit / 2, textLen - pos);
+                nextPos = SmsMessageBase.findNextUnicodePosition(pos, limit, newMsgBody);
             }
             if ((nextPos <= pos) || (nextPos > textLen)) {
                 Rlog.e(LOG_TAG, "fragmentText failed (" + pos + " >= " + nextPos + " or " +
diff --git a/src/java/com/android/internal/telephony/BaseCommands.java b/src/java/com/android/internal/telephony/BaseCommands.java
index 45f720c..472004a 100644
--- a/src/java/com/android/internal/telephony/BaseCommands.java
+++ b/src/java/com/android/internal/telephony/BaseCommands.java
@@ -93,6 +93,7 @@
     protected Registrant mGsmBroadcastSmsRegistrant;
     protected Registrant mCatCcAlphaRegistrant;
     protected Registrant mSsRegistrant;
+    protected Registrant mLceInfoRegistrant;
 
     // Preferred network type received from PhoneFactory.
     // This is used when establishing a connection to the
@@ -104,8 +105,6 @@
     protected int mPhoneType;
     // RIL Version
     protected int mRilVersion = -1;
-    // Supported Radio Access Family
-    protected int mSupportedRaf = RadioAccessFamily.RAF_UNKNOWN;
 
     public BaseCommands(Context context) {
         mContext = context;  // May be null (if so we won't log statistics)
@@ -748,14 +747,14 @@
         mRilConnectedRegistrants.remove(h);
     }
 
-     public void registerForSubscriptionStatusChanged(Handler h, int what, Object obj) {
-         Registrant r = new Registrant (h, what, obj);
-         mSubscriptionStatusRegistrants.add(r);
-     }
+    public void registerForSubscriptionStatusChanged(Handler h, int what, Object obj) {
+        Registrant r = new Registrant (h, what, obj);
+        mSubscriptionStatusRegistrants.add(r);
+    }
 
-     public void unregisterForSubscriptionStatusChanged(Handler h) {
-         mSubscriptionStatusRegistrants.remove(h);
-     }
+    public void unregisterForSubscriptionStatusChanged(Handler h) {
+        mSubscriptionStatusRegistrants.remove(h);
+    }
 
     //***** Protected Methods
     /**
@@ -847,11 +846,6 @@
         return mRilVersion;
     }
 
-    @Override
-    public int getSupportedRadioAccessFamily() {
-        return mSupportedRaf;
-    }
-
     public void setUiccSubscription(int slotId, int appIndex, int subId, int subStatus,
             Message response) {
     }
@@ -881,4 +875,29 @@
     public void unregisterForRadioCapabilityChanged(Handler h) {
         mPhoneRadioCapabilityChangedRegistrants.remove(h);
     }
+
+    @Override
+    public void startLceService(int reportIntervalMs, boolean pullMode, Message result) {
+    }
+
+    @Override
+    public void stopLceService(Message result) {
+    }
+
+    @Override
+    public void pullLceData(Message result) {
+    }
+
+    @Override
+    public void registerForLceInfo(Handler h, int what, Object obj) {
+      mLceInfoRegistrant = new Registrant(h, what, obj);
+    }
+
+    @Override
+    public void unregisterForLceInfo(Handler h) {
+      if (mLceInfoRegistrant != null && mLceInfoRegistrant.getHandler() == h) {
+          mLceInfoRegistrant.clear();
+          mLceInfoRegistrant = null;
+      }
+    }
 }
diff --git a/src/java/com/android/internal/telephony/Call.java b/src/java/com/android/internal/telephony/Call.java
index 64c57f2..719b9fd 100644
--- a/src/java/com/android/internal/telephony/Call.java
+++ b/src/java/com/android/internal/telephony/Call.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony;
 
+import android.telecom.ConferenceParticipant;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -121,6 +123,14 @@
     }
 
     /**
+     * getConferenceParticipants
+     * @return List of conference participants.
+     */
+    public List<ConferenceParticipant> getConferenceParticipants() {
+        return null;
+    }
+
+    /**
      * isIdle
      *
      * FIXME rename
diff --git a/src/java/com/android/internal/telephony/CallManager.java b/src/java/com/android/internal/telephony/CallManager.java
index 727be96..3cb00d3 100644
--- a/src/java/com/android/internal/telephony/CallManager.java
+++ b/src/java/com/android/internal/telephony/CallManager.java
@@ -741,7 +741,7 @@
         }
 
         // We only support the AUDIO_ONLY video state in this scenario.
-        ringingPhone.acceptCall(VideoProfile.VideoState.AUDIO_ONLY);
+        ringingPhone.acceptCall(VideoProfile.STATE_AUDIO_ONLY);
 
         if (VDBG) {
             Rlog.d(LOG_TAG, "End acceptCall(" +ringingCall + ")");
@@ -1023,7 +1023,7 @@
      */
     public Connection dial(Phone phone, String dialString, UUSInfo uusInfo, int videoState)
             throws CallStateException {
-        return phone.dial(dialString, uusInfo, videoState);
+        return phone.dial(dialString, uusInfo, videoState, null);
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/CallStateException.java b/src/java/com/android/internal/telephony/CallStateException.java
index 6087124..57cbeb8 100644
--- a/src/java/com/android/internal/telephony/CallStateException.java
+++ b/src/java/com/android/internal/telephony/CallStateException.java
@@ -21,6 +21,13 @@
  */
 public class CallStateException extends Exception
 {
+    private int mError = ERROR_INVALID;
+
+    /** The error code is not valid (Not received a disconnect cause) */
+    public static final int ERROR_INVALID = -1;
+
+    public static final int ERROR_DISCONNECTED = 1;
+
     public
     CallStateException()
     {
@@ -31,4 +38,15 @@
     {
         super(string);
     }
+
+    public
+    CallStateException(int error, String string)
+    {
+        super(string);
+        mError = error;
+    }
+
+    public int getError() {
+        return mError;
+    }
 }
diff --git a/src/java/com/android/internal/telephony/CarrierAppUtils.java b/src/java/com/android/internal/telephony/CarrierAppUtils.java
new file mode 100644
index 0000000..4be914c
--- /dev/null
+++ b/src/java/com/android/internal/telephony/CarrierAppUtils.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.internal.telephony;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.os.RemoteException;
+import android.telephony.TelephonyManager;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utilities for handling carrier applications.
+ * @hide
+ */
+public final class CarrierAppUtils {
+    private static final String TAG = "CarrierAppUtils";
+
+    private static final boolean DEBUG = false; // STOPSHIP if true
+
+    private CarrierAppUtils() {}
+
+    /**
+     * Handle preinstalled carrier apps which should be disabled until a matching SIM is inserted.
+     *
+     * Evaluates the list of applications in config_disabledUntilUsedPreinstalledCarrierApps. We
+     * want to disable each such application which is present on the system image until the user
+     * inserts a SIM which causes that application to gain carrier privilege (indicating a "match"),
+     * without interfering with the user if they opt to enable/disable the app explicitly.
+     *
+     * So, for each such app, we either disable until used IFF the app is not carrier privileged AND
+     * in the default state (e.g. not explicitly DISABLED/DISABLED_BY_USER/ENABLED), or we enable if
+     * the app is carrier privileged and in either the default state or DISABLED_UNTIL_USED.
+     *
+     * When enabling a carrier app we also grant it default permissions.
+     *
+     * This method is idempotent and is safe to be called at any time; it should be called once at
+     * system startup prior to any application running, as well as any time the set of carrier
+     * privileged apps may have changed.
+     */
+    public synchronized static void disableCarrierAppsUntilPrivileged(String callingPackage,
+            IPackageManager packageManager, TelephonyManager telephonyManager, int userId) {
+        if (DEBUG) {
+            Slog.d(TAG, "disableCarrierAppsUntilPrivileged");
+        }
+        String[] systemCarrierAppsDisabledUntilUsed = Resources.getSystem().getStringArray(
+                com.android.internal.R.array.config_disabledUntilUsedPreinstalledCarrierApps);
+        disableCarrierAppsUntilPrivileged(callingPackage, packageManager, telephonyManager, userId,
+                systemCarrierAppsDisabledUntilUsed);
+    }
+
+    // Must be public b/c framework unit tests can't access package-private methods.
+    @VisibleForTesting
+    public static void disableCarrierAppsUntilPrivileged(String callingPackage,
+            IPackageManager packageManager, TelephonyManager telephonyManager, int userId,
+            String[] systemCarrierAppsDisabledUntilUsed) {
+        List<ApplicationInfo> candidates = getDefaultCarrierAppCandidatesHelper(packageManager,
+                userId, systemCarrierAppsDisabledUntilUsed);
+        if (candidates == null || candidates.isEmpty()) {
+            return;
+        }
+
+        List<String> enabledCarrierPackages = new ArrayList<>();
+
+        try {
+            for (ApplicationInfo ai : candidates) {
+                String packageName = ai.packageName;
+                boolean hasPrivileges =
+                        telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName) ==
+                                TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+
+                // Only update enabled state for the app on /system. Once it has been updated we
+                // shouldn't touch it.
+                if (!ai.isUpdatedSystemApp()) {
+                    if (hasPrivileges
+                            && (ai.enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+                            || ai.enabledSetting ==
+                                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) {
+                        Slog.i(TAG, "Update state(" + packageName + "): ENABLED for user "
+                                + userId);
+                        packageManager.setApplicationEnabledSetting(packageName,
+                                PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+                                PackageManager.DONT_KILL_APP, userId, callingPackage);
+                    } else if (!hasPrivileges
+                            && ai.enabledSetting ==
+                                    PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
+                        Slog.i(TAG, "Update state(" + packageName
+                                + "): DISABLED_UNTIL_USED for user " + userId);
+                        packageManager.setApplicationEnabledSetting(packageName,
+                                PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, 0,
+                                userId, callingPackage);
+                    }
+                }
+
+                // Always re-grant default permissions to carrier apps w/ privileges.
+                if (hasPrivileges) {
+                    enabledCarrierPackages.add(ai.packageName);
+                }
+            }
+
+            if (!enabledCarrierPackages.isEmpty()) {
+                // Since we enabled at least one app, ensure we grant default permissions to those
+                // apps.
+                String[] packageNames = new String[enabledCarrierPackages.size()];
+                enabledCarrierPackages.toArray(packageNames);
+                packageManager.grantDefaultPermissionsToEnabledCarrierApps(packageNames, userId);
+            }
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Could not reach PackageManager", e);
+        }
+    }
+
+    /**
+     * Returns the list of "default" carrier apps.
+     *
+     * This is the subset of apps returned by
+     * {@link #getDefaultCarrierAppCandidates(IPackageManager, int)} which currently have carrier
+     * privileges per the SIM(s) inserted in the device.
+     */
+    public static List<ApplicationInfo> getDefaultCarrierApps(IPackageManager packageManager,
+            TelephonyManager telephonyManager, int userId) {
+        // Get all system apps from the default list.
+        List<ApplicationInfo> candidates = getDefaultCarrierAppCandidates(packageManager, userId);
+        if (candidates == null || candidates.isEmpty()) {
+            return null;
+        }
+
+        // Filter out apps without carrier privileges.
+        // Iterate from the end to avoid creating an Iterator object and because we will be removing
+        // elements from the list as we pass through it.
+        for (int i = candidates.size() - 1; i >= 0; i--) {
+            ApplicationInfo ai = candidates.get(i);
+            String packageName = ai.packageName;
+            boolean hasPrivileges =
+                    telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName) ==
+                            TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+            if (!hasPrivileges) {
+                candidates.remove(i);
+            }
+        }
+
+        return candidates;
+    }
+
+    /**
+     * Returns the list of "default" carrier app candidates.
+     *
+     * These are the apps subject to the hiding/showing logic in
+     * {@link CarrierAppUtils#disableCarrierAppsUntilPrivileged(String, IPackageManager,
+     * TelephonyManager, int)}, as well as the apps which should have default permissions granted,
+     * when a matching SIM is inserted.
+     *
+     * Whether or not the app is actually considered a default app depends on whether the app has
+     * carrier privileges as determined by the SIMs in the device.
+     */
+    public static List<ApplicationInfo> getDefaultCarrierAppCandidates(
+            IPackageManager packageManager, int userId) {
+        String[] systemCarrierAppsDisabledUntilUsed = Resources.getSystem().getStringArray(
+                com.android.internal.R.array.config_disabledUntilUsedPreinstalledCarrierApps);
+        return getDefaultCarrierAppCandidatesHelper(packageManager, userId,
+                systemCarrierAppsDisabledUntilUsed);
+    }
+
+    private static List<ApplicationInfo> getDefaultCarrierAppCandidatesHelper(
+            IPackageManager packageManager, int userId,
+            String[] systemCarrierAppsDisabledUntilUsed) {
+        if (systemCarrierAppsDisabledUntilUsed == null
+                || systemCarrierAppsDisabledUntilUsed.length == 0) {
+            return null;
+        }
+        List<ApplicationInfo> apps = null;
+        try {
+            apps = new ArrayList<>(systemCarrierAppsDisabledUntilUsed.length);
+            for (String packageName : systemCarrierAppsDisabledUntilUsed) {
+                ApplicationInfo ai = packageManager.getApplicationInfo(packageName,
+                        PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, userId);
+                if (ai == null) {
+                    // No app found for packageName
+                    continue;
+                }
+                if (!ai.isSystemApp()) {
+                    continue;
+                }
+                apps.add(ai);
+            }
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Could not reach PackageManager", e);
+        }
+        return apps;
+    }
+}
diff --git a/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java b/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
new file mode 100644
index 0000000..18e1554
--- /dev/null
+++ b/src/java/com/android/internal/telephony/CarrierServiceBindHelper.java
@@ -0,0 +1,306 @@
+/*
+* Copyright (C) 2015 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.android.internal.telephony;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.UserHandle;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.service.carrier.CarrierService;
+
+import com.android.internal.telephony.IccCardConstants;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Manages long-lived bindings to carrier services
+ * @hide
+ */
+public class CarrierServiceBindHelper {
+    private static final String LOG_TAG = CarrierServiceBindHelper.class.getSimpleName();
+
+    private Context mContext;
+    private AppBinding[] mBindings;
+    private final BroadcastReceiver mReceiver = new PackageChangedBroadcastReceiver();
+
+    private static final int EVENT_BIND = 0;
+    private static final int EVENT_UNBIND = 1;
+    private static final int EVENT_BIND_TIMEOUT = 2;
+    private static final int EVENT_PACKAGE_CHANGED = 3;
+
+    private static final int BIND_TIMEOUT_MILLIS = 10000;
+
+    private Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            String carrierPackageName;
+            AppBinding binding;
+            log("mHandler: " + msg.what);
+
+            CarrierServiceConnection connection;
+            switch (msg.what) {
+                case EVENT_BIND:
+                    binding = (AppBinding) msg.obj;
+                    log("Binding to phoneId: " + binding.getPhoneId());
+                    binding.bind();
+                    break;
+                case EVENT_BIND_TIMEOUT:
+                    binding = (AppBinding) msg.obj;
+                    log("Bind timeout for phoneId: " + binding.getPhoneId());
+                    binding.unbind();
+                    break;
+                case EVENT_UNBIND:
+                    binding = (AppBinding) msg.obj;
+                    log("Unbinding for phoneId: " + binding.getPhoneId());
+                    binding.unbind();
+                    break;
+                case EVENT_PACKAGE_CHANGED:
+                    carrierPackageName = (String) msg.obj;
+                    for (AppBinding appBinding : mBindings) {
+                        if (carrierPackageName.equals(appBinding.getPackage())) {
+                          log(carrierPackageName + " changed and corresponds to a phone. Rebinding.");
+                          appBinding.bind();
+                        }
+                    }
+                    break;
+            }
+        }
+    };
+
+    public CarrierServiceBindHelper(Context context) {
+        mContext = context;
+
+        int numPhones = TelephonyManager.from(context).getPhoneCount();
+        mBindings = new AppBinding[numPhones];
+
+        for (int phoneId = 0; phoneId < numPhones; phoneId++) {
+            mBindings[phoneId] = new AppBinding(phoneId);
+        }
+
+        // Register for package updates. Update app or uninstall app update will have all 3 intents,
+        // in the order or removed, added, replaced, all with extra_replace set to true.
+        IntentFilter pkgFilter = new IntentFilter();
+        pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        pkgFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+        pkgFilter.addDataScheme("package");
+        context.registerReceiverAsUser(mReceiver, UserHandle.ALL, pkgFilter, null, null);
+    }
+
+    public void updateForPhoneId(int phoneId, String simState) {
+        log("update binding for phoneId: " + phoneId + " simState: " + simState);
+        if (!SubscriptionManager.isValidPhoneId(phoneId)) {
+            return;
+        }
+        // requires Java 7 for switch on string.
+        switch (simState) {
+            case IccCardConstants.INTENT_VALUE_ICC_ABSENT:
+            case IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR:
+            case IccCardConstants.INTENT_VALUE_ICC_UNKNOWN:
+                mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNBIND, mBindings[phoneId]));
+                break;
+            case IccCardConstants.INTENT_VALUE_ICC_LOADED:
+            case IccCardConstants.INTENT_VALUE_ICC_LOCKED:
+                mHandler.sendMessage(mHandler.obtainMessage(EVENT_BIND, mBindings[phoneId]));
+                break;
+        }
+    }
+
+    private class AppBinding {
+        private int phoneId;
+        private CarrierServiceConnection connection;
+        private int bindCount;
+        private long lastBindStartMillis;
+        private int unbindCount;
+        private long lastUnbindMillis;
+        private String carrierPackage;
+
+        public AppBinding(int phoneId) {
+            this.phoneId = phoneId;
+        }
+
+        public int getPhoneId() {
+            return phoneId;
+        }
+
+        public String getPackage() {
+            return carrierPackage;
+        }
+
+        public void handleConnectionDown() {
+            connection = null;
+        }
+
+        public boolean bind() {
+            // Make sure there is no existing binding for this phone
+            unbind();
+
+            // Get the package name for the carrier app
+            List<String> carrierPackageNames =
+                TelephonyManager.from(mContext).getCarrierPackageNamesForIntentAndPhone(
+                    new Intent(CarrierService.CARRIER_SERVICE_INTERFACE), phoneId
+                );
+
+            if (carrierPackageNames == null || carrierPackageNames.size() <= 0) {
+                log("No carrier app for: " + phoneId);
+                return false;
+            }
+
+            log("Found carrier app: " + carrierPackageNames);
+            carrierPackage = carrierPackageNames.get(0);
+
+            // Log debug information
+            bindCount++;
+            lastBindStartMillis = System.currentTimeMillis();
+
+            // Look up the carrier service
+            Intent carrierService = new Intent(CarrierService.CARRIER_SERVICE_INTERFACE);
+            carrierService.setPackage(carrierPackage);
+
+            ResolveInfo carrierResolveInfo = mContext.getPackageManager().resolveService(
+                carrierService, PackageManager.GET_META_DATA);
+            Bundle metadata = null;
+            if (carrierResolveInfo != null) {
+              metadata = carrierResolveInfo.serviceInfo.metaData;
+            }
+
+            // Only bind if the service wants it
+            if (metadata == null ||
+                !metadata.getBoolean("android.service.carrier.LONG_LIVED_BINDING", false)) {
+                log("Carrier app does not want a long lived binding");
+                return false;
+            }
+
+            log("Binding to " + carrierPackage + " for phone " + phoneId);
+            connection = new CarrierServiceConnection(this);
+            mHandler.sendMessageDelayed(
+                mHandler.obtainMessage(EVENT_BIND_TIMEOUT, this),
+                BIND_TIMEOUT_MILLIS);
+
+            String error;
+            try {
+                if (mContext.bindService(carrierService, connection, Context.BIND_AUTO_CREATE)) {
+                    return true;
+                }
+
+                error = "bindService returned false";
+            } catch (SecurityException ex) {
+                error = ex.getMessage();
+            }
+
+            log("Unable to bind to " + carrierPackage + " for phone " + phoneId +
+                ". Error: " + error);
+            return false;
+        }
+
+        public void unbind() {
+            mHandler.removeMessages(EVENT_BIND_TIMEOUT, this);
+            if (connection == null) {
+                return;
+            }
+
+            // Log debug information
+            unbindCount++;
+            lastUnbindMillis = System.currentTimeMillis();
+
+            // Actually unbind
+            log("Unbinding from carrier app");
+            mContext.unbindService(connection);
+            connection = null;
+        }
+
+        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            pw.println("Carrier app binding for phone " + phoneId);
+            pw.println("  connection: " + connection);
+            pw.println("  bindCount: " + bindCount);
+            pw.println("  lastBindStartMillis: " + lastBindStartMillis);
+            pw.println("  unbindCount: " + unbindCount);
+            pw.println("  lastUnbindMillis: " + lastUnbindMillis);
+            pw.println();
+        }
+    }
+
+    private class CarrierServiceConnection implements ServiceConnection {
+        private IBinder service;
+        private AppBinding binding;
+
+        public CarrierServiceConnection(AppBinding binding) {
+            this.binding = binding;
+        }
+
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            log("Connected to carrier app: " + name.flattenToString());
+            mHandler.removeMessages(EVENT_BIND_TIMEOUT, binding);
+            this.service = service;
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            log("Disconnected from carrier app: " + name.flattenToString());
+            this.service = null;
+            this.binding.handleConnectionDown();
+        }
+    }
+
+    private class PackageChangedBroadcastReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            log("Receive action: " + action);
+            switch (action) {
+                case Intent.ACTION_PACKAGE_ADDED:
+                case Intent.ACTION_PACKAGE_REMOVED:
+                case Intent.ACTION_PACKAGE_REPLACED:
+                    int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+                    String packageName = mContext.getPackageManager().getNameForUid(uid);
+                    if (packageName != null) {
+                      // We don't have a phoneId for arg1.
+                      mHandler.sendMessage(
+                              mHandler.obtainMessage(EVENT_PACKAGE_CHANGED, packageName));
+                    }
+                    break;
+
+            }
+        }
+    }
+
+    private static void log(String message) {
+        Log.d(LOG_TAG, message);
+    }
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("CarrierServiceBindHelper:");
+        for (AppBinding binding : mBindings) {
+            binding.dump(fd, pw, args);
+        }
+    }
+}
diff --git a/src/java/com/android/internal/telephony/CommandsInterface.java b/src/java/com/android/internal/telephony/CommandsInterface.java
index 42b0338..fd8081b 100644
--- a/src/java/com/android/internal/telephony/CommandsInterface.java
+++ b/src/java/com/android/internal/telephony/CommandsInterface.java
@@ -1898,11 +1898,6 @@
      */
     int getRilVersion();
 
-    /**
-     * @return Radio access families supported by the hardware.
-     */
-    int getSupportedRadioAccessFamily();
-
    /**
      * Sets user selected subscription at Modem.
      *
@@ -1971,4 +1966,56 @@
      * @param h Handler to be removed from the registrant list.
      */
     public void unregisterForRadioCapabilityChanged(Handler h);
+
+    /**
+     * Start LCE (Link Capacity Estimation) service with a desired reporting interval.
+     *
+     * @param reportIntervalMs
+     *        LCE info reporting interval (ms).
+     *
+     * @param result Callback message contains the current LCE status.
+     * {byte status, int actualIntervalMs}
+     */
+    public void startLceService(int reportIntervalMs, boolean pullMode, Message result);
+
+    /**
+     * Stop LCE service.
+     *
+     * @param result Callback message contains the current LCE status:
+     * {byte status, int actualIntervalMs}
+     *
+     */
+    public void stopLceService(Message result);
+
+    /**
+     * Pull LCE service for capacity data.
+     *
+     * @param result Callback message contains the capacity info:
+     * {int capacityKbps, byte confidenceLevel, byte lceSuspendedTemporarily}
+     */
+    public void pullLceData(Message result);
+
+    /**
+     * Register a LCE info listener.
+     *
+     * @param h Handler for notification message.
+     * @param what User-defined message code.
+     * @param obj User object.
+     */
+    void registerForLceInfo(Handler h, int what, Object obj);
+
+    /**
+     * Unregister the LCE Info listener.
+     *
+     * @param h handle to be removed.
+     */
+    void unregisterForLceInfo(Handler h);
+
+    /**
+     *
+     * Get modem activity info and stats
+     *
+     * @param result Callback message contains the modem activity information
+     */
+    public void getModemActivityInfo(Message result);
 }
diff --git a/src/java/com/android/internal/telephony/Connection.java b/src/java/com/android/internal/telephony/Connection.java
index 79aebec..8edf67a 100644
--- a/src/java/com/android/internal/telephony/Connection.java
+++ b/src/java/com/android/internal/telephony/Connection.java
@@ -45,10 +45,14 @@
         public void onVideoStateChanged(int videoState);
         public void onLocalVideoCapabilityChanged(boolean capable);
         public void onRemoteVideoCapabilityChanged(boolean capable);
+        public void onWifiChanged(boolean isWifi);
         public void onVideoProviderChanged(
                 android.telecom.Connection.VideoProvider videoProvider);
         public void onAudioQualityChanged(int audioQuality);
         public void onConferenceParticipantsChanged(List<ConferenceParticipant> participants);
+        public void onCallSubstateChanged(int callSubstate);
+        public void onMultipartyStateChanged(boolean isMultiParty);
+        public void onConferenceMergedFailed();
     }
 
     /**
@@ -62,12 +66,20 @@
         @Override
         public void onRemoteVideoCapabilityChanged(boolean capable) {}
         @Override
+        public void onWifiChanged(boolean isWifi) {}
+        @Override
         public void onVideoProviderChanged(
                 android.telecom.Connection.VideoProvider videoProvider) {}
         @Override
         public void onAudioQualityChanged(int audioQuality) {}
         @Override
         public void onConferenceParticipantsChanged(List<ConferenceParticipant> participants) {}
+        @Override
+        public void onCallSubstateChanged(int callSubstate) {}
+        @Override
+        public void onMultipartyStateChanged(boolean isMultiParty) {}
+        @Override
+        public void onConferenceMergedFailed() {}
     }
 
     public static final int AUDIO_QUALITY_STANDARD = 1;
@@ -108,7 +120,9 @@
     private int mVideoState;
     private boolean mLocalVideoCapable;
     private boolean mRemoteVideoCapable;
+    private boolean mIsWifi;
     private int mAudioQuality;
+    private int mCallSubstate;
     private android.telecom.Connection.VideoProvider mVideoProvider;
     public Call.State mPreHandoverState = Call.State.IDLE;
 
@@ -242,6 +256,15 @@
     public abstract int getDisconnectCause();
 
     /**
+     * Returns a string disconnect cause which is from vendor.
+     * Vendors may use this string to explain the underline causes of failed calls.
+     * There is no guarantee that it is non-null nor it'll have meaningful stable values.
+     * Only use it when getDisconnectCause() returns a value that is not specific enough, like
+     * ERROR_UNSPECIFIED.
+     */
+    public abstract String getVendorDisconnectCause();
+
+    /**
      * Returns true of this connection originated elsewhere
      * ("MT" or mobile terminated; another party called this terminal)
      * or false if this call originated here (MO or mobile originated).
@@ -278,6 +301,22 @@
     }
 
     /**
+     * Get the details of conference participants. Expected to be
+     * overwritten by the Connection subclasses.
+     */
+    public List<ConferenceParticipant> getConferenceParticipants() {
+        Call c;
+
+        c = getCall();
+
+        if (c == null) {
+            return null;
+        } else {
+            return c.getConferenceParticipants();
+        }
+    }
+
+    /**
      * isAlive()
      *
      * @return true if the connection isn't disconnected
@@ -350,6 +389,10 @@
         }
     }
 
+    public final void removePostDialListener(PostDialListener listener) {
+        mPostDialListeners.remove(listener);
+    }
+
     protected final void clearPostDialListeners() {
         mPostDialListeners.clear();
     }
@@ -487,6 +530,15 @@
     }
 
     /**
+     * Returns whether the connection is using a wifi network.
+     *
+     * @return {@code True} if the connection is using a wifi network.
+     */
+    public boolean isWifi() {
+        return mIsWifi;
+    }
+
+    /**
      * Returns the {@link android.telecom.Connection.VideoProvider} for the connection.
      *
      * @return The {@link android.telecom.Connection.VideoProvider}.
@@ -504,6 +556,17 @@
         return mAudioQuality;
     }
 
+
+    /**
+     * Returns the current call substate of the connection.
+     *
+     * @return The call substate of the connection.
+     */
+    public int getCallSubstate() {
+        return mCallSubstate;
+    }
+
+
     /**
      * Sets the videoState for the current connection and reports the changes to all listeners.
      * Valid video states are defined in {@link android.telecom.VideoProfile}.
@@ -542,6 +605,18 @@
     }
 
     /**
+     * Sets whether a wifi network is used for the connection.
+     *
+     * @param isWifi {@code True} if wifi is being used.
+     */
+    public void setWifi(boolean isWifi) {
+        mIsWifi = isWifi;
+        for (Listener l : mListeners) {
+            l.onWifiChanged(mIsWifi);
+        }
+    }
+
+    /**
      * Set the audio quality for the connection.
      *
      * @param audioQuality The audio quality.
@@ -554,6 +629,19 @@
     }
 
     /**
+     * Sets the call substate for the current connection and reports the changes to all listeners.
+     * Valid call substates are defined in {@link android.telecom.Connection}.
+     *
+     * @return The call substate.
+     */
+    public void setCallSubstate(int callSubstate) {
+        mCallSubstate = callSubstate;
+        for (Listener l : mListeners) {
+            l.onCallSubstateChanged(mCallSubstate);
+        }
+    }
+
+    /**
      * Sets the {@link android.telecom.Connection.VideoProvider} for the connection.
      *
      * @param videoProvider The video call provider.
@@ -584,6 +672,26 @@
     }
 
     /**
+     * Notifies listeners of a change to the multiparty state of the connection.
+     *
+     * @param isMultiparty The participant(s).
+     */
+    public void updateMultipartyState(boolean isMultiparty) {
+        for (Listener l : mListeners) {
+            l.onMultipartyStateChanged(isMultiparty);
+        }
+    }
+
+    /**
+     * Notifies listeners of a failure in merging this connection with the background connection.
+     */
+    public void onConferenceMergeFailed() {
+        for (Listener l : mListeners) {
+            l.onConferenceMergedFailed();
+        }
+    }
+
+    /**
      * Notifies this Connection of a request to disconnect a participant of the conference managed
      * by the connection.
      *
diff --git a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
index 9be78fc..8550876 100644
--- a/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
+++ b/src/java/com/android/internal/telephony/IccSmsInterfaceManager.java
@@ -317,6 +317,32 @@
     }
 
     /**
+     * A permissions check before passing to {@link IccSmsInterfaceManager#sendDataInternal}.
+     * This method checks if the calling package or itself has the permission to send the data sms.
+     */
+    public void sendDataWithSelfPermissions(String callingPackage, String destAddr, String scAddr,
+            int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+        mPhone.getContext().enforceCallingOrSelfPermission(
+                Manifest.permission.SEND_SMS,
+                "Sending SMS message");
+        sendDataInternal(callingPackage, destAddr, scAddr, destPort, data, sentIntent,
+                deliveryIntent);
+    }
+
+    /**
+     * A permissions check before passing to {@link IccSmsInterfaceManager#sendDataInternal}.
+     * This method checks only if the calling package has the permission to send the data sms.
+     */
+    public void sendData(String callingPackage, String destAddr, String scAddr, int destPort,
+            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+        mPhone.getContext().enforceCallingPermission(
+                Manifest.permission.SEND_SMS,
+                "Sending SMS message");
+        sendDataInternal(callingPackage, destAddr, scAddr, destPort, data, sentIntent,
+                deliveryIntent);
+    }
+
+    /**
      * Send a data based SMS to a specific application port.
      *
      * @param destAddr the address to send the message to
@@ -342,11 +368,8 @@
      *  raw pdu of the status report is in the extended data ("pdu").
      */
 
-    public void sendData(String callingPackage, String destAddr, String scAddr, int destPort,
-            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
-        mPhone.getContext().enforceCallingPermission(
-                Manifest.permission.SEND_SMS,
-                "Sending SMS message");
+    private void sendDataInternal(String callingPackage, String destAddr, String scAddr,
+            int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
         if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
             log("sendData: destAddr=" + destAddr + " scAddr=" + scAddr + " destPort=" +
                 destPort + " data='"+ HexDump.toHexString(data)  + "' sentIntent=" +
@@ -361,6 +384,33 @@
     }
 
     /**
+     * A permissions check before passing to {@link IccSmsInterfaceManager#sendTextInternal}.
+     * This method checks only if the calling package has the permission to send the sms.
+     */
+    public void sendText(String callingPackage, String destAddr, String scAddr,
+            String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
+            boolean persistMessageForNonDefaultSmsApp) {
+        mPhone.getContext().enforceCallingPermission(
+                Manifest.permission.SEND_SMS,
+                "Sending SMS message");
+        sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent,
+            persistMessageForNonDefaultSmsApp);
+    }
+
+    /**
+     * A permissions check before passing to {@link IccSmsInterfaceManager#sendTextInternal}.
+     * This method checks if the calling package or itself has the permission to send the sms.
+     */
+    public void sendTextWithSelfPermissions(String callingPackage, String destAddr, String scAddr,
+            String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+        mPhone.getContext().enforceCallingOrSelfPermission(
+                Manifest.permission.SEND_SMS,
+                "Sending SMS message");
+        sendTextInternal(callingPackage, destAddr, scAddr, text, sentIntent, deliveryIntent,
+            true /* persistMessageForNonDefaultSmsApp */);
+    }
+
+    /**
      * Send a text based SMS.
      *
      * @param destAddr the address to send the message to
@@ -385,11 +435,9 @@
      *  raw pdu of the status report is in the extended data ("pdu").
      */
 
-    public void sendText(String callingPackage, String destAddr, String scAddr,
-            String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
-        mPhone.getContext().enforceCallingPermission(
-                Manifest.permission.SEND_SMS,
-                "Sending SMS message");
+    private void sendTextInternal(String callingPackage, String destAddr, String scAddr,
+            String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
+            boolean persistMessageForNonDefaultSmsApp) {
         if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
             log("sendText: destAddr=" + destAddr + " scAddr=" + scAddr +
                 " text='"+ text + "' sentIntent=" +
@@ -399,9 +447,13 @@
                 callingPackage) != AppOpsManager.MODE_ALLOWED) {
             return;
         }
+        if (!persistMessageForNonDefaultSmsApp) {
+            // Only allow carrier app to skip auto message persistence.
+            enforceCarrierPrivilege();
+        }
         destAddr = filterDestAddress(destAddr);
         mDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
-                null/*messageUri*/, callingPackage);
+                null/*messageUri*/, callingPackage, persistMessageForNonDefaultSmsApp);
     }
 
     /**
@@ -452,10 +504,14 @@
 
     public void sendMultipartText(String callingPackage, String destAddr, String scAddr,
             List<String> parts, List<PendingIntent> sentIntents,
-            List<PendingIntent> deliveryIntents) {
+            List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp) {
         mPhone.getContext().enforceCallingPermission(
                 Manifest.permission.SEND_SMS,
                 "Sending SMS message");
+        if (!persistMessageForNonDefaultSmsApp) {
+            // Only allow carrier app to skip auto message persistence.
+            enforceCarrierPrivilege();
+        }
         if (Rlog.isLoggable("SMS", Log.VERBOSE)) {
             int i = 0;
             for (String part : parts) {
@@ -493,14 +549,15 @@
 
                 mDispatcher.sendText(destAddr, scAddr, singlePart,
                         singleSentIntent, singleDeliveryIntent,
-                        null/*messageUri*/, callingPackage);
+                        null/*messageUri*/, callingPackage,
+                        persistMessageForNonDefaultSmsApp);
             }
             return;
         }
 
         mDispatcher.sendMultipartText(destAddr, scAddr, (ArrayList<String>) parts,
                 (ArrayList<PendingIntent>) sentIntents, (ArrayList<PendingIntent>) deliveryIntents,
-                null/*messageUri*/, callingPackage);
+                null/*messageUri*/, callingPackage, persistMessageForNonDefaultSmsApp);
     }
 
 
@@ -900,7 +957,8 @@
         }
         textAndAddress[1] = filterDestAddress(textAndAddress[1]);
         mDispatcher.sendText(textAndAddress[1], scAddress, textAndAddress[0],
-                sentIntent, deliveryIntent, messageUri, callingPkg);
+                sentIntent, deliveryIntent, messageUri, callingPkg,
+                true /* persistMessageForNonDefaultSmsApp */);
     }
 
     public void sendStoredMultipartText(String callingPkg, Uri messageUri, String scAddress,
@@ -955,7 +1013,8 @@
                 }
 
                 mDispatcher.sendText(textAndAddress[1], scAddress, singlePart,
-                        singleSentIntent, singleDeliveryIntent, messageUri, callingPkg);
+                        singleSentIntent, singleDeliveryIntent, messageUri, callingPkg,
+                        true  /* persistMessageForNonDefaultSmsApp */);
             }
             return;
         }
@@ -967,7 +1026,8 @@
                 (ArrayList<PendingIntent>) sentIntents,
                 (ArrayList<PendingIntent>) deliveryIntents,
                 messageUri,
-                callingPkg);
+                callingPkg,
+                true  /* persistMessageForNonDefaultSmsApp */);
     }
 
     private boolean isFailedOrDraft(ContentResolver resolver, Uri messageUri) {
diff --git a/src/java/com/android/internal/telephony/ImsSMSDispatcher.java b/src/java/com/android/internal/telephony/ImsSMSDispatcher.java
index 9ad7169..13ada85 100644
--- a/src/java/com/android/internal/telephony/ImsSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/ImsSMSDispatcher.java
@@ -172,13 +172,14 @@
     @Override
     protected void sendMultipartText(String destAddr, String scAddr,
             ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
-            ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg) {
+            ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg,
+            boolean persistMessage) {
         if (isCdmaMo()) {
             mCdmaDispatcher.sendMultipartText(destAddr, scAddr,
-                    parts, sentIntents, deliveryIntents, messageUri, callingPkg);
+                    parts, sentIntents, deliveryIntents, messageUri, callingPkg, persistMessage);
         } else {
             mGsmDispatcher.sendMultipartText(destAddr, scAddr,
-                    parts, sentIntents, deliveryIntents, messageUri, callingPkg);
+                    parts, sentIntents, deliveryIntents, messageUri, callingPkg, persistMessage);
         }
     }
 
@@ -197,14 +198,15 @@
 
     @Override
     protected void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent,
-            PendingIntent deliveryIntent, Uri messageUri, String callingPkg) {
+            PendingIntent deliveryIntent, Uri messageUri, String callingPkg,
+            boolean persistMessage) {
         Rlog.d(TAG, "sendText");
         if (isCdmaMo()) {
             mCdmaDispatcher.sendText(destAddr, scAddr,
-                    text, sentIntent, deliveryIntent, messageUri, callingPkg);
+                    text, sentIntent, deliveryIntent, messageUri, callingPkg, persistMessage);
         } else {
             mGsmDispatcher.sendText(destAddr, scAddr,
-                    text, sentIntent, deliveryIntent, messageUri, callingPkg);
+                    text, sentIntent, deliveryIntent, messageUri, callingPkg, persistMessage);
         }
     }
 
diff --git a/src/java/com/android/internal/telephony/InboundSmsHandler.java b/src/java/com/android/internal/telephony/InboundSmsHandler.java
index 0387555..b4cce2c 100644
--- a/src/java/com/android/internal/telephony/InboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/InboundSmsHandler.java
@@ -21,6 +21,7 @@
 import android.app.Activity;
 import android.app.ActivityManagerNative;
 import android.app.AppOpsManager;
+import android.app.BroadcastOptions;
 import android.app.PendingIntent;
 import android.app.PendingIntent.CanceledException;
 import android.content.BroadcastReceiver;
@@ -42,9 +43,12 @@
 import android.os.AsyncResult;
 import android.os.Binder;
 import android.os.Build;
+import android.os.Bundle;
+import android.os.IDeviceIdleController;
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -187,6 +191,8 @@
 
     private UserManager mUserManager;
 
+    IDeviceIdleController mDeviceIdleController;
+
     /**
      * Create a new SMS broadcast helper.
      * @param name the class name for logging
@@ -213,6 +219,8 @@
         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name);
         mWakeLock.acquire();    // wake lock released after we enter idle state
         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
+                ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
 
         addState(mDefaultState);
         addState(mStartupState, mDefaultState);
@@ -407,8 +415,17 @@
 
                 case EVENT_BROADCAST_SMS:
                     // if any broadcasts were sent, transition to waiting state
-                    if (processMessagePart((InboundSmsTracker) msg.obj)) {
+                    InboundSmsTracker inboundSmsTracker = (InboundSmsTracker) msg.obj;
+                    if (processMessagePart(inboundSmsTracker)) {
                         transitionTo(mWaitingState);
+                    } else {
+                        // if event is sent from SmsBroadcastUndelivered.broadcastSms(), and
+                        // processMessagePart() returns false, the state machine will be stuck in
+                        // DeliveringState until next message is received. Send message to
+                        // transition to idle to avoid that so that wakelock can be released
+                        log("No broadcast sent on processing EVENT_BROADCAST_SMS in Delivering " +
+                                "state. Return to Idle state");
+                        sendMessage(EVENT_RETURN_TO_IDLE);
                     }
                     return HANDLED;
 
@@ -746,7 +763,12 @@
             int result = mWapPush.dispatchWapPdu(output.toByteArray(), resultReceiver, this);
             if (DBG) log("dispatchWapPdu() returned " + result);
             // result is Activity.RESULT_OK if an ordered broadcast was sent
-            return (result == Activity.RESULT_OK);
+            if (result == Activity.RESULT_OK) {
+                return true;
+            } else {
+                deleteFromRawTable(tracker.getDeleteWhere(), tracker.getDeleteWhereArgs());
+                return false;
+            }
         }
 
         List<String> carrierPackages = null;
@@ -814,7 +836,7 @@
      * @param user user to deliver the intent to
      */
     protected void dispatchIntent(Intent intent, String permission, int appOp,
-            BroadcastReceiver resultReceiver, UserHandle user) {
+            Bundle opts, BroadcastReceiver resultReceiver, UserHandle user) {
         intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
         SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
         if (user.equals(UserHandle.ALL)) {
@@ -843,14 +865,13 @@
                     }
                 }
                 // Only pass in the resultReceiver when the USER_OWNER is processed.
-                mContext.sendOrderedBroadcastAsUser(intent, targetUser, permission, appOp,
+                mContext.sendOrderedBroadcastAsUser(intent, targetUser, permission, appOp, opts,
                         users[i] == UserHandle.USER_OWNER ? resultReceiver : null,
                         getHandler(), Activity.RESULT_OK, null, null);
             }
         } else {
-            mContext.sendOrderedBroadcastAsUser(intent, user, permission, appOp,
-                    resultReceiver,
-                    getHandler(), Activity.RESULT_OK, null, null);
+            mContext.sendOrderedBroadcastAsUser(intent, user, permission, appOp, opts,
+                    resultReceiver, getHandler(), Activity.RESULT_OK, null, null);
         }
     }
 
@@ -866,6 +887,28 @@
         }
     }
 
+    Bundle handleSmsWhitelisting(ComponentName target) {
+        String pkgName;
+        String reason;
+        if (target != null) {
+            pkgName = target.getPackageName();
+            reason = "sms-app";
+        } else {
+            pkgName = mContext.getPackageName();
+            reason = "sms-broadcast";
+        }
+        try {
+            long duration = mDeviceIdleController.addPowerSaveTempWhitelistAppForSms(
+                    pkgName, 0, reason);
+            BroadcastOptions bopts = BroadcastOptions.makeBasic();
+            bopts.setTemporaryAppWhitelistDuration(duration);
+            return bopts.toBundle();
+        } catch (RemoteException e) {
+        }
+
+        return null;
+    }
+
     /**
      * Creates and dispatches the intent to the default SMS app or the appropriate port.
      *
@@ -910,8 +953,9 @@
             intent.setComponent(null);
         }
 
+        Bundle options = handleSmsWhitelisting(intent.getComponent());
         dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
-                AppOpsManager.OP_RECEIVE_SMS, resultReceiver, UserHandle.OWNER);
+                AppOpsManager.OP_RECEIVE_SMS, options, resultReceiver, UserHandle.OWNER);
     }
 
     /**
@@ -1040,16 +1084,26 @@
                 intent.setAction(Intents.SMS_RECEIVED_ACTION);
                 intent.setComponent(null);
                 // All running users will be notified of the received sms.
+                Bundle options = handleSmsWhitelisting(null);
                 dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
-                        AppOpsManager.OP_RECEIVE_SMS, this, UserHandle.ALL);
+                        AppOpsManager.OP_RECEIVE_SMS, options, this, UserHandle.ALL);
             } else if (action.equals(Intents.WAP_PUSH_DELIVER_ACTION)) {
                 // Now dispatch the notification only intent
                 intent.setAction(Intents.WAP_PUSH_RECEIVED_ACTION);
                 intent.setComponent(null);
                 // Only the primary user will receive notification of incoming mms.
                 // That app will do the actual downloading of the mms.
+                Bundle options = null;
+                try {
+                    long duration = mDeviceIdleController.addPowerSaveTempWhitelistAppForMms(
+                            mContext.getPackageName(), 0, "mms-broadcast");
+                    BroadcastOptions bopts = BroadcastOptions.makeBasic();
+                    bopts.setTemporaryAppWhitelistDuration(duration);
+                    options = bopts.toBundle();
+                } catch (RemoteException e) {
+                }
                 dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS,
-                        AppOpsManager.OP_RECEIVE_SMS, this, UserHandle.OWNER);
+                        AppOpsManager.OP_RECEIVE_SMS, options, this, UserHandle.OWNER);
             } else {
                 // Now that the intents have been deleted we can clean up the PDU data.
                 if (!Intents.DATA_SMS_RECEIVED_ACTION.equals(action)
diff --git a/src/java/com/android/internal/telephony/LastCallFailCause.java b/src/java/com/android/internal/telephony/LastCallFailCause.java
new file mode 100644
index 0000000..d06eee6
--- /dev/null
+++ b/src/java/com/android/internal/telephony/LastCallFailCause.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+public class LastCallFailCause {
+  public int causeCode;
+  public String vendorCause;
+
+    @Override
+    public String toString() {
+        return super.toString()
+            + " causeCode: " + causeCode
+            + " vendorCause: " + vendorCause;
+    }
+}
diff --git a/src/java/com/android/internal/telephony/MccTable.java b/src/java/com/android/internal/telephony/MccTable.java
index 33a77ff..cccd142 100644
--- a/src/java/com/android/internal/telephony/MccTable.java
+++ b/src/java/com/android/internal/telephony/MccTable.java
@@ -24,7 +24,6 @@
 import android.os.Build;
 import android.os.RemoteException;
 import android.os.SystemProperties;
-import android.provider.Settings;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Slog;
@@ -32,8 +31,11 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
+
 import libcore.icu.ICU;
 import libcore.icu.TimeZoneNames;
 
@@ -184,11 +186,8 @@
             }
 
             Slog.d(LOG_TAG, "updateMccMncConfiguration: mcc=" + mcc + ", mnc=" + mnc);
-
-            Locale locale = null;
             if (mcc != 0) {
                 setTimezoneFromMccIfNeeded(context, mcc);
-                locale = getLocaleFromMcc(context, mcc);
             }
             if (fromServiceState) {
                 setWifiCountryCodeFromMcc(context, mcc);
@@ -202,10 +201,7 @@
                         config.mnc = mnc == 0 ? Configuration.MNC_ZERO : mnc;
                         updateConfig = true;
                     }
-                    if (locale != null) {
-                        config.setLocale(locale);
-                        updateConfig = true;
-                    }
+
                     if (updateConfig) {
                         Slog.d(LOG_TAG, "updateMccMncConfiguration updateConfig config=" + config);
                         ActivityManagerNative.getDefault().updateConfiguration(config);
@@ -224,26 +220,39 @@
         }
     }
 
-    // Bug 19232829: It is possible to get through provisioning without setting up a persistent
-    // locale value. We don't modify the locale if the device has completed "provisioning" because
-    // we don't want to change the locale if the user inserts a new SIM or a new version of Android
-    // is better at recognizing MCC values than an older version.
-    private static boolean canUpdateLocale(Context context) {
-        return !(userHasPersistedLocale() || isDeviceProvisioned(context));
+    /**
+     * Maps a given locale to a fallback locale that approximates it. This is a hack.
+     */
+    private static final Map<Locale, Locale> FALLBACKS = new HashMap<Locale, Locale>();
+
+    static {
+        FALLBACKS.put(Locale.CANADA, Locale.US);
     }
 
-    private static boolean userHasPersistedLocale() {
-        final String persistedLocale = SystemProperties.get("persist.sys.locale", "");
-        return !persistedLocale.isEmpty();
-    }
-
-    private static boolean isDeviceProvisioned(Context context) {
-        try {
-            return Settings.Global.getInt(
-                    context.getContentResolver(), Settings.Global.DEVICE_PROVISIONED) != 0;
-        } catch (Settings.SettingNotFoundException e) {
-            return false;
+    /**
+     * Find the best match we actually have a localization for. This function assumes we
+     * couldn't find an exact match.
+     *
+     * TODO: This should really follow the CLDR chain of parent locales! That might be a bit
+     * of a problem because we don't really have an en-001 locale on android.
+     */
+    private static Locale chooseBestFallback(Locale target, List<Locale> candidates) {
+        if (candidates.isEmpty()) {
+            return null;
         }
+
+        Locale fallback = target;
+        while ((fallback = FALLBACKS.get(fallback)) != null) {
+            if (candidates.contains(fallback)) {
+                return fallback;
+            }
+        }
+
+        // Somewhat arbitrarily take the first locale for the language,
+        // unless we get a perfect match later. Note that these come back in no
+        // particular order, so there's no reason to think the first match is
+        // a particularly good match.
+        return candidates.get(0);
     }
 
     /**
@@ -265,17 +274,6 @@
             country = ""; // The Locale constructor throws if passed null.
         }
 
-        // Check whether a developer is trying to test an arbitrary MCC.
-        boolean debuggingMccOverride = isDebuggingMccOverride();
-
-        // If this is a regular user and they already have a persisted locale, we're done.
-        if (!(debuggingMccOverride || canUpdateLocale(context))) {
-            Slog.d(LOG_TAG, "getLocaleForLanguageCountry: not permitted to update locale");
-            return null;
-        }
-
-        // Find the best match we actually have a localization for.
-        // TODO: this should really follow the CLDR chain of parent locales!
         final Locale target = new Locale(language, country);
         try {
             String[] localeArray = context.getAssets().getLocales();
@@ -285,7 +283,7 @@
             locales.remove("ar-XB");
             locales.remove("en-XA");
 
-            Locale firstMatch = null;
+            List<Locale> languageMatches = new ArrayList<>();
             for (String locale : locales) {
                 final Locale l = Locale.forLanguageTag(locale.replace('_', '-'));
 
@@ -301,22 +299,17 @@
                                l.toLanguageTag());
                         return l;
                     }
-                    // Otherwise somewhat arbitrarily take the first locale for the language,
-                    // unless we get a perfect match later. Note that these come back in no
-                    // particular order, so there's no reason to think the first match is
-                    // a particularly good match.
-                    if (firstMatch == null) {
-                        firstMatch = l;
-                    }
+
+                    // We've only matched the language, not the country.
+                    languageMatches.add(l);
                 }
             }
 
-            // We didn't find the exact locale, so return whichever locale we saw first where
-            // the language matched (if any).
-            if (firstMatch != null) {
+            Locale bestMatch = chooseBestFallback(target, languageMatches);
+            if (bestMatch != null) {
                 Slog.d(LOG_TAG, "getLocaleForLanguageCountry: got a language-only match: " +
-                       firstMatch.toLanguageTag());
-                return firstMatch;
+                       bestMatch.toLanguageTag());
+                return bestMatch;
             } else {
                 Slog.d(LOG_TAG, "getLocaleForLanguageCountry: no locales for language " +
                        language);
@@ -328,41 +321,6 @@
         return null;
     }
 
-    private static boolean isDebuggingMccOverride() {
-        if (Build.IS_DEBUGGABLE) {
-            String overrideMcc = SystemProperties.get("persist.sys.override_mcc", "");
-            if (!overrideMcc.isEmpty()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Utility code to set the system locale if it's not set already
-     * @param context Context to act on.
-     * @param language Two character language code desired
-     * @param country Two character country code desired
-     *
-     *  {@hide}
-     */
-    public static void setSystemLocale(Context context, String language, String country) {
-        Locale locale = getLocaleForLanguageCountry(context, language, country);
-        if (locale != null) {
-            Configuration config = new Configuration();
-            config.setLocale(locale);
-            config.userSetLocale = false;
-            Slog.d(LOG_TAG, "setSystemLocale: updateLocale config=" + config);
-            try {
-                ActivityManagerNative.getDefault().updateConfiguration(config);
-            } catch (RemoteException e) {
-                Slog.d(LOG_TAG, "setSystemLocale exception", e);
-            }
-        } else {
-            Slog.d(LOG_TAG, "setSystemLocale: no locale");
-        }
-    }
-
     /**
      * If the timezone is not already set, set it based on the MCC of the SIM.
      * @param context Context to act on.
@@ -384,17 +342,29 @@
 
     /**
      * Get Locale based on the MCC of the SIM.
+     *
      * @param context Context to act on.
      * @param mcc Mobile Country Code of the SIM or SIM-like entity (build prop on CDMA)
+     * @param simLanguage (nullable) the language from the SIM records (if present).
      *
      * @return locale for the mcc or null if none
      */
-    private static Locale getLocaleFromMcc(Context context, int mcc) {
-        String language = MccTable.defaultLanguageForMcc(mcc);
+    public static Locale getLocaleFromMcc(Context context, int mcc, String simLanguage) {
+        String language = (simLanguage == null) ? MccTable.defaultLanguageForMcc(mcc) : simLanguage;
         String country = MccTable.countryCodeForMcc(mcc);
 
-        Slog.d(LOG_TAG, "getLocaleFromMcc to " + language + "_" + country + " mcc=" + mcc);
-        return getLocaleForLanguageCountry(context, language, country);
+        Slog.d(LOG_TAG, "getLocaleFromMcc(" + language + ", " + country + ", " + mcc);
+        final Locale locale = getLocaleForLanguageCountry(context, language, country);
+
+        // If we couldn't find a locale that matches the SIM language, give it a go again
+        // with the "likely" language for the given country.
+        if (locale == null && simLanguage != null) {
+            language = MccTable.defaultLanguageForMcc(mcc);
+            Slog.d(LOG_TAG, "[retry ] getLocaleFromMcc(" + language + ", " + country + ", " + mcc);
+            return getLocaleForLanguageCountry(context, null, country);
+        }
+
+        return locale;
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/OperatorInfo.java b/src/java/com/android/internal/telephony/OperatorInfo.java
deleted file mode 100644
index 49c96a9..0000000
--- a/src/java/com/android/internal/telephony/OperatorInfo.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * {@hide}
- */
-public class OperatorInfo implements Parcelable {
-    public enum State {
-        UNKNOWN,
-        AVAILABLE,
-        CURRENT,
-        FORBIDDEN;
-    }
-
-    private String mOperatorAlphaLong;
-    private String mOperatorAlphaShort;
-    private String mOperatorNumeric;
-
-    private State mState = State.UNKNOWN;
-
-
-    public String
-    getOperatorAlphaLong() {
-        return mOperatorAlphaLong;
-    }
-
-    public String
-    getOperatorAlphaShort() {
-        return mOperatorAlphaShort;
-    }
-
-    public String
-    getOperatorNumeric() {
-        return mOperatorNumeric;
-    }
-
-    public State
-    getState() {
-        return mState;
-    }
-
-    OperatorInfo(String operatorAlphaLong,
-                String operatorAlphaShort,
-                String operatorNumeric,
-                State state) {
-
-        mOperatorAlphaLong = operatorAlphaLong;
-        mOperatorAlphaShort = operatorAlphaShort;
-        mOperatorNumeric = operatorNumeric;
-
-        mState = state;
-    }
-
-
-    public OperatorInfo(String operatorAlphaLong,
-                String operatorAlphaShort,
-                String operatorNumeric,
-                String stateString) {
-        this (operatorAlphaLong, operatorAlphaShort,
-                operatorNumeric, rilStateToState(stateString));
-    }
-
-    /**
-     * See state strings defined in ril.h RIL_REQUEST_QUERY_AVAILABLE_NETWORKS
-     */
-    private static State rilStateToState(String s) {
-        if (s.equals("unknown")) {
-            return State.UNKNOWN;
-        } else if (s.equals("available")) {
-            return State.AVAILABLE;
-        } else if (s.equals("current")) {
-            return State.CURRENT;
-        } else if (s.equals("forbidden")) {
-            return State.FORBIDDEN;
-        } else {
-            throw new RuntimeException(
-                "RIL impl error: Invalid network state '" + s + "'");
-        }
-    }
-
-
-    @Override
-    public String toString() {
-        return "OperatorInfo " + mOperatorAlphaLong
-                + "/" + mOperatorAlphaShort
-                + "/" + mOperatorNumeric
-                + "/" + mState;
-    }
-
-    /**
-     * Parcelable interface implemented below.
-     * This is a simple effort to make OperatorInfo parcelable rather than
-     * trying to make the conventional containing object (AsyncResult),
-     * implement parcelable.  This functionality is needed for the
-     * NetworkQueryService to fix 1128695.
-     */
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    /**
-     * Implement the Parcelable interface.
-     * Method to serialize a OperatorInfo object.
-     */
-    @Override
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeString(mOperatorAlphaLong);
-        dest.writeString(mOperatorAlphaShort);
-        dest.writeString(mOperatorNumeric);
-        dest.writeSerializable(mState);
-    }
-
-    /**
-     * Implement the Parcelable interface
-     * Method to deserialize a OperatorInfo object, or an array thereof.
-     */
-    public static final Creator<OperatorInfo> CREATOR =
-        new Creator<OperatorInfo>() {
-            @Override
-            public OperatorInfo createFromParcel(Parcel in) {
-                OperatorInfo opInfo = new OperatorInfo(
-                        in.readString(), /*operatorAlphaLong*/
-                        in.readString(), /*operatorAlphaShort*/
-                        in.readString(), /*operatorNumeric*/
-                        (State) in.readSerializable()); /*state*/
-                return opInfo;
-            }
-
-            @Override
-            public OperatorInfo[] newArray(int size) {
-                return new OperatorInfo[size];
-            }
-        };
-}
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 32966ae..2f13f4e 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.net.LinkProperties;
 import android.net.NetworkCapabilities;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
 import android.telephony.CellInfo;
@@ -37,6 +38,7 @@
 import com.android.internal.telephony.PhoneConstants.*; // ????
 
 import java.util.List;
+import java.util.Locale;
 
 /**
  * Internal interface used to control the phone; SDK developers cannot
@@ -110,6 +112,8 @@
     static final String REASON_SINGLE_PDN_ARBITRATION = "SinglePdnArbitration";
     static final String REASON_DATA_SPECIFIC_DISABLED = "specificDisabled";
     static final String REASON_SIM_NOT_READY = "simNotReady";
+    static final String REASON_IWLAN_AVAILABLE = "iwlanAvailable";
+    static final String REASON_CARRIER_CHANGE = "carrierChange";
 
     // Used for band mode selection methods
     static final int BM_UNSPECIFIED = 0; // selected by baseband automatically
@@ -372,6 +376,22 @@
     void unregisterForNewRingingConnection(Handler h);
 
     /**
+     * Notifies when phone's video capabilities changes <p>
+     *
+     *  Messages received from this:
+     *  Message.obj will be an AsyncResult
+     *  AsyncResult.userObj = obj
+     *  AsyncResult.result = true if phone supports video calling <p>
+     */
+    public void registerForVideoCapabilityChanged(Handler h, int what, Object obj);
+
+    /**
+     * Unregisters for video capability changed notification.
+     * Extraneous calls are tolerated silently
+     */
+    public void unregisterForVideoCapabilityChanged(Handler h);
+
+    /**
      * Notifies when an incoming call rings.<p>
      *
      *  Messages received from this:
@@ -845,15 +865,20 @@
      * path is connected (or a call index has been assigned) until
      * PhoneStateChanged notification has occurred.
      *
+     * NOTE: If adding another parameter, consider creating a DialArgs parameter instead to
+     * encapsulate all dial arguments and decrease scaffolding headache.
+     *
      * @param dialString The dial string.
      * @param uusInfo The UUSInfo.
      * @param videoState The desired video state for the connection.
+     * @param intentExtras The extras from the original CALL intent.
      * @exception CallStateException if a new outgoing call is not currently
      *                possible because no more call slots exist or a call exists
      *                that is dialing, alerting, ringing, or waiting. Other
      *                errors are handled asynchronously.
      */
-    Connection dial(String dialString, UUSInfo uusInfo, int videoState) throws CallStateException;
+    Connection dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
+            throws CallStateException;
 
     /**
      * Handles PIN MMI commands (PIN/PIN2/PUK/PUK2), which are initiated
@@ -1406,6 +1431,11 @@
     String getGroupIdLevel1();
 
     /**
+     * Retrieves the Group Identifier Level2 for phones.
+     */
+    String getGroupIdLevel2();
+
+    /**
      * Retrieves the serial number of the ICC, if applicable.
      */
     String getIccSerialNumber();
@@ -1888,6 +1918,11 @@
     public Phone getImsPhone();
 
     /**
+     * Start listening for IMS service UP/DOWN events.
+     */
+    public void startMonitoringImsService();
+
+    /**
      * Release the local instance of the ImsPhone and disconnect from
      * the phone.
      * @return the instance of the ImsPhone phone previously owned
@@ -1924,6 +1959,11 @@
     public boolean isRadioAvailable();
 
     /**
+     * Is Radio turned on
+     */
+    public boolean isRadioOn();
+
+    /**
      * shutdown Radio gracefully
      */
     public void shutdownRadio();
@@ -1938,6 +1978,13 @@
     public void setRadioCapability(RadioCapability rc, Message response);
 
     /**
+     *  Get phone radio capability
+     *
+     *  @return the capability of the radio defined in RadioCapability
+     */
+    public RadioCapability getRadioCapability();
+
+    /**
      *  Get phone radio access family
      *
      *  @return a bit mask to identify the radio access family.
@@ -1945,11 +1992,21 @@
     public int getRadioAccessFamily();
 
     /**
-     *  Get supported phone radio access family
+     *  Get the associated data modems Id.
      *
-     *  @return a bit mask to identify the radio access family.
+     *  @return a String containing the id of the data modem
      */
-    public int getSupportedRadioAccessFamily();
+    public String getModemUuId();
+
+    /**
+     *  The RadioCapability has changed. This comes up from the RIL and is called when radios first
+     *  become available or after a capability switch.  The flow is we use setRadioCapability to
+     *  request a change with the RIL and get an UNSOL response with the new data which gets set
+     *  here.
+     *
+     *  @param rc the phone radio capability currently in effect for this phone.
+     */
+    public void radioCapabilityUpdated(RadioCapability rc);
 
     /**
      * Registers the handler when phone radio  capability is changed.
@@ -1973,4 +2030,51 @@
      * @return true if IMS is Registered
      */
     public boolean isImsRegistered();
+
+    /**
+     * Determines if video calling is enabled for the phone.
+     *
+     * @return {@code true} if video calling is enabled, {@code false} otherwise.
+     */
+    public boolean isVideoEnabled();
+
+    /**
+     * @return {@code true} if we are in emergency call back mode. This is a period where the phone
+     * should be using as little power as possible and be ready to receive an incoming call from the
+     * emergency operator.
+     */
+    public boolean isInEcm();
+
+    /**
+     * Returns the Status of Wi-Fi Calling
+     *@hide
+     */
+    public boolean isWifiCallingEnabled();
+
+     /**
+     * Returns the Status of Volte
+     *@hide
+     */
+    public boolean isVolteEnabled();
+
+    /**
+     * @return {@code true} if video call is present, false otherwise.
+     */
+    public boolean isVideoCallPresent();
+
+    /**
+     * Returns the status of Link Capacity Estimation (LCE) service.
+     */
+    public int getLceStatus();
+
+    /**
+     * Returns the locale based on the carrier properties (such as {@code ro.carrier}) and
+     * SIM preferences.
+     */
+    public Locale getLocaleFromSimAndCarrierPrefs();
+
+    /**
+     * Returns the modem activity information
+     */
+    public void getModemActivityInfo(Message response);
 }
diff --git a/src/java/com/android/internal/telephony/PhoneBase.java b/src/java/com/android/internal/telephony/PhoneBase.java
index e1444ee..0815d50 100644
--- a/src/java/com/android/internal/telephony/PhoneBase.java
+++ b/src/java/com/android/internal/telephony/PhoneBase.java
@@ -26,6 +26,7 @@
 import android.net.wifi.WifiManager;
 import android.os.AsyncResult;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -45,13 +46,14 @@
 import android.telephony.SignalStrength;
 import android.telephony.SubscriptionManager;
 import android.telephony.VoLteServiceState;
+import android.telephony.ModemActivityInfo;
 import android.text.TextUtils;
 
 import com.android.ims.ImsManager;
 import com.android.internal.R;
 import com.android.internal.telephony.dataconnection.DcTrackerBase;
 import com.android.internal.telephony.imsphone.ImsPhone;
-import com.android.internal.telephony.RadioCapability;
+import com.android.internal.telephony.imsphone.ImsPhoneConnection;
 import com.android.internal.telephony.test.SimulatedRadioControl;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppType;
 import com.android.internal.telephony.uicc.IccFileHandler;
@@ -86,6 +88,7 @@
 public abstract class PhoneBase extends Handler implements Phone {
     private static final String LOG_TAG = "PhoneBase";
 
+    private boolean mImsIntentReceiverRegistered = false;
     private BroadcastReceiver mImsIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -100,12 +103,14 @@
                 }
             }
 
-            if (intent.getAction().equals(ImsManager.ACTION_IMS_SERVICE_UP)) {
-                mImsServiceReady = true;
-                updateImsPhone();
-            } else if (intent.getAction().equals(ImsManager.ACTION_IMS_SERVICE_DOWN)) {
-                mImsServiceReady = false;
-                updateImsPhone();
+            synchronized (PhoneProxy.lockForRadioTechnologyChange) {
+                if (intent.getAction().equals(ImsManager.ACTION_IMS_SERVICE_UP)) {
+                    mImsServiceReady = true;
+                    updateImsPhone();
+                } else if (intent.getAction().equals(ImsManager.ACTION_IMS_SERVICE_DOWN)) {
+                    mImsServiceReady = false;
+                    updateImsPhone();
+                }
             }
         }
     };
@@ -114,6 +119,8 @@
     public static final String NETWORK_SELECTION_KEY = "network_selection_key";
     // Key used to read and write the saved network selection operator name
     public static final String NETWORK_SELECTION_NAME_KEY = "network_selection_name_key";
+    // Key used to read and write the saved network selection operator short name
+    public static final String NETWORK_SELECTION_SHORT_KEY = "network_selection_short_key";
 
 
     // Key used to read/write "disable data connection on boot" pref (used for testing)
@@ -163,7 +170,10 @@
     protected static final int EVENT_UNSOL_OEM_HOOK_RAW             = 34;
     protected static final int EVENT_GET_RADIO_CAPABILITY           = 35;
     protected static final int EVENT_SS                             = 36;
-    protected static final int EVENT_LAST                           = EVENT_SS;
+    protected static final int EVENT_CONFIG_LCE                     = 37;
+    private static final int EVENT_CHECK_FOR_NETWORK_AUTOMATIC      = 38;
+    protected static final int EVENT_LAST                           =
+            EVENT_CHECK_FOR_NETWORK_AUTOMATIC;
 
     // For shared prefs.
     private static final String GSM_ROAMING_LIST_OVERRIDE_PREFIX = "gsm_roaming_list_";
@@ -192,6 +202,7 @@
         public Message message;
         public String operatorNumeric;
         public String operatorAlphaLong;
+        public String operatorAlphaShort;
     }
 
     /* Instance Variables */
@@ -204,8 +215,12 @@
     int mCallRingDelay;
     public boolean mIsTheCurrentActivePhone = true;
     boolean mIsVoiceCapable = true;
+
+    // Variable to cache the video capability. When RAT changes, we lose this info and are unable
+    // to recover from the state. We cache it and notify listeners when they register.
+    protected boolean mIsVideoCapable = false;
     protected UiccController mUiccController = null;
-    public AtomicReference<IccRecords> mIccRecords = new AtomicReference<IccRecords>();
+    public final AtomicReference<IccRecords> mIccRecords = new AtomicReference<IccRecords>();
     public SmsStorageMonitor mSmsStorageMonitor;
     public SmsUsageMonitor mSmsUsageMonitor;
     protected AtomicReference<UiccCardApplication> mUiccApplication =
@@ -218,11 +233,16 @@
 
     protected int mPhoneId;
 
-    private final Object mImsLock = new Object();
     private boolean mImsServiceReady = false;
     protected ImsPhone mImsPhone = null;
 
-    protected int mRadioAccessFamily = RadioAccessFamily.RAF_UNKNOWN;
+    private final AtomicReference<RadioCapability> mRadioCapability =
+            new AtomicReference<RadioCapability>();
+
+    protected static final int DEFAULT_REPORT_INTERVAL_MS = 200;
+    protected static final boolean LCE_PULL_MODE = true;
+    protected int mReportInterval = 0;  // ms
+    protected int mLceStatus = RILConstants.LCE_NOT_AVAILABLE;
 
     @Override
     public String getPhoneName() {
@@ -314,6 +334,13 @@
     protected final RegistrantList mSimRecordsLoadedRegistrants
             = new RegistrantList();
 
+    protected final RegistrantList mVideoCapabilityChangedRegistrants
+            = new RegistrantList();
+
+    protected final RegistrantList mEmergencyCallToggledRegistrants
+            = new RegistrantList();
+
+
     protected Looper mLooper; /* to insure registrants are in correct thread*/
 
     protected final Context mContext;
@@ -365,7 +392,7 @@
      * @param ci is CommandsInterface
      * @param unitTestMode when true, prevents notifications
      * of state change events
-     * @param subscription is current phone subscription
+     * @param phoneId the phone-id of this phone.
      */
     protected PhoneBase(String name, PhoneNotifier notifier, Context context, CommandsInterface ci,
             boolean unitTestMode, int phoneId) {
@@ -415,36 +442,70 @@
                 TelephonyProperties.PROPERTY_CALL_RING_DELAY, 3000);
         Rlog.d(LOG_TAG, "mCallRingDelay=" + mCallRingDelay);
 
-        if (getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) return;
+        if (getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
+            return;
+        }
 
-        setPropertiesByCarrier();
+        // The locale from the "ro.carrier" system property or R.array.carrier_properties.
+        // This will be overwritten by the Locale from the SIM language settings (EF-PL, EF-LI)
+        // if applicable.
+        final Locale carrierLocale = getLocaleFromCarrierProperties(mContext);
+        if (carrierLocale != null && !TextUtils.isEmpty(carrierLocale.getCountry())) {
+            final String country = carrierLocale.getCountry();
+            try {
+                Settings.Global.getInt(mContext.getContentResolver(),
+                        Settings.Global.WIFI_COUNTRY_CODE);
+            } catch (Settings.SettingNotFoundException e) {
+                // note this is not persisting
+                WifiManager wM = (WifiManager)
+                        mContext.getSystemService(Context.WIFI_SERVICE);
+                wM.setCountryCode(country, false);
+            }
+        }
 
         // Initialize device storage and outgoing SMS usage monitors for SMSDispatchers.
         mSmsStorageMonitor = new SmsStorageMonitor(this);
         mSmsUsageMonitor = new SmsUsageMonitor(context);
         mUiccController = UiccController.getInstance();
         mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null);
-
-        // Monitor IMS service - but first poll to see if already up (could miss
-        // intent)
-        ImsManager imsManager = ImsManager.getInstance(mContext, getPhoneId());
-        if (imsManager != null && imsManager.isServiceAvailable()) {
-            mImsServiceReady = true;
-            updateImsPhone();
+        if (getPhoneType() != PhoneConstants.PHONE_TYPE_SIP) {
+            mCi.registerForSrvccStateChanged(this, EVENT_SRVCC_STATE_CHANGED, null);
         }
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(ImsManager.ACTION_IMS_SERVICE_UP);
-        filter.addAction(ImsManager.ACTION_IMS_SERVICE_DOWN);
-        mContext.registerReceiver(mImsIntentReceiver, filter);
-
-        mCi.registerForSrvccStateChanged(this, EVENT_SRVCC_STATE_CHANGED, null);
         mCi.setOnUnsolOemHookRaw(this, EVENT_UNSOL_OEM_HOOK_RAW, null);
+        mCi.startLceService(DEFAULT_REPORT_INTERVAL_MS, LCE_PULL_MODE,
+                obtainMessage(EVENT_CONFIG_LCE));
+    }
+
+    @Override
+    public void startMonitoringImsService() {
+        if (getPhoneType() == PhoneConstants.PHONE_TYPE_SIP) {
+            return;
+        }
+
+        synchronized(PhoneProxy.lockForRadioTechnologyChange) {
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(ImsManager.ACTION_IMS_SERVICE_UP);
+            filter.addAction(ImsManager.ACTION_IMS_SERVICE_DOWN);
+            mContext.registerReceiver(mImsIntentReceiver, filter);
+            mImsIntentReceiverRegistered = true;
+
+            // Monitor IMS service - but first poll to see if already up (could miss
+            // intent)
+            ImsManager imsManager = ImsManager.getInstance(mContext, getPhoneId());
+            if (imsManager != null && imsManager.isServiceAvailable()) {
+                mImsServiceReady = true;
+                updateImsPhone();
+            }
+        }
     }
 
     @Override
     public void dispose() {
         synchronized(PhoneProxy.lockForRadioTechnologyChange) {
-            mContext.unregisterReceiver(mImsIntentReceiver);
+            if (mImsIntentReceiverRegistered) {
+                mContext.unregisterReceiver(mImsIntentReceiver);
+                mImsIntentReceiverRegistered = false;
+            }
             mCi.unSetOnCallRing(this);
             // Must cleanup all connectionS and needs to use sendMessage!
             mDcTracker.cleanUpAllConnections(null);
@@ -455,6 +516,7 @@
             mUiccController.unregisterForIccChanged(this);
             mCi.unregisterForSrvccStateChanged(this);
             mCi.unSetOnUnsolOemHookRaw(this);
+            mCi.stopLceService(obtainMessage(EVENT_CONFIG_LCE));
 
             if (mTelephonyTester != null) {
                 mTelephonyTester.dispose();
@@ -548,7 +610,7 @@
                     String dialString = (String) ar.result;
                     if (TextUtils.isEmpty(dialString)) return;
                     try {
-                        dialInternal(dialString, null, VideoProfile.VideoState.AUDIO_ONLY);
+                        dialInternal(dialString, null, VideoProfile.STATE_AUDIO_ONLY, null);
                     } catch (CallStateException e) {
                         Rlog.e(LOG_TAG, "silent redial failed: " + e);
                     }
@@ -581,14 +643,29 @@
                 RadioCapability rc = (RadioCapability) ar.result;
                 if (ar.exception != null) {
                     Rlog.d(LOG_TAG, "get phone radio capability fail,"
-                            + "no need to change mRadioAccessFamily");
+                            + "no need to change mRadioCapability");
                 } else {
-                    mRadioAccessFamily = rc.getRadioAccessFamily();
+                    radioCapabilityUpdated(rc);
                 }
                 Rlog.d(LOG_TAG, "EVENT_GET_RADIO_CAPABILITY :"
-                        + "phone RAF : " + mRadioAccessFamily);
+                        + "phone rc : " + rc);
                 break;
 
+            case EVENT_CONFIG_LCE:
+                ar = (AsyncResult) msg.obj;
+                if (ar.exception != null) {
+                    Rlog.d(LOG_TAG, "config LCE service failed: " + ar.exception);
+                } else {
+                    final ArrayList<Integer> statusInfo = (ArrayList<Integer>)ar.result;
+                    mLceStatus = statusInfo.get(0);
+                    mReportInterval = statusInfo.get(1);
+                }
+                break;
+
+            case EVENT_CHECK_FOR_NETWORK_AUTOMATIC: {
+                onCheckForNetworkSelectionModeAutomatic(msg);
+                break;
+            }
             default:
                 throw new RuntimeException("unexpected event not handled");
         }
@@ -764,6 +841,24 @@
 
     // Inherited documentation suffices.
     @Override
+    public void registerForVideoCapabilityChanged(
+            Handler h, int what, Object obj) {
+        checkCorrectThread(h);
+
+        mVideoCapabilityChangedRegistrants.addUnique(h, what, obj);
+
+        // Notify any registrants of the cached video capability as soon as they register.
+        notifyForVideoCapabilityChanged(mIsVideoCapable);
+    }
+
+    // Inherited documentation suffices.
+    @Override
+    public void unregisterForVideoCapabilityChanged(Handler h) {
+        mVideoCapabilityChangedRegistrants.remove(h);
+    }
+
+    // Inherited documentation suffices.
+    @Override
     public void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj){
         mCi.registerForInCallVoicePrivacyOn(h, what, obj);
     }
@@ -877,18 +972,47 @@
 
     @Override
     public void setNetworkSelectionModeAutomatic(Message response) {
-        // wrap the response message in our own message along with
-        // an empty string (to indicate automatic selection) for the
-        // operator's id.
-        NetworkSelectMessage nsm = new NetworkSelectMessage();
-        nsm.message = response;
-        nsm.operatorNumeric = "";
-        nsm.operatorAlphaLong = "";
+        Rlog.d(LOG_TAG, "setNetworkSelectionModeAutomatic, querying current mode");
+        // we don't want to do this unecesarily - it acutally causes
+        // the radio to repeate network selection and is costly
+        // first check if we're already in automatic mode
+        Message msg = obtainMessage(EVENT_CHECK_FOR_NETWORK_AUTOMATIC);
+        msg.obj = response;
+        mCi.getNetworkSelectionMode(msg);
+    }
 
-        Message msg = obtainMessage(EVENT_SET_NETWORK_AUTOMATIC_COMPLETE, nsm);
-        mCi.setNetworkSelectionModeAutomatic(msg);
+    private void onCheckForNetworkSelectionModeAutomatic(Message fromRil) {
+        AsyncResult ar = (AsyncResult)fromRil.obj;
+        Message response = (Message)ar.userObj;
+        boolean doAutomatic = true;
+        if (ar.exception == null && ar.result != null) {
+            try {
+                int[] modes = (int[])ar.result;
+                if (modes[0] == 0) {
+                    // already confirmed to be in automatic mode - don't resend
+                    doAutomatic = false;
+                }
+            } catch (Exception e) {
+                // send the setting on error
+            }
+        }
+        if (doAutomatic) {
+            // wrap the response message in our own message along with
+            // an empty string (to indicate automatic selection) for the
+            // operator's id.
+            NetworkSelectMessage nsm = new NetworkSelectMessage();
+            nsm.message = response;
+            nsm.operatorNumeric = "";
+            nsm.operatorAlphaLong = "";
+            nsm.operatorAlphaShort = "";
 
-        updateSavedNetworkOperator(nsm);
+            Message msg = obtainMessage(EVENT_SET_NETWORK_AUTOMATIC_COMPLETE, nsm);
+            mCi.setNetworkSelectionModeAutomatic(msg);
+
+            updateSavedNetworkOperator(nsm);
+        } else {
+            Rlog.d(LOG_TAG, "setNetworkSelectionModeAutomatic - already auto, ignoring");
+        }
     }
 
     @Override
@@ -904,6 +1028,7 @@
         nsm.message = response;
         nsm.operatorNumeric = network.getOperatorNumeric();
         nsm.operatorAlphaLong = network.getOperatorAlphaLong();
+        nsm.operatorAlphaShort = network.getOperatorAlphaShort();
 
         Message msg = obtainMessage(EVENT_SET_NETWORK_MANUAL_COMPLETE, nsm);
         mCi.setNetworkSelectionModeManual(network.getOperatorNumeric(), msg);
@@ -911,6 +1036,23 @@
         updateSavedNetworkOperator(nsm);
     }
 
+    /**
+     * Registration point for emergency call/callback mode start. Message.obj is AsyncResult and
+     * Message.obj.result will be Integer indicating start of call by value 1 or end of call by
+     * value 0
+     * @param h handler to notify
+     * @param what what code of message when delivered
+     * @param obj placed in Message.obj.userObj
+     */
+    public void registerForEmergencyCallToggle(Handler h, int what, Object obj) {
+        Registrant r = new Registrant(h, what, obj);
+        mEmergencyCallToggledRegistrants.add(r);
+    }
+
+    public void unregisterForEmergencyCallToggle(Handler h) {
+        mEmergencyCallToggledRegistrants.remove(h);
+    }
+
     private void updateSavedNetworkOperator(NetworkSelectMessage nsm) {
         int subId = getSubId();
         if (SubscriptionManager.isValidSubscriptionId(subId)) {
@@ -920,6 +1062,7 @@
             SharedPreferences.Editor editor = sp.edit();
             editor.putString(NETWORK_SELECTION_KEY + subId, nsm.operatorNumeric);
             editor.putString(NETWORK_SELECTION_NAME_KEY + subId, nsm.operatorAlphaLong);
+            editor.putString(NETWORK_SELECTION_SHORT_KEY + subId, nsm.operatorAlphaShort);
 
             // commit and log the result.
             if (!editor.commit()) {
@@ -953,12 +1096,15 @@
     }
 
     /**
-     * Method to retrieve the saved operator id from the Shared Preferences
+     * Method to retrieve the saved operator from the Shared Preferences
      */
-    private String getSavedNetworkSelection() {
+    private OperatorInfo getSavedNetworkSelection() {
         // open the shared preferences and search with our key.
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
-        return sp.getString(NETWORK_SELECTION_KEY + getSubId(), "");
+        String numeric = sp.getString(NETWORK_SELECTION_KEY + getSubId(), "");
+        String name = sp.getString(NETWORK_SELECTION_NAME_KEY + getSubId(), "");
+        String shrt = sp.getString(NETWORK_SELECTION_SHORT_KEY + getSubId(), "");
+        return new OperatorInfo(numeric, name, shrt);
     }
 
     /**
@@ -967,14 +1113,14 @@
      * preferences.
      */
     public void restoreSavedNetworkSelection(Message response) {
-        // retrieve the operator id
-        String networkSelection = getSavedNetworkSelection();
+        // retrieve the operator
+        OperatorInfo networkSelection = getSavedNetworkSelection();
 
         // set to auto if the id is empty, otherwise select the network.
-        if (TextUtils.isEmpty(networkSelection)) {
-            mCi.setNetworkSelectionModeAutomatic(response);
+        if (networkSelection == null || TextUtils.isEmpty(networkSelection.getOperatorNumeric())) {
+            setNetworkSelectionModeAutomatic(response);
         } else {
-            mCi.setNetworkSelectionModeManual(networkSelection, response);
+            selectNetworkManually(networkSelection, response);
         }
     }
 
@@ -1091,37 +1237,23 @@
      * Set the properties by matching the carrier string in
      * a string-array resource
      */
-    private void setPropertiesByCarrier() {
+    private static Locale getLocaleFromCarrierProperties(Context ctx) {
         String carrier = SystemProperties.get("ro.carrier");
 
         if (null == carrier || 0 == carrier.length() || "unknown".equals(carrier)) {
-            return;
+            return null;
         }
 
-        CharSequence[] carrierLocales = mContext.
-                getResources().getTextArray(R.array.carrier_properties);
+        CharSequence[] carrierLocales = ctx.getResources().getTextArray(R.array.carrier_properties);
 
         for (int i = 0; i < carrierLocales.length; i+=3) {
             String c = carrierLocales[i].toString();
             if (carrier.equals(c)) {
-                final Locale l = Locale.forLanguageTag(carrierLocales[i + 1].toString().replace('_', '-'));
-                final String country = l.getCountry();
-                MccTable.setSystemLocale(mContext, l.getLanguage(), country);
-
-                if (!country.isEmpty()) {
-                    try {
-                        Settings.Global.getInt(mContext.getContentResolver(),
-                                Settings.Global.WIFI_COUNTRY_CODE);
-                    } catch (Settings.SettingNotFoundException e) {
-                        // note this is not persisting
-                        WifiManager wM = (WifiManager)
-                                mContext.getSystemService(Context.WIFI_SERVICE);
-                        wM.setCountryCode(country, false);
-                    }
-                }
-                return;
+                return Locale.forLanguageTag(carrierLocales[i + 1].toString().replace('_', '-'));
             }
         }
+
+        return null;
     }
 
     /**
@@ -1216,6 +1348,7 @@
      * @return the original list with CDMA lat/long cleared if necessary
      */
     private List<CellInfo> privatizeCellInfoList(List<CellInfo> cellInfoList) {
+        if (cellInfoList == null) return null;
         int mode = Settings.Secure.getInt(getContext().getContentResolver(),
                 Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF);
         if (mode == Settings.Secure.LOCATION_MODE_OFF) {
@@ -1304,7 +1437,33 @@
      */
     @Override
     public void setPreferredNetworkType(int networkType, Message response) {
-        mCi.setPreferredNetworkType(networkType, response);
+        // Only set preferred network types to that which the modem supports
+        int modemRaf = getRadioAccessFamily();
+        int rafFromType = RadioAccessFamily.getRafFromNetworkType(networkType);
+
+        if (modemRaf == RadioAccessFamily.RAF_UNKNOWN
+                || rafFromType == RadioAccessFamily.RAF_UNKNOWN) {
+            Rlog.d(LOG_TAG, "setPreferredNetworkType: Abort, unknown RAF: "
+                    + modemRaf + " " + rafFromType);
+            if (response != null) {
+                CommandException ex;
+
+                ex = new CommandException(CommandException.Error.GENERIC_FAILURE);
+                AsyncResult.forMessage(response, null, ex);
+                response.sendToTarget();
+            }
+            return;
+        }
+
+        int filteredRaf = (rafFromType & modemRaf);
+        int filteredType = RadioAccessFamily.getNetworkTypeFromRaf(filteredRaf);
+
+        Rlog.d(LOG_TAG, "setPreferredNetworkType: networkType = " + networkType
+                + " modemRaf = " + modemRaf
+                + " rafFromType = " + rafFromType
+                + " filteredType = " + filteredType);
+
+        mCi.setPreferredNetworkType(filteredType, response);
     }
 
     @Override
@@ -1447,14 +1606,40 @@
     }
 
     /**
-     * @return true if we are in the emergency call back mode. This is a period where
-     * the phone should be using as little power as possible and be ready to receive an
-     * incoming call from the emergency operator.
+     * @return {@code true} if we are in emergency call back mode. This is a period where the phone
+     * should be using as little power as possible and be ready to receive an incoming call from the
+     * emergency operator.
      */
     public boolean isInEcm() {
         return false;
     }
 
+    private static int getVideoState(Call call) {
+        int videoState = VideoProfile.STATE_AUDIO_ONLY;
+        ImsPhoneConnection conn = (ImsPhoneConnection) call.getEarliestConnection();
+        if (conn != null) {
+            videoState = conn.getVideoState();
+        }
+        return videoState;
+    }
+
+    private boolean isVideoCall(Call call) {
+        int videoState = getVideoState(call);
+        return (VideoProfile.isVideo(videoState));
+    }
+
+    @Override
+    public boolean isVideoCallPresent() {
+        boolean isVideoCallActive = false;
+        if (mImsPhone != null) {
+            isVideoCallActive = isVideoCall(mImsPhone.getForegroundCall()) ||
+                    isVideoCall(mImsPhone.getBackgroundCall()) ||
+                    isVideoCall(mImsPhone.getRingingCall());
+        }
+        Rlog.d(LOG_TAG, "isVideoCallActive: " + isVideoCallActive);
+        return isVideoCallActive;
+    }
+
     @Override
     public abstract int getPhoneType();
 
@@ -1479,14 +1664,15 @@
         String subscriberId = sp.getString(VM_ID, null);
         String currentSubscriberId = getSubscriberId();
 
-        Rlog.d(LOG_TAG, "Voicemail count retrieval for subscriberId = " + subscriberId +
-                " current subscriberId = " + currentSubscriberId);
-
         if ((subscriberId != null) && (currentSubscriberId != null)
                 && (currentSubscriberId.equals(subscriberId))) {
             // get voice mail count from preferences
             countVoiceMessages = sp.getInt(VM_COUNT, 0);
             Rlog.d(LOG_TAG, "Voice Mail Count from preference = " + countVoiceMessages);
+        } else {
+            Rlog.d(LOG_TAG, "Voicemail count retrieval returning 0 as count for matching " +
+                    "subscriberId not found");
+
         }
         return countVoiceMessages;
     }
@@ -1759,6 +1945,18 @@
         mNewRingingConnectionRegistrants.notifyRegistrants(ar);
     }
 
+
+    /**
+     * Notify registrants if phone is video capable.
+     */
+    public void notifyForVideoCapabilityChanged(boolean isVideoCallCapable) {
+        // Cache the current video capability so that we don't lose the information.
+        mIsVideoCapable = isVideoCallCapable;
+
+        AsyncResult ar = new AsyncResult(null, isVideoCallCapable, null);
+        mVideoCapabilityChangedRegistrants.notifyRegistrants(ar);
+    }
+
     /**
      * Notify registrants of a RING event.
      */
@@ -1904,10 +2102,15 @@
 
     @Override
     public ImsPhone relinquishOwnershipOfImsPhone() {
-        synchronized (mImsLock) {
+        synchronized (PhoneProxy.lockForRadioTechnologyChange) {
             if (mImsPhone == null)
                 return null;
 
+            if (mImsIntentReceiverRegistered) {
+                mContext.unregisterReceiver(mImsIntentReceiver);
+                mImsIntentReceiverRegistered = false;
+            }
+
             ImsPhone imsPhone = mImsPhone;
             mImsPhone = null;
 
@@ -1920,7 +2123,7 @@
 
     @Override
     public void acquireOwnershipOfImsPhone(ImsPhone imsPhone) {
-        synchronized (mImsLock) {
+        synchronized (PhoneProxy.lockForRadioTechnologyChange) {
             if (imsPhone == null)
                 return;
 
@@ -1945,26 +2148,24 @@
     }
 
     protected void updateImsPhone() {
-        synchronized (mImsLock) {
-            Rlog.d(LOG_TAG, "updateImsPhone"
-                    + " mImsServiceReady=" + mImsServiceReady);
+        Rlog.d(LOG_TAG, "updateImsPhone"
+                + " mImsServiceReady=" + mImsServiceReady);
 
-            if (mImsServiceReady && (mImsPhone == null)) {
-                mImsPhone = PhoneFactory.makeImsPhone(mNotifier, this);
-                CallManager.getInstance().registerPhone(mImsPhone);
-                mImsPhone.registerForSilentRedial(
-                        this, EVENT_INITIATE_SILENT_REDIAL, null);
-            } else if (!mImsServiceReady && (mImsPhone != null)) {
-                CallManager.getInstance().unregisterPhone(mImsPhone);
-                mImsPhone.unregisterForSilentRedial(this);
+        if (mImsServiceReady && (mImsPhone == null)) {
+            mImsPhone = PhoneFactory.makeImsPhone(mNotifier, this);
+            CallManager.getInstance().registerPhone(mImsPhone);
+            mImsPhone.registerForSilentRedial(
+                    this, EVENT_INITIATE_SILENT_REDIAL, null);
+        } else if (!mImsServiceReady && (mImsPhone != null)) {
+            CallManager.getInstance().unregisterPhone(mImsPhone);
+            mImsPhone.unregisterForSilentRedial(this);
 
-                mImsPhone.dispose();
-                // Potential GC issue if someone keeps a reference to ImsPhone.
-                // However: this change will make sure that such a reference does
-                // not access functions through NULL pointer.
-                //mImsPhone.removeReferences();
-                mImsPhone = null;
-            }
+            mImsPhone.dispose();
+            // Potential GC issue if someone keeps a reference to ImsPhone.
+            // However: this change will make sure that such a reference does
+            // not access functions through NULL pointer.
+            //mImsPhone.removeReferences();
+            mImsPhone = null;
         }
     }
 
@@ -1974,10 +2175,12 @@
      * @param dialString The number to dial.
      * @param uusInfo The UUSInfo.
      * @param videoState The video state for the call.
+     * @param intentExtras Extras from the original CALL intent.
      * @return The Connection.
      * @throws CallStateException
      */
-    protected Connection dialInternal(String dialString, UUSInfo uusInfo, int videoState)
+    protected Connection dialInternal(
+            String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
             throws CallStateException {
         // dialInternal shall be overriden by GSMPhone and CDMAPhone
         return null;
@@ -2086,6 +2289,34 @@
         return isImsRegistered;
     }
 
+    /**
+     * Get Wifi Calling Feature Availability
+     */
+    @Override
+    public boolean isWifiCallingEnabled() {
+        ImsPhone imsPhone = mImsPhone;
+        boolean isWifiCallingEnabled = false;
+        if (imsPhone != null) {
+            isWifiCallingEnabled = imsPhone.isVowifiEnabled();
+        }
+        Rlog.d(LOG_TAG, "isWifiCallingEnabled =" + isWifiCallingEnabled);
+        return isWifiCallingEnabled;
+    }
+
+    /**
+     * Get Volte Feature Availability
+     */
+    @Override
+    public boolean isVolteEnabled() {
+        ImsPhone imsPhone = mImsPhone;
+        boolean isVolteEnabled = false;
+        if (imsPhone != null) {
+            isVolteEnabled = imsPhone.isVolteEnabled();
+        }
+        Rlog.d(LOG_TAG, "isImsRegistered =" + isVolteEnabled);
+        return isVolteEnabled;
+    }
+
     private boolean getRoamingOverrideHelper(String prefix, String key) {
         String iccId = getIccSerialNumber();
         if (TextUtils.isEmpty(iccId) || TextUtils.isEmpty(key)) {
@@ -2106,6 +2337,11 @@
     }
 
     @Override
+    public boolean isRadioOn() {
+        return mCi.getRadioState().isOn();
+    }
+
+    @Override
     public void shutdownRadio() {
         getServiceStateTracker().requestShutdown();
     }
@@ -2117,12 +2353,49 @@
 
     @Override
     public int getRadioAccessFamily() {
-        return mRadioAccessFamily;
+        final RadioCapability rc = getRadioCapability();
+        return (rc == null ? RadioAccessFamily.RAF_UNKNOWN : rc.getRadioAccessFamily());
     }
 
     @Override
-    public int getSupportedRadioAccessFamily() {
-        return mCi.getSupportedRadioAccessFamily();
+    public String getModemUuId() {
+        final RadioCapability rc = getRadioCapability();
+        return (rc == null ? "" : rc.getLogicalModemUuid());
+    }
+
+    @Override
+    public RadioCapability getRadioCapability() {
+        return mRadioCapability.get();
+    }
+
+    @Override
+    public void radioCapabilityUpdated(RadioCapability rc) {
+        // Called when radios first become available or after a capability switch
+        // Update the cached value
+        mRadioCapability.set(rc);
+
+        if (SubscriptionManager.isValidSubscriptionId(getSubId())) {
+            sendSubscriptionSettings(true);
+        }
+    }
+
+    public void sendSubscriptionSettings(boolean restoreNetworkSelection) {
+        // Send settings down
+        int type = PhoneFactory.calculatePreferredNetworkType(mContext, getSubId());
+        setPreferredNetworkType(type, null);
+
+        if (restoreNetworkSelection) {
+            restoreSavedNetworkSelection(null);
+        }
+        mDcTracker.setDataEnabled(getDataEnabled());
+    }
+
+    protected void setPreferredNetworkTypeIfSimLoaded() {
+        int subId = getSubId();
+        if (SubscriptionManager.isValidSubscriptionId(subId)) {
+            int type = PhoneFactory.calculatePreferredNetworkType(mContext, getSubId());
+            setPreferredNetworkType(type, null);
+        }
     }
 
     @Override
@@ -2135,6 +2408,67 @@
         mCi.unregisterForRadioCapabilityChanged(this);
     }
 
+    /**
+     * Determines if  IMS is enabled for call.
+     *
+     * @return {@code true} if IMS calling is enabled.
+     */
+    public boolean isImsUseEnabled() {
+        boolean imsUseEnabled =
+                ((ImsManager.isVolteEnabledByPlatform(mContext) &&
+                ImsManager.isEnhanced4gLteModeSettingEnabledByUser(mContext)) ||
+                (ImsManager.isWfcEnabledByPlatform(mContext) &&
+                ImsManager.isWfcEnabledByUser(mContext)) &&
+                ImsManager.isNonTtyOrTtyOnVolteEnabled(mContext));
+        return imsUseEnabled;
+    }
+
+    /**
+     * Determines if video calling is enabled for the IMS phone.
+     *
+     * @return {@code true} if video calling is enabled.
+     */
+    @Override
+    public boolean isVideoEnabled() {
+        ImsPhone imsPhone = mImsPhone;
+        if ((imsPhone != null)
+                && (imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)) {
+            return imsPhone.isVideoEnabled();
+        }
+        return false;
+    }
+
+    @Override
+    public int getLceStatus() {
+        return mLceStatus;
+    }
+
+    @Override
+    public void getModemActivityInfo(Message response)  {
+        mCi.getModemActivityInfo(response);
+    }
+
+    /**
+     * Starts LCE service after radio becomes available.
+     * LCE service state may get destroyed on the modem when radio becomes unavailable.
+     */
+    public void startLceAfterRadioIsAvailable() {
+        if (mIsTheCurrentActivePhone) {
+            mCi.startLceService(DEFAULT_REPORT_INTERVAL_MS, LCE_PULL_MODE,
+                obtainMessage(EVENT_CONFIG_LCE));
+        }
+    }
+
+    @Override
+    public Locale getLocaleFromSimAndCarrierPrefs() {
+        final IccRecords records = mIccRecords.get();
+        if (records != null && records.getSimLanguage() != null) {
+            return new Locale(records.getSimLanguage());
+        }
+
+        return getLocaleFromCarrierProperties(mContext);
+    }
+
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("PhoneBase: subId=" + getSubId());
         pw.println(" mPhoneId=" + mPhoneId);
diff --git a/src/java/com/android/internal/telephony/PhoneFactory.java b/src/java/com/android/internal/telephony/PhoneFactory.java
index 9f02433..b4c1c94 100644
--- a/src/java/com/android/internal/telephony/PhoneFactory.java
+++ b/src/java/com/android/internal/telephony/PhoneFactory.java
@@ -29,6 +29,7 @@
 import android.telephony.Rlog;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.util.LocalLog;
 
 import com.android.internal.telephony.cdma.CDMALTEPhone;
 import com.android.internal.telephony.cdma.CDMAPhone;
@@ -42,9 +43,11 @@
 import com.android.internal.telephony.sip.SipPhoneFactory;
 import com.android.internal.telephony.uicc.IccCardProxy;
 import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.util.IndentingPrintWriter;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.HashMap;
 
 /**
  * {@hide}
@@ -53,6 +56,7 @@
     static final String LOG_TAG = "PhoneFactory";
     static final int SOCKET_OPEN_RETRY_MILLIS = 2 * 1000;
     static final int SOCKET_OPEN_MAX_RETRY = 3;
+    static final boolean DBG = false;
 
     //***** Class Variables
 
@@ -73,6 +77,8 @@
     static private PhoneNotifier sPhoneNotifier;
     static private Context sContext;
 
+    static private final HashMap<String, LocalLog>sLocalLogs = new HashMap<String, LocalLog>();
+
     //***** Class Methods
 
     public static void makeDefaultPhones(Context context) {
@@ -151,9 +157,11 @@
                     if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
                         phone = new GSMPhone(context,
                                 sCommandsInterfaces[i], sPhoneNotifier, i);
+                        phone.startMonitoringImsService();
                     } else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
                         phone = new CDMALTEPhone(context,
                                 sCommandsInterfaces[i], sPhoneNotifier, i);
+                        phone.startMonitoringImsService();
                     }
                     Rlog.i(LOG_TAG, "Creating Phone with type = " + phoneType + " sub = " + i);
 
@@ -226,15 +234,18 @@
                 throw new IllegalStateException("Default phones haven't been made yet!");
                 // CAF_MSIM FIXME need to introduce default phone id ?
             } else if (phoneId == SubscriptionManager.DEFAULT_PHONE_INDEX) {
-                dbgInfo = "phoneId == DEFAULT_PHONE_ID return sProxyPhone";
+                if (DBG) dbgInfo = "phoneId == DEFAULT_PHONE_ID return sProxyPhone";
                 phone = sProxyPhone;
             } else {
-                dbgInfo = "phoneId != DEFAULT_PHONE_ID return sProxyPhones[phoneId]";
+                if (DBG) dbgInfo = "phoneId != DEFAULT_PHONE_ID return sProxyPhones[phoneId]";
                 phone = (((phoneId >= 0)
                                 && (phoneId < TelephonyManager.getDefault().getPhoneCount()))
                         ? sProxyPhones[phoneId] : null);
             }
-            Rlog.d(LOG_TAG, "getPhone:- " + dbgInfo + " phoneId=" + phoneId + " phone=" + phone);
+            if (DBG) {
+                Rlog.d(LOG_TAG, "getPhone:- " + dbgInfo + " phoneId=" + phoneId +
+                        " phone=" + phone);
+            }
             return phone;
         }
     }
@@ -276,7 +287,7 @@
 
         // Update MCC MNC device configuration information
         String defaultMccMnc = TelephonyManager.getDefault().getSimOperatorNumericForPhone(phoneId);
-        Rlog.d(LOG_TAG, "update mccmnc=" + defaultMccMnc);
+        if (DBG) Rlog.d(LOG_TAG, "update mccmnc=" + defaultMccMnc);
         MccTable.updateMccMncConfiguration(sContext, defaultMccMnc, false);
 
         // Broadcast an Intent for default sub change
@@ -406,6 +417,43 @@
         return ImsPhoneFactory.makePhone(sContext, phoneNotifier, defaultPhone);
     }
 
+    /**
+     * Adds a local log category.
+     *
+     * Only used within the telephony process.  Use localLog to add log entries.
+     *
+     * TODO - is there a better way to do this?  Think about design when we have a minute.
+     *
+     * @param key the name of the category - will be the header in the service dump.
+     * @param size the number of lines to maintain in this category
+     */
+    public static void addLocalLog(String key, int size) {
+        synchronized(sLocalLogs) {
+            if (sLocalLogs.containsKey(key)) {
+                throw new IllegalArgumentException("key " + key + " already present");
+            }
+            sLocalLogs.put(key, new LocalLog(size));
+        }
+    }
+
+    /**
+     * Add a line to the named Local Log.
+     *
+     * This will appear in the TelephonyDebugService dump.
+     *
+     * @param key the name of the log category to put this in.  Must be created
+     *            via addLocalLog.
+     * @param log the string to add to the log.
+     */
+    public static void localLog(String key, String log) {
+        synchronized(sLocalLogs) {
+            if (sLocalLogs.containsKey(key) == false) {
+                throw new IllegalArgumentException("key " + key + " not found");
+            }
+            sLocalLogs.get(key).log(log);
+        }
+    }
+
     public static void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("PhoneFactory:");
         PhoneProxy [] phones = (PhoneProxy[])PhoneFactory.getPhones();
@@ -454,5 +502,25 @@
             e.printStackTrace();
         }
         pw.flush();
+        pw.println("++++++++++++++++++++++++++++++++");
+
+        try {
+            sSubInfoRecordUpdater.dump(fd, pw, args);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        pw.flush();
+
+        pw.println("++++++++++++++++++++++++++++++++");
+        synchronized (sLocalLogs) {
+            final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
+            for (String key : sLocalLogs.keySet()) {
+                ipw.println(key);
+                ipw.increaseIndent();
+                sLocalLogs.get(key).dump(fd, ipw, args);
+                ipw.decreaseIndent();
+            }
+            ipw.flush();
+        }
     }
 }
diff --git a/src/java/com/android/internal/telephony/PhoneProxy.java b/src/java/com/android/internal/telephony/PhoneProxy.java
index 3c71012..13428ec 100644
--- a/src/java/com/android/internal/telephony/PhoneProxy.java
+++ b/src/java/com/android/internal/telephony/PhoneProxy.java
@@ -24,10 +24,13 @@
 import android.net.LinkProperties;
 import android.net.NetworkCapabilities;
 import android.os.AsyncResult;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.os.PersistableBundle;
 import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.telephony.CarrierConfigManager;
 import android.telephony.CellInfo;
 import android.telephony.CellLocation;
 import android.telephony.Rlog;
@@ -40,7 +43,6 @@
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.test.SimulatedRadioControl;
 import com.android.internal.telephony.cdma.CDMALTEPhone;
-import com.android.internal.telephony.RadioCapability;
 import com.android.internal.telephony.uicc.IccCardProxy;
 import com.android.internal.telephony.uicc.IccFileHandler;
 import com.android.internal.telephony.uicc.IsimRecords;
@@ -50,7 +52,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.List;
-import java.util.Set;
+import java.util.Locale;
 
 import com.android.internal.telephony.dataconnection.DctController;
 
@@ -177,13 +179,20 @@
 
         if (mActivePhone != null) {
             // Check for a voice over lte replacement
-            if ((newVoiceRadioTech == ServiceState.RIL_RADIO_TECHNOLOGY_LTE) ||
-                    (newVoiceRadioTech == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN)) {
-                int volteReplacementRat = mActivePhone.getContext().getResources().getInteger(
-                        com.android.internal.R.integer.config_volte_replacement_rat);
-                logd("phoneObjectUpdater: volteReplacementRat=" + volteReplacementRat);
-                if (volteReplacementRat != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) {
-                    newVoiceRadioTech = volteReplacementRat;
+            if ((newVoiceRadioTech == ServiceState.RIL_RADIO_TECHNOLOGY_LTE)
+                    || (newVoiceRadioTech == ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN)) {
+                CarrierConfigManager configMgr = (CarrierConfigManager)
+                        mActivePhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
+                PersistableBundle b = configMgr.getConfigForSubId(mActivePhone.getSubId());
+                if (b != null) {
+                    int volteReplacementRat =
+                            b.getInt(CarrierConfigManager.KEY_VOLTE_REPLACEMENT_RAT_INT);
+                    logd("phoneObjectUpdater: volteReplacementRat=" + volteReplacementRat);
+                    if (volteReplacementRat != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN) {
+                        newVoiceRadioTech = volteReplacementRat;
+                    }
+                } else {
+                    loge("phoneObjectUpdater: didn't get volteReplacementRat from carrier config");
                 }
             }
 
@@ -301,6 +310,7 @@
             if (imsPhone != null) {
                 mActivePhone.acquireOwnershipOfImsPhone(imsPhone);
             }
+            mActivePhone.startMonitoringImsService();
             mActivePhone.registerForSimRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null);
         }
 
@@ -333,6 +343,11 @@
     }
 
     @Override
+    public boolean isVideoCallPresent() {
+        return mActivePhone.isVideoCallPresent();
+    }
+
+    @Override
     public void updatePhoneObject(int voiceRadioTech) {
         logd("updatePhoneObject: radioTechnology=" + voiceRadioTech);
         sendMessage(obtainMessage(EVENT_UPDATE_PHONE_OBJECT, voiceRadioTech, 0, null));
@@ -480,6 +495,17 @@
     }
 
     @Override
+    public void registerForVideoCapabilityChanged(
+            Handler h, int what, Object obj) {
+        mActivePhone.registerForVideoCapabilityChanged(h, what, obj);
+    }
+
+    @Override
+    public void unregisterForVideoCapabilityChanged(Handler h) {
+        mActivePhone.unregisterForVideoCapabilityChanged(h);
+    }
+
+    @Override
     public void registerForIncomingRing(Handler h, int what, Object obj) {
         mActivePhone.registerForIncomingRing(h, what, obj);
     }
@@ -739,8 +765,9 @@
     }
 
     @Override
-    public Connection dial(String dialString, UUSInfo uusInfo, int videoState) throws CallStateException {
-        return mActivePhone.dial(dialString, uusInfo, videoState);
+    public Connection dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
+            throws CallStateException {
+        return mActivePhone.dial(dialString, uusInfo, videoState, intentExtras);
     }
 
     @Override
@@ -1048,6 +1075,11 @@
     }
 
     @Override
+    public String getGroupIdLevel2() {
+        return mActivePhone.getGroupIdLevel2();
+    }
+
+    @Override
     public String getIccSerialNumber() {
         return mActivePhone.getIccSerialNumber();
     }
@@ -1444,6 +1476,9 @@
     public ImsPhone relinquishOwnershipOfImsPhone() { return null; }
 
     @Override
+    public void startMonitoringImsService() {}
+
+    @Override
     public void acquireOwnershipOfImsPhone(ImsPhone imsPhone) { }
 
     @Override
@@ -1470,6 +1505,11 @@
     }
 
     @Override
+    public boolean isRadioOn() {
+        return mCommandsInterface.getRadioState().isOn();
+    }
+
+    @Override
     public void shutdownRadio() {
         mActivePhone.shutdownRadio();
     }
@@ -1485,8 +1525,18 @@
     }
 
     @Override
-    public int getSupportedRadioAccessFamily() {
-        return mCommandsInterface.getSupportedRadioAccessFamily();
+    public String getModemUuId() {
+        return mActivePhone.getModemUuId();
+    }
+
+    @Override
+    public RadioCapability getRadioCapability() {
+        return mActivePhone.getRadioCapability();
+    }
+
+    @Override
+    public void radioCapabilityUpdated(RadioCapability rc) {
+        mActivePhone.radioCapabilityUpdated(rc);
     }
 
     @Override
@@ -1507,6 +1557,52 @@
         return mActivePhone.isImsRegistered();
     }
 
+    /**
+     * Determines if video calling is enabled for the IMS phone.
+     *
+     * @return {@code true} if video calling is enabled.
+     */
+    @Override
+    public boolean isVideoEnabled() {
+        return mActivePhone.isVideoEnabled();
+    }
+
+    /**
+     * Returns the status of Link Capacity Estimation (LCE) service.
+     */
+    @Override
+    public int getLceStatus() {
+        return mActivePhone.getLceStatus();
+    }
+
+    @Override
+    public Locale getLocaleFromSimAndCarrierPrefs() {
+        return mActivePhone.getLocaleFromSimAndCarrierPrefs();
+    }
+
+    @Override
+    public void getModemActivityInfo(Message response)  {
+        mActivePhone.getModemActivityInfo(response);
+    }
+
+    /**
+     * @return true if we are in the emergency call back mode. This is a period where
+     * the phone should be using as little power as possible and be ready to receive an
+     * incoming call from the emergency operator.
+     */
+    @Override
+    public boolean isInEcm() {
+        return mActivePhone.isInEcm();
+    }
+
+    public boolean isVolteEnabled() {
+        return mActivePhone.isVolteEnabled();
+    }
+
+    public boolean isWifiCallingEnabled() {
+        return mActivePhone.isWifiCallingEnabled();
+    }
+
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         try {
             ((PhoneBase)mActivePhone).dump(fd, pw, args);
diff --git a/src/java/com/android/internal/telephony/PhoneSubInfo.java b/src/java/com/android/internal/telephony/PhoneSubInfo.java
index 264d335..71222ae 100755
--- a/src/java/com/android/internal/telephony/PhoneSubInfo.java
+++ b/src/java/com/android/internal/telephony/PhoneSubInfo.java
@@ -18,10 +18,10 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
+import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Binder;
-import android.os.RemoteException;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.Rlog;
 
@@ -36,6 +36,7 @@
 
     private Phone mPhone;
     private Context mContext;
+    private AppOpsManager mAppOps;
     private static final String READ_PHONE_STATE =
         android.Manifest.permission.READ_PHONE_STATE;
     // TODO: change getCompleteVoiceMailNumber() to require READ_PRIVILEGED_PHONE_STATE
@@ -47,6 +48,7 @@
     public PhoneSubInfo(Phone phone) {
         mPhone = phone;
         mContext = phone.getContext();
+        mAppOps = mContext.getSystemService(AppOpsManager.class);
     }
 
     public void dispose() {
@@ -65,24 +67,30 @@
     /**
      * Retrieves the unique device ID, e.g., IMEI for GSM phones and MEID for CDMA phones.
      */
-    public String getDeviceId() {
-        mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+    public String getDeviceId(String callingPackage) {
+        if (!checkReadPhoneState(callingPackage, "getDeviceId")) {
+            return null;
+        }
         return mPhone.getDeviceId();
     }
 
     /**
      * Retrieves the IMEI.
      */
-    public String getImei() {
-        mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+    public String getImei(String callingPackage) {
+        if (!checkReadPhoneState(callingPackage, "getImei")) {
+            return null;
+        }
         return mPhone.getImei();
     }
 
     /**
      * Retrieves the NAI.
      */
-    public String getNai() {
-        mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+    public String getNai(String callingPackage) {
+        if (!checkReadPhoneState(callingPackage, "getNai")) {
+            return null;
+        }
         return mPhone.getNai();
     }
 
@@ -90,64 +98,82 @@
      * Retrieves the software version number for the device, e.g., IMEI/SV
      * for GSM phones.
      */
-    public String getDeviceSvn() {
-        mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+    public String getDeviceSvn(String callingPackage) {
+        if (!checkReadPhoneState(callingPackage, "getDeviceSvn")) {
+            return null;
+        }
+
         return mPhone.getDeviceSvn();
     }
 
     /**
      * Retrieves the unique subscriber ID, e.g., IMSI for GSM phones.
      */
-    public String getSubscriberId() {
-        mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+    public String getSubscriberId(String callingPackage) {
+        if (!checkReadPhoneState(callingPackage, "getSubscriberId")) {
+            return null;
+        }
         return mPhone.getSubscriberId();
     }
 
     /**
      * Retrieves the Group Identifier Level1 for GSM phones.
      */
-    public String getGroupIdLevel1() {
-        mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+    public String getGroupIdLevel1(String callingPackage) {
+        if (!checkReadPhoneState(callingPackage, "getGroupIdLevel1")) {
+            return null;
+        }
         return mPhone.getGroupIdLevel1();
     }
 
     /**
      * Retrieves the serial number of the ICC, if applicable.
      */
-    public String getIccSerialNumber() {
-        mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+    public String getIccSerialNumber(String callingPackage) {
+        if (!checkReadPhoneState(callingPackage, "getIccSerialNumber")) {
+            return null;
+        }
         return mPhone.getIccSerialNumber();
     }
 
     /**
      * Retrieves the phone number string for line 1.
      */
-    public String getLine1Number() {
-        mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+    public String getLine1Number(String callingPackage) {
+        // This is open to apps with WRITE_SMS.
+        if (!checkReadPhoneNumber(callingPackage, "getLine1Number")) {
+            return null;
+        }
         return mPhone.getLine1Number();
     }
 
     /**
      * Retrieves the alpha identifier for line 1.
      */
-    public String getLine1AlphaTag() {
-        mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+    public String getLine1AlphaTag(String callingPackage) {
+        if (!checkReadPhoneState(callingPackage, "getLine1AlphaTag")) {
+            return null;
+        }
         return mPhone.getLine1AlphaTag();
     }
 
     /**
      * Retrieves the MSISDN string.
      */
-    public String getMsisdn() {
-        mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+    public String getMsisdn(String callingPackage) {
+        if (!checkReadPhoneState(callingPackage, "getMsisdn")) {
+            return null;
+        }
         return mPhone.getMsisdn();
     }
 
     /**
      * Retrieves the voice mail number.
      */
-    public String getVoiceMailNumber() {
-        mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+    public String getVoiceMailNumber(String callingPackage) {
+        if (!checkReadPhoneState(callingPackage, "getVoiceMailNumber")) {
+            return null;
+        }
         String number = PhoneNumberUtils.extractNetworkPortion(mPhone.getVoiceMailNumber());
         if (VDBG) log("VM: PhoneSubInfo.getVoiceMailNUmber: " + number);
         return number;
@@ -169,8 +195,10 @@
     /**
      * Retrieves the alpha identifier associated with the voice mail number.
      */
-    public String getVoiceMailAlphaTag() {
-        mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, "Requires READ_PHONE_STATE");
+    public String getVoiceMailAlphaTag(String callingPackage) {
+        if (!checkReadPhoneState(callingPackage, "getVoiceMailAlphaTag")) {
+            return null;
+        }
         return mPhone.getVoiceMailAlphaTag();
     }
 
@@ -336,4 +364,40 @@
         pw.println("  Phone Type = " + mPhone.getPhoneName());
         pw.println("  Device ID = " + mPhone.getDeviceId());
     }
+
+    private boolean checkReadPhoneState(String callingPackage, String message) {
+        try {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, message);
+
+            // SKIP checking run-time OP_READ_PHONE_STATE since self or using PRIVILEGED
+            return true;
+        } catch (SecurityException e) {
+            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE,
+                    message);
+        }
+
+        return mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
+            callingPackage) == AppOpsManager.MODE_ALLOWED;
+    }
+
+
+    /**
+     * Besides READ_PHONE_STATE, WRITE_SMS also allows apps to get phone numbers.
+     */
+    private boolean checkReadPhoneNumber(String callingPackage, String message) {
+        // Default SMS app can always read it.
+        if (mAppOps.noteOp(AppOpsManager.OP_WRITE_SMS,
+                Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED) {
+            return true;
+        }
+        try {
+            return checkReadPhoneState(callingPackage, message);
+        } catch (SecurityException e) {
+            // Can be read with READ_SMS too.
+            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_SMS, message);
+            return mAppOps.noteOp(AppOpsManager.OP_READ_SMS,
+                    Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED;
+        }
+    }
 }
diff --git a/src/java/com/android/internal/telephony/PhoneSubInfoController.java b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
index 3394d20..abd7860 100644
--- a/src/java/com/android/internal/telephony/PhoneSubInfoController.java
+++ b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
@@ -18,6 +18,9 @@
 
 package com.android.internal.telephony;
 
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.os.Binder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.telephony.SubscriptionManager;
@@ -25,30 +28,67 @@
 import android.telephony.TelephonyManager;
 
 import java.lang.NullPointerException;
-import java.lang.ArrayIndexOutOfBoundsException;
-
-import com.android.internal.telephony.IPhoneSubInfo;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneSubInfoProxy;
 
 public class PhoneSubInfoController extends IPhoneSubInfo.Stub {
     private static final String TAG = "PhoneSubInfoController";
-    private Phone[] mPhone;
+    private final Phone[] mPhone;
+    private final Context mContext;
+    private final AppOpsManager mAppOps;
 
-    public PhoneSubInfoController(Phone[] phone) {
-        mPhone = phone;
+    public PhoneSubInfoController(Phone[] phones) {
+        mPhone = phones;
+        Context context = null;
+        AppOpsManager appOpsManager = null;
+        for (Phone phone : mPhone) {
+            if (phone != null) {
+                context = phone.getContext();
+                appOpsManager = context.getSystemService(AppOpsManager.class);
+                break;
+            }
+        }
+        mContext = context;
+        mAppOps = appOpsManager;
         if (ServiceManager.getService("iphonesubinfo") == null) {
             ServiceManager.addService("iphonesubinfo", this);
         }
     }
 
+    // try-state
+    // either have permission (true), don't (exception), or explicitly turned off (false)
+    private boolean canReadPhoneState(String callingPackage, String message) {
+        if (mContext == null) return false;
+        try {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, message);
 
-    public String getDeviceId() {
-        return getDeviceIdForPhone(SubscriptionManager.getPhoneId(getDefaultSubscription()));
+            // SKIP checking for run-time permission since caller or self has PRIVILEDGED permission
+            return true;
+        } catch (SecurityException e) {
+            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE,
+                    message);
+        }
+
+
+
+        if (mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
+                callingPackage) != AppOpsManager.MODE_ALLOWED) {
+            return false;
+        }
+
+        return true;
     }
 
-    public String getDeviceIdForPhone(int phoneId) {
-        Phone phone = getPhone(phoneId);
+    public String getDeviceId(String callingPackage) {
+        return getDeviceIdForPhone(SubscriptionManager.getPhoneId(getDefaultSubscription()),
+                callingPackage);
+    }
+
+    public String getDeviceIdForPhone(int phoneId, String callingPackage) {
+        if (!canReadPhoneState(callingPackage, "getDeviceId")) {
+            return null;
+        }
+
+        final Phone phone = getPhone(phoneId);
         if (phone != null) {
             phone.getContext().enforceCallingOrSelfPermission(
                     android.Manifest.permission.READ_PHONE_STATE,
@@ -60,10 +100,10 @@
         }
     }
 
-    public String getNaiForSubscriber(int subId) {
+    public String getNaiForSubscriber(int subId, String callingPackage) {
         PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
         if (phoneSubInfoProxy != null) {
-            return phoneSubInfoProxy.getNai();
+            return phoneSubInfoProxy.getNai(callingPackage);
         } else {
             Rlog.e(TAG,"getNai phoneSubInfoProxy is null" +
                       " for Subscription:" + subId);
@@ -71,10 +111,10 @@
         }
     }
 
-    public String getImeiForSubscriber(int subId) {
+    public String getImeiForSubscriber(int subId, String callingPackage) {
         PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
         if (phoneSubInfoProxy != null) {
-            return phoneSubInfoProxy.getImei();
+            return phoneSubInfoProxy.getImei(callingPackage);
         } else {
             Rlog.e(TAG,"getDeviceId phoneSubInfoProxy is null" +
                     " for Subscription:" + subId);
@@ -82,28 +122,28 @@
         }
     }
 
-    public String getDeviceSvn() {
-        return getDeviceSvnUsingSubId(getDefaultSubscription());
+    public String getDeviceSvn(String callingPackage) {
+        return getDeviceSvnUsingSubId(getDefaultSubscription(), callingPackage);
     }
 
-    public String getDeviceSvnUsingSubId(int subId) {
+    public String getDeviceSvnUsingSubId(int subId, String callingPackage) {
         PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
         if (phoneSubInfoProxy != null) {
-            return phoneSubInfoProxy.getDeviceSvn();
+            return phoneSubInfoProxy.getDeviceSvn(callingPackage);
         } else {
             Rlog.e(TAG,"getDeviceSvn phoneSubInfoProxy is null");
             return null;
         }
     }
 
-    public String getSubscriberId() {
-        return getSubscriberIdForSubscriber(getDefaultSubscription());
+    public String getSubscriberId(String callingPackage) {
+        return getSubscriberIdForSubscriber(getDefaultSubscription(), callingPackage);
     }
 
-    public String getSubscriberIdForSubscriber(int subId) {
+    public String getSubscriberIdForSubscriber(int subId, String callingPackage) {
         PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
         if (phoneSubInfoProxy != null) {
-            return phoneSubInfoProxy.getSubscriberId();
+            return phoneSubInfoProxy.getSubscriberId(callingPackage);
         } else {
             Rlog.e(TAG,"getSubscriberId phoneSubInfoProxy is" +
                       " null for Subscription:" + subId);
@@ -114,14 +154,14 @@
     /**
      * Retrieves the serial number of the ICC, if applicable.
      */
-    public String getIccSerialNumber() {
-        return getIccSerialNumberForSubscriber(getDefaultSubscription());
+    public String getIccSerialNumber(String callingPackage) {
+        return getIccSerialNumberForSubscriber(getDefaultSubscription(), callingPackage);
     }
 
-    public String getIccSerialNumberForSubscriber(int subId) {
+    public String getIccSerialNumberForSubscriber(int subId, String callingPackage) {
         PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
         if (phoneSubInfoProxy != null) {
-            return phoneSubInfoProxy.getIccSerialNumber();
+            return phoneSubInfoProxy.getIccSerialNumber(callingPackage);
         } else {
             Rlog.e(TAG,"getIccSerialNumber phoneSubInfoProxy is" +
                       " null for Subscription:" + subId);
@@ -129,14 +169,14 @@
         }
     }
 
-    public String getLine1Number() {
-        return getLine1NumberForSubscriber(getDefaultSubscription());
+    public String getLine1Number(String callingPackage) {
+        return getLine1NumberForSubscriber(getDefaultSubscription(), callingPackage);
     }
 
-    public String getLine1NumberForSubscriber(int subId) {
+    public String getLine1NumberForSubscriber(int subId, String callingPackage) {
         PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
         if (phoneSubInfoProxy != null) {
-            return phoneSubInfoProxy.getLine1Number();
+            return phoneSubInfoProxy.getLine1Number(callingPackage);
         } else {
             Rlog.e(TAG,"getLine1Number phoneSubInfoProxy is" +
                       " null for Subscription:" + subId);
@@ -144,14 +184,14 @@
         }
     }
 
-    public String getLine1AlphaTag() {
-        return getLine1AlphaTagForSubscriber(getDefaultSubscription());
+    public String getLine1AlphaTag(String callingPackage) {
+        return getLine1AlphaTagForSubscriber(getDefaultSubscription(), callingPackage);
     }
 
-    public String getLine1AlphaTagForSubscriber(int subId) {
+    public String getLine1AlphaTagForSubscriber(int subId, String callingPackage) {
         PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
         if (phoneSubInfoProxy != null) {
-            return phoneSubInfoProxy.getLine1AlphaTag();
+            return phoneSubInfoProxy.getLine1AlphaTag(callingPackage);
         } else {
             Rlog.e(TAG,"getLine1AlphaTag phoneSubInfoProxy is" +
                       " null for Subscription:" + subId);
@@ -159,14 +199,14 @@
         }
     }
 
-    public String getMsisdn() {
-        return getMsisdnForSubscriber(getDefaultSubscription());
+    public String getMsisdn(String callingPackage) {
+        return getMsisdnForSubscriber(getDefaultSubscription(), callingPackage);
     }
 
-    public String getMsisdnForSubscriber(int subId) {
+    public String getMsisdnForSubscriber(int subId, String callingPackage) {
         PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
         if (phoneSubInfoProxy != null) {
-            return phoneSubInfoProxy.getMsisdn();
+            return phoneSubInfoProxy.getMsisdn(callingPackage);
         } else {
             Rlog.e(TAG,"getMsisdn phoneSubInfoProxy is" +
                       " null for Subscription:" + subId);
@@ -174,14 +214,14 @@
         }
     }
 
-    public String getVoiceMailNumber() {
-        return getVoiceMailNumberForSubscriber(getDefaultSubscription());
+    public String getVoiceMailNumber(String callingPackage) {
+        return getVoiceMailNumberForSubscriber(getDefaultSubscription(), callingPackage);
     }
 
-    public String getVoiceMailNumberForSubscriber(int subId) {
+    public String getVoiceMailNumberForSubscriber(int subId, String callingPackage) {
         PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
         if (phoneSubInfoProxy != null) {
-            return phoneSubInfoProxy.getVoiceMailNumber();
+            return phoneSubInfoProxy.getVoiceMailNumber(callingPackage);
         } else {
             Rlog.e(TAG,"getVoiceMailNumber phoneSubInfoProxy is" +
                       " null for Subscription:" + subId);
@@ -204,14 +244,14 @@
         }
     }
 
-    public String getVoiceMailAlphaTag() {
-        return getVoiceMailAlphaTagForSubscriber(getDefaultSubscription());
+    public String getVoiceMailAlphaTag(String callingPackage) {
+        return getVoiceMailAlphaTagForSubscriber(getDefaultSubscription(), callingPackage);
     }
 
-    public String getVoiceMailAlphaTagForSubscriber(int subId) {
+    public String getVoiceMailAlphaTagForSubscriber(int subId, String callingPackage) {
         PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
         if (phoneSubInfoProxy != null) {
-            return phoneSubInfoProxy.getVoiceMailAlphaTag();
+            return phoneSubInfoProxy.getVoiceMailAlphaTag(callingPackage);
         } else {
             Rlog.e(TAG,"getVoiceMailAlphaTag phoneSubInfoProxy is" +
                       " null for Subscription:" + subId);
@@ -283,14 +323,14 @@
         return phoneSubInfoProxy.getIccSimChallengeResponse(subId, appType, data);
     }
 
-     public String getGroupIdLevel1() {
-         return getGroupIdLevel1ForSubscriber(getDefaultSubscription());
+     public String getGroupIdLevel1(String callingPackage) {
+         return getGroupIdLevel1ForSubscriber(getDefaultSubscription(), callingPackage);
      }
 
-     public String getGroupIdLevel1ForSubscriber(int subId) {
+     public String getGroupIdLevel1ForSubscriber(int subId, String callingPackage) {
          PhoneSubInfoProxy phoneSubInfoProxy = getPhoneSubInfoProxy(subId);
          if (phoneSubInfoProxy != null) {
-             return phoneSubInfoProxy.getGroupIdLevel1();
+             return phoneSubInfoProxy.getGroupIdLevel1(callingPackage);
          } else {
              Rlog.e(TAG,"getGroupIdLevel1 phoneSubInfoProxy is" +
                        " null for Subscription:" + subId);
diff --git a/src/java/com/android/internal/telephony/PhoneSubInfoProxy.java b/src/java/com/android/internal/telephony/PhoneSubInfoProxy.java
index cedc0dc..92a1ab0 100755
--- a/src/java/com/android/internal/telephony/PhoneSubInfoProxy.java
+++ b/src/java/com/android/internal/telephony/PhoneSubInfoProxy.java
@@ -35,76 +35,76 @@
     }
 
     @Override
-    public String getDeviceId() {
-        return mPhoneSubInfo.getDeviceId();
+    public String getDeviceId(String callingPackage) {
+        return mPhoneSubInfo.getDeviceId(callingPackage);
     }
 
-    public String getImei() {
-        return mPhoneSubInfo.getImei();
+    public String getImei(String callingPackage) {
+        return mPhoneSubInfo.getImei(callingPackage);
     }
 
-    public String getNai() {
-        return mPhoneSubInfo.getNai();
+    public String getNai(String callingPackage) {
+        return mPhoneSubInfo.getNai(callingPackage);
     }
 
     @Override
-    public String getDeviceSvn() {
-        return mPhoneSubInfo.getDeviceSvn();
+    public String getDeviceSvn(String callingPackage) {
+        return mPhoneSubInfo.getDeviceSvn(callingPackage);
     }
 
     /**
      * Retrieves the unique subscriber ID, e.g., IMSI for GSM phones.
      */
     @Override
-    public String getSubscriberId() {
-        return mPhoneSubInfo.getSubscriberId();
+    public String getSubscriberId(String callingPackage) {
+        return mPhoneSubInfo.getSubscriberId(callingPackage);
     }
 
     /**
      * Retrieves the Group Identifier Level1 for GSM phones.
      */
-    public String getGroupIdLevel1() {
-        return mPhoneSubInfo.getGroupIdLevel1();
+    public String getGroupIdLevel1(String callingPackage) {
+        return mPhoneSubInfo.getGroupIdLevel1(callingPackage);
     }
 
     /**
      * Retrieves the serial number of the ICC, if applicable.
      */
     @Override
-    public String getIccSerialNumber() {
-        return mPhoneSubInfo.getIccSerialNumber();
+    public String getIccSerialNumber(String callingPackage) {
+        return mPhoneSubInfo.getIccSerialNumber(callingPackage);
     }
 
     /**
      * Retrieves the phone number string for line 1.
      */
     @Override
-    public String getLine1Number() {
-        return mPhoneSubInfo.getLine1Number();
+    public String getLine1Number(String callingPackage) {
+        return mPhoneSubInfo.getLine1Number(callingPackage);
     }
 
     /**
      * Retrieves the alpha identifier for line 1.
      */
     @Override
-    public String getLine1AlphaTag() {
-        return mPhoneSubInfo.getLine1AlphaTag();
+    public String getLine1AlphaTag(String callingPackage) {
+        return mPhoneSubInfo.getLine1AlphaTag(callingPackage);
     }
 
     /**
      * Retrieves the MSISDN Number.
      */
     @Override
-    public String getMsisdn() {
-        return mPhoneSubInfo.getMsisdn();
+    public String getMsisdn(String callingPackage) {
+        return mPhoneSubInfo.getMsisdn(callingPackage);
     }
 
     /**
      * Retrieves the voice mail number.
      */
     @Override
-    public String getVoiceMailNumber() {
-        return mPhoneSubInfo.getVoiceMailNumber();
+    public String getVoiceMailNumber(String callingPackage) {
+        return mPhoneSubInfo.getVoiceMailNumber(callingPackage);
     }
 
     /**
@@ -119,8 +119,8 @@
      * Retrieves the alpha identifier associated with the voice mail number.
      */
     @Override
-    public String getVoiceMailAlphaTag() {
-        return mPhoneSubInfo.getVoiceMailAlphaTag();
+    public String getVoiceMailAlphaTag(String callingPackage) {
+        return mPhoneSubInfo.getVoiceMailAlphaTag(callingPackage);
     }
 
     /**
@@ -152,67 +152,70 @@
     }
 
     @Override
-    public String getDeviceIdForPhone(int phoneId) throws RemoteException {
+    public String getDeviceIdForPhone(int phoneId, String callingPackage) throws RemoteException {
         // FIXME: getDeviceIdForPhone
         return null;
     }
 
     @Override
-    public String getImeiForSubscriber(int subId) throws RemoteException {
+    public String getImeiForSubscriber(int subId, String callingPackage) throws RemoteException {
         // FIXME: getImeiForSubscriber
         return null;
     }
 
     @Override
-    public String getDeviceSvnUsingSubId(int subId) throws RemoteException {
+    public String getDeviceSvnUsingSubId(int subId, String callingPackage) throws RemoteException {
         // FIXME: getDeviceSvnUsingSubId
         return null;
     }
 
     @Override
-    public String getNaiForSubscriber(int subId) throws RemoteException {
+    public String getNaiForSubscriber(int subId, String callingPackage) throws RemoteException {
         // FIXME: NaiForSubscriber
         return null;
     }
 
     @Override
-    public String getSubscriberIdForSubscriber(int subId) throws RemoteException {
+    public String getSubscriberIdForSubscriber(int subId, String callingPackage)
+            throws RemoteException {
         // FIXME: getSubscriberIdForSubscriber
         return null;
     }
 
     @Override
-    public String getGroupIdLevel1ForSubscriber(int subId) throws RemoteException {
+    public String getGroupIdLevel1ForSubscriber(int subId, String callingPackage)
+            throws RemoteException {
         // FIXME: getGroupIdLevel1ForSubscriber
         return null;
     }
 
     @Override
-    public String getIccSerialNumberForSubscriber(int subId) throws RemoteException {
+    public String getIccSerialNumberForSubscriber(int subId, String callingPackage)
+            throws RemoteException {
         // FIXME: getIccSerialNumberForSubscriber
         return null;
     }
 
     @Override
-    public String getLine1NumberForSubscriber(int subId) throws RemoteException {
+    public String getLine1NumberForSubscriber(int subId, String callingPackage) throws RemoteException {
         // FIXME: getLine1NumberForSubscriber
         return null;
     }
 
     @Override
-    public String getLine1AlphaTagForSubscriber(int subId) throws RemoteException {
+    public String getLine1AlphaTagForSubscriber(int subId, String callingPackage) throws RemoteException {
         // FIXME: getLine1AlphaTagForSubscriber
         return null;
     }
 
     @Override
-    public String getMsisdnForSubscriber(int subId) throws RemoteException {
+    public String getMsisdnForSubscriber(int subId, String callingPackage) throws RemoteException {
         // FIXME: getMsisdnForSubscriber
         return null;
     }
 
     @Override
-    public String getVoiceMailNumberForSubscriber(int subId) throws RemoteException {
+    public String getVoiceMailNumberForSubscriber(int subId, String callingPackage) throws RemoteException {
         // FIXME: getVoiceMailNumberForSubscriber
         return null;
     }
@@ -224,7 +227,7 @@
     }
 
     @Override
-    public String getVoiceMailAlphaTagForSubscriber(int subId) throws RemoteException {
+    public String getVoiceMailAlphaTagForSubscriber(int subId, String callingPackage) throws RemoteException {
         // FIXME: getVoiceMailAlphaTagForSubscriber
         return null;
     }
@@ -253,7 +256,7 @@
      * Returns null if the Authentification hasn't been successed or isn't present iphonesubinfo.
      * @return the response of ISIM Authetification, or null if not available
      * @deprecated
-     * @see getIccSimChallengeResponse
+     * @see #getIccSimChallengeResponse
      */
     public String getIsimChallengeResponse(String nonce) {
         return mPhoneSubInfo.getIsimChallengeResponse(nonce);
diff --git a/src/java/com/android/internal/telephony/ProxyController.java b/src/java/com/android/internal/telephony/ProxyController.java
index aa4976a..0b84800 100644
--- a/src/java/com/android/internal/telephony/ProxyController.java
+++ b/src/java/com/android/internal/telephony/ProxyController.java
@@ -29,6 +29,7 @@
 import android.telephony.RadioAccessFamily;
 import android.telephony.Rlog;
 import android.telephony.TelephonyManager;
+import android.util.Log;
 
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.Phone;
@@ -42,6 +43,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.HashSet;
 
 public class ProxyController {
     static final String LOG_TAG = "ProxyController";
@@ -50,6 +52,7 @@
     private static final int EVENT_START_RC_RESPONSE        = 2;
     private static final int EVENT_APPLY_RC_RESPONSE        = 3;
     private static final int EVENT_FINISH_RC_RESPONSE       = 4;
+    private static final int EVENT_TIMEOUT                  = 5;
 
     private static final int SET_RC_STATUS_IDLE             = 0;
     private static final int SET_RC_STATUS_STARTING         = 1;
@@ -90,8 +93,10 @@
     // record each phone's set radio capability status
     private int[] mSetRadioAccessFamilyStatus;
     private int mRadioAccessFamilyStatusCounter;
+    private boolean mTransactionFailed = false;
 
-    private String[] mLogicalModemIds;
+    private String[] mCurrentLogicalModemIds;
+    private String[] mNewLogicalModemIds;
 
     // Allows the generation of unique Id's for radio capability request session  id
     private AtomicInteger mUniqueIdGenerator = new AtomicInteger(new Random().nextInt());
@@ -105,8 +110,6 @@
     private int[] mNewRadioAccessFamily;
     private int[] mOldRadioAccessFamily;
 
-    // runnable for radio capability request timeout handling
-    RadioCapabilityRunnable mSetRadioCapabilityRunnable;
 
     //***** Class Methods
     public static ProxyController getInstance(Context context, PhoneProxy[] phoneProxy,
@@ -137,14 +140,8 @@
         mSetRadioAccessFamilyStatus = new int[mProxyPhones.length];
         mNewRadioAccessFamily = new int[mProxyPhones.length];
         mOldRadioAccessFamily = new int[mProxyPhones.length];
-        mLogicalModemIds = new String[mProxyPhones.length];
-
-        // TODO Get logical modem ids assume its just the phoneId as a string for now
-        for (int i = 0; i < mProxyPhones.length; i++) {
-            mLogicalModemIds[i] = Integer.toString(i);
-        }
-
-        mSetRadioCapabilityRunnable = new RadioCapabilityRunnable();
+        mCurrentLogicalModemIds = new String[mProxyPhones.length];
+        mNewLogicalModemIds = new String[mProxyPhones.length];
 
         // wake lock for set radio capability
         PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
@@ -200,7 +197,8 @@
             Phone activePhone = mProxyPhones[phoneId].getActivePhone();
             return ((PhoneBase) activePhone).mDcTracker.isDisconnected();
         } else {
-            return false;
+            // if we can't find a phone for the given subId, it is disconnected.
+            return true;
         }
     }
 
@@ -230,13 +228,10 @@
         if (rafs.length != mProxyPhones.length) {
             throw new RuntimeException("Length of input rafs must equal to total phone count");
         }
-
         // Check if there is any ongoing transaction and throw an exception if there
         // is one as this is a programming error.
         synchronized (mSetRadioAccessFamilyStatus) {
             for (int i = 0; i < mProxyPhones.length; i++) {
-                logd("setRadioCapability: mSetRadioAccessFamilyStatus[" + i + "]="
-                        + mSetRadioAccessFamilyStatus[i]);
                 if (mSetRadioAccessFamilyStatus[i] != SET_RC_STATUS_IDLE) {
                     // TODO: The right behaviour is to cancel previous request and send this.
                     loge("setRadioCapability: Phone[" + i + "] is not idle. Rejecting request.");
@@ -245,23 +240,41 @@
             }
         }
 
+        // Check we actually need to do anything
+        boolean same = true;
+        for (int i = 0; i < mProxyPhones.length; i++) {
+            if (mProxyPhones[i].getRadioAccessFamily() != rafs[i].getRadioAccessFamily()) {
+                same = false;
+            }
+        }
+        if (same) {
+            // All phones are already set to the requested raf
+            logd("setRadioCapability: Already in requested configuration, nothing to do.");
+            // It isn't really an error, so return true - everything is OK.
+            return true;
+        }
+
         // Clear to be sure we're in the initial state
         clearTransaction();
 
-        // A new sessionId for this transaction
-        mRadioCapabilitySessionId = mUniqueIdGenerator.getAndIncrement();
-
         // Keep a wake lock until we finish radio capability changed
         mWakeLock.acquire();
 
+        return doSetRadioCapabilities(rafs);
+    }
+
+    private boolean doSetRadioCapabilities(RadioAccessFamily[] rafs) {
+        // A new sessionId for this transaction
+        mRadioCapabilitySessionId = mUniqueIdGenerator.getAndIncrement();
+
         // Start timer to make sure all phones respond within a specific time interval.
         // Will send FINISH if a timeout occurs.
-        mSetRadioCapabilityRunnable.setTimeoutState(mRadioCapabilitySessionId);
-        mHandler.postDelayed(mSetRadioCapabilityRunnable, SET_RC_TIMEOUT_WAITING_MSEC);
+        Message msg = mHandler.obtainMessage(EVENT_TIMEOUT, mRadioCapabilitySessionId, 0);
+        mHandler.sendMessageDelayed(msg, SET_RC_TIMEOUT_WAITING_MSEC);
 
         synchronized (mSetRadioAccessFamilyStatus) {
             logd("setRadioCapability: new request session id=" + mRadioCapabilitySessionId);
-            mRadioAccessFamilyStatusCounter = rafs.length;
+            resetRadioAccessFamilyStatusCounter();
             for (int i = 0; i < rafs.length; i++) {
                 int phoneId = rafs[i].getPhoneId();
                 logd("setRadioCapability: phoneId=" + phoneId + " status=STARTING");
@@ -269,9 +282,14 @@
                 mOldRadioAccessFamily[phoneId] = mProxyPhones[phoneId].getRadioAccessFamily();
                 int requestedRaf = rafs[i].getRadioAccessFamily();
                 // TODO Set the new radio access family to the maximum of the requested & supported
-                // int supportedRaf = mProxyPhones[i].getSupportedRadioAccessFamily();
+                // int supportedRaf = mProxyPhones[i].getRadioAccessFamily();
                 // mNewRadioAccessFamily[phoneId] = requestedRaf & supportedRaf;
                 mNewRadioAccessFamily[phoneId] = requestedRaf;
+
+                mCurrentLogicalModemIds[phoneId] = mProxyPhones[phoneId].getModemUuId();
+                // get the logical mode corresponds to new raf requested and pass the
+                // same as part of SET_RADIO_CAP APPLY phase
+                mNewLogicalModemIds[phoneId] = getLogicalModemIdFromRaf(requestedRaf);
                 logd("setRadioCapability: mOldRadioAccessFamily[" + phoneId + "]="
                         + mOldRadioAccessFamily[phoneId]);
                 logd("setRadioCapability: mNewRadioAccessFamily[" + phoneId + "]="
@@ -281,7 +299,7 @@
                         mRadioCapabilitySessionId,
                         RadioCapability.RC_PHASE_START,
                         mOldRadioAccessFamily[phoneId],
-                        mLogicalModemIds[phoneId],
+                        mCurrentLogicalModemIds[phoneId],
                         RadioCapability.RC_STATUS_NONE,
                         EVENT_START_RC_RESPONSE);
             }
@@ -311,6 +329,10 @@
                     onFinishRadioCapabilityResponse(msg);
                     break;
 
+                case EVENT_TIMEOUT:
+                    onTimeoutRadioCapability(msg);
+                    break;
+
                 default:
                     break;
             }
@@ -323,6 +345,16 @@
      */
     private void onStartRadioCapabilityResponse(Message msg) {
         synchronized (mSetRadioAccessFamilyStatus) {
+            AsyncResult ar = (AsyncResult)msg.obj;
+            if (ar.exception != null) {
+                // just abort now.  They didn't take our start so we don't have to revert
+                logd("onStartRadioCapabilityResponse got exception=" + ar.exception);
+                mRadioCapabilitySessionId = mUniqueIdGenerator.getAndIncrement();
+                Intent intent = new Intent(TelephonyIntents.ACTION_SET_RADIO_CAPABILITY_FAILED);
+                mContext.sendBroadcast(intent);
+                clearTransaction();
+                return;
+            }
             RadioCapability rc = (RadioCapability) ((AsyncResult) msg.obj).result;
             if ((rc == null) || (rc.getSession() != mRadioCapabilitySessionId)) {
                 logd("onStartRadioCapabilityResponse: Ignore session=" + mRadioCapabilitySessionId
@@ -335,27 +367,35 @@
                 logd("onStartRadioCapabilityResponse: Error response session=" + rc.getSession());
                 logd("onStartRadioCapabilityResponse: phoneId=" + id + " status=FAIL");
                 mSetRadioAccessFamilyStatus[id] = SET_RC_STATUS_FAIL;
+                mTransactionFailed = true;
             } else {
                 logd("onStartRadioCapabilityResponse: phoneId=" + id + " status=STARTED");
                 mSetRadioAccessFamilyStatus[id] = SET_RC_STATUS_STARTED;
             }
 
             if (mRadioAccessFamilyStatusCounter == 0) {
-                resetRadioAccessFamilyStatusCounter();
-                boolean success = checkAllRadioCapabilitySuccess();
-                logd("onStartRadioCapabilityResponse: success=" + success);
-                if (!success) {
-                    issueFinish(RadioCapability.RC_STATUS_FAIL,
-                            mRadioCapabilitySessionId);
+                HashSet<String> modemsInUse = new HashSet<String>(mNewLogicalModemIds.length);
+                for (String modemId : mNewLogicalModemIds) {
+                    if (!modemsInUse.add(modemId)) {
+                        mTransactionFailed = true;
+                        Log.wtf(LOG_TAG, "ERROR: sending down the same id for different phones");
+                    }
+                }
+                logd("onStartRadioCapabilityResponse: success=" + !mTransactionFailed);
+                if (mTransactionFailed) {
+                    // Sends a variable number of requests, so don't resetRadioAccessFamilyCounter
+                    // here.
+                    issueFinish(mRadioCapabilitySessionId);
                 } else {
                     // All logical modem accepted the new radio access family, issue the APPLY
+                    resetRadioAccessFamilyStatusCounter();
                     for (int i = 0; i < mProxyPhones.length; i++) {
                         sendRadioCapabilityRequest(
                             i,
                             mRadioCapabilitySessionId,
                             RadioCapability.RC_PHASE_APPLY,
                             mNewRadioAccessFamily[i],
-                            mLogicalModemIds[i],
+                            mNewLogicalModemIds[i],
                             RadioCapability.RC_STATUS_NONE,
                             EVENT_APPLY_RC_RESPONSE);
 
@@ -385,6 +425,7 @@
                 int id = rc.getPhoneId();
                 logd("onApplyRadioCapabilityResponse: phoneId=" + id + " status=FAIL");
                 mSetRadioAccessFamilyStatus[id] = SET_RC_STATUS_FAIL;
+                mTransactionFailed = true;
             }
         } else {
             logd("onApplyRadioCapabilityResponse: Valid start expecting notification rc=" + rc);
@@ -416,25 +457,20 @@
                     (rc.getStatus() == RadioCapability.RC_STATUS_FAIL)) {
                 logd("onNotificationRadioCapabilityChanged: phoneId=" + id + " status=FAIL");
                 mSetRadioAccessFamilyStatus[id] = SET_RC_STATUS_FAIL;
+                mTransactionFailed = true;
             } else {
                 logd("onNotificationRadioCapabilityChanged: phoneId=" + id + " status=SUCCESS");
                 mSetRadioAccessFamilyStatus[id] = SET_RC_STATUS_SUCCESS;
+                // The modems may have been restarted and forgotten this
+                mDctController.retryAttach(id);
+                mProxyPhones[id].radioCapabilityUpdated(rc);
             }
 
             mRadioAccessFamilyStatusCounter--;
             if (mRadioAccessFamilyStatusCounter == 0) {
-                logd("onNotificationRadioCapabilityChanged: removing callback from handler");
-                mHandler.removeCallbacks(mSetRadioCapabilityRunnable);
-                resetRadioAccessFamilyStatusCounter();
-                boolean success = checkAllRadioCapabilitySuccess();
-                logd("onNotificationRadioCapabilityChanged: APPLY URC success=" + success);
-                int status;
-                if (success) {
-                    status = RadioCapability.RC_STATUS_SUCCESS;
-                } else {
-                    status = RadioCapability.RC_STATUS_FAIL;
-                }
-                issueFinish(status, mRadioCapabilitySessionId);
+                logd("onNotificationRadioCapabilityChanged: APPLY URC success=" +
+                        mTransactionFailed);
+                issueFinish(mRadioCapabilitySessionId);
             }
         }
     }
@@ -460,29 +496,49 @@
         }
     }
 
-    private void issueFinish(int status, int sessionId) {
+    private void onTimeoutRadioCapability(Message msg) {
+        if (msg.arg1 != mRadioCapabilitySessionId) {
+           logd("RadioCapability timeout: Ignore msg.arg1=" + msg.arg1 +
+                   "!= mRadioCapabilitySessionId=" + mRadioCapabilitySessionId);
+            return;
+        }
+
+        synchronized(mSetRadioAccessFamilyStatus) {
+            // timed-out.  Clean up as best we can
+            for (int i = 0; i < mProxyPhones.length; i++) {
+                logd("RadioCapability timeout: mSetRadioAccessFamilyStatus[" + i + "]=" +
+                        mSetRadioAccessFamilyStatus[i]);
+            }
+
+            // Increment the sessionId as we are completing the transaction below
+            // so we don't want it completed when the FINISH phase is done.
+            int uniqueDifferentId = mUniqueIdGenerator.getAndIncrement();
+            // send FINISH request with fail status and then uniqueDifferentId
+            mTransactionFailed = true;
+            issueFinish(uniqueDifferentId);
+        }
+    }
+
+    private void issueFinish(int sessionId) {
         // Issue FINISH
         synchronized(mSetRadioAccessFamilyStatus) {
             for (int i = 0; i < mProxyPhones.length; i++) {
-                if (mSetRadioAccessFamilyStatus[i] != SET_RC_STATUS_FAIL) {
-                    logd("issueFinish: phoneId=" + i + " sessionId=" + sessionId
-                            + " status=" + status);
-                    sendRadioCapabilityRequest(
+                logd("issueFinish: phoneId=" + i + " sessionId=" + sessionId
+                        + " mTransactionFailed=" + mTransactionFailed);
+                mRadioAccessFamilyStatusCounter++;
+                sendRadioCapabilityRequest(
                         i,
                         sessionId,
                         RadioCapability.RC_PHASE_FINISH,
                         mOldRadioAccessFamily[i],
-                        mLogicalModemIds[i],
-                        status,
+                        mCurrentLogicalModemIds[i],
+                        (mTransactionFailed ? RadioCapability.RC_STATUS_FAIL :
+                        RadioCapability.RC_STATUS_SUCCESS),
                         EVENT_FINISH_RC_RESPONSE);
-                    if (status == RadioCapability.RC_STATUS_FAIL) {
-                        logd("issueFinish: phoneId: " + i + " status: FAIL");
-                        // At least one failed, mark them all failed.
-                        mSetRadioAccessFamilyStatus[i] = SET_RC_STATUS_FAIL;
-                    }
-                } else {
-                    logd("issueFinish: Ignore already FAIL, Phone" + i + " sessionId=" + sessionId
-                            + " status=" + status);
+                if (mTransactionFailed) {
+                    logd("issueFinish: phoneId: " + i + " status: FAIL");
+                    // At least one failed, mark them all failed.
+                    mSetRadioAccessFamilyStatus[i] = SET_RC_STATUS_FAIL;
                 }
             }
         }
@@ -491,9 +547,8 @@
     private void completeRadioCapabilityTransaction() {
         // Create the intent to broadcast
         Intent intent;
-        boolean success = checkAllRadioCapabilitySuccess();
-        logd("onFinishRadioCapabilityResponse: success=" + success);
-        if (success) {
+        logd("onFinishRadioCapabilityResponse: success=" + !mTransactionFailed);
+        if (!mTransactionFailed) {
             ArrayList<RadioAccessFamily> phoneRAFList = new ArrayList<RadioAccessFamily>();
             for (int i = 0; i < mProxyPhones.length; i++) {
                 int raf = mProxyPhones[i].getRadioAccessFamily();
@@ -504,12 +559,23 @@
             intent = new Intent(TelephonyIntents.ACTION_SET_RADIO_CAPABILITY_DONE);
             intent.putParcelableArrayListExtra(TelephonyIntents.EXTRA_RADIO_ACCESS_FAMILY,
                     phoneRAFList);
+
+            // make messages about the old transaction obsolete (specifically the timeout)
+            mRadioCapabilitySessionId = mUniqueIdGenerator.getAndIncrement();
+
+            // Reinitialize
+            clearTransaction();
         } else {
             intent = new Intent(TelephonyIntents.ACTION_SET_RADIO_CAPABILITY_FAILED);
-        }
 
-        // Reinitialize
-        clearTransaction();
+            // now revert.
+            mTransactionFailed = false;
+            RadioAccessFamily[] rafs = new RadioAccessFamily[mProxyPhones.length];
+            for (int phoneId = 0; phoneId < mProxyPhones.length; phoneId++) {
+                rafs[phoneId] = new RadioAccessFamily(phoneId, mOldRadioAccessFamily[phoneId]);
+            }
+            doSetRadioCapabilities(rafs);
+        }
 
         // Broadcast that we're done
         mContext.sendBroadcast(intent);
@@ -524,6 +590,7 @@
                 mSetRadioAccessFamilyStatus[i] = SET_RC_STATUS_IDLE;
                 mOldRadioAccessFamily[i] = 0;
                 mNewRadioAccessFamily[i] = 0;
+                mTransactionFailed = false;
             }
 
             if (mWakeLock.isHeld()) {
@@ -532,17 +599,6 @@
         }
     }
 
-    private boolean checkAllRadioCapabilitySuccess() {
-        synchronized(mSetRadioAccessFamilyStatus) {
-            for (int i = 0; i < mProxyPhones.length; i++) {
-                if (mSetRadioAccessFamilyStatus[i] == SET_RC_STATUS_FAIL) {
-                    return false;
-                }
-            }
-            return true;
-        }
-    }
-
     private void resetRadioAccessFamilyStatusCounter() {
         mRadioAccessFamilyStatusCounter = mProxyPhones.length;
     }
@@ -555,47 +611,53 @@
                 requestRC, mHandler.obtainMessage(eventId));
     }
 
-    /**
-     * RadioCapabilityRunnable is used to check
-     * if radio capability request's response is out of date.
-     * <p>
-     * Note that the setRadioCapability will be stopped directly and send FINISH
-     * with fail status to all logical modems. and send out fail intent
-     *
-     */
-    private class RadioCapabilityRunnable implements Runnable {
-        private int mSessionId;
-        public  RadioCapabilityRunnable() {
-        }
+    // This method will return max number of raf bits supported from the raf
+    // values currently stored in all phone objects
+    public int getMaxRafSupported() {
+        int[] numRafSupported = new int[mProxyPhones.length];
+        int maxNumRafBit = 0;
+        int maxRaf = RadioAccessFamily.RAF_UNKNOWN;
 
-        public void setTimeoutState(int sessionId) {
-            mSessionId = sessionId;
-        }
-
-        @Override
-        public void run() {
-            if (mSessionId != mRadioCapabilitySessionId) {
-                logd("RadioCapability timeout: Ignore mSessionId=" + mSessionId
-                       + "!= mRadioCapabilitySessionId=" + mRadioCapabilitySessionId);
-                return;
-            }
-
-            synchronized(mSetRadioAccessFamilyStatus) {
-                for (int i = 0; i < mProxyPhones.length; i++) {
-                    logd("RadioCapability timeout: mSetRadioAccessFamilyStatus[" + i + "]=" +
-                            mSetRadioAccessFamilyStatus[i]);
-                }
-
-                // Increment the sessionId as we are completing the transaction below
-                // so we don't want it completed when the FINISH phase is done.
-                int uniqueDifferentId = mUniqueIdGenerator.getAndIncrement();
-
-                // send FINISH request with fail status and then uniqueDifferentId
-                issueFinish(RadioCapability.RC_STATUS_FAIL,
-                        uniqueDifferentId);
-                completeRadioCapabilityTransaction();
+        for (int len = 0; len < mProxyPhones.length; len++) {
+            numRafSupported[len] = Integer.bitCount(mProxyPhones[len].getRadioAccessFamily());
+            if (maxNumRafBit < numRafSupported[len]) {
+                maxNumRafBit = numRafSupported[len];
+                maxRaf = mProxyPhones[len].getRadioAccessFamily();
             }
         }
+
+        return maxRaf;
+    }
+
+    // This method will return minimum number of raf bits supported from the raf
+    // values currently stored in all phone objects
+    public int getMinRafSupported() {
+        int[] numRafSupported = new int[mProxyPhones.length];
+        int minNumRafBit = 0;
+        int minRaf = RadioAccessFamily.RAF_UNKNOWN;
+
+        for (int len = 0; len < mProxyPhones.length; len++) {
+            numRafSupported[len] = Integer.bitCount(mProxyPhones[len].getRadioAccessFamily());
+            if ((minNumRafBit == 0) || (minNumRafBit > numRafSupported[len])) {
+                minNumRafBit = numRafSupported[len];
+                minRaf = mProxyPhones[len].getRadioAccessFamily();
+            }
+        }
+        return minRaf;
+    }
+
+    // This method checks current raf values stored in all phones and
+    // whicheve phone raf matches with input raf, returns modemId from that phone
+    private String getLogicalModemIdFromRaf(int raf) {
+        String modemUuid = null;
+
+        for (int phoneId = 0; phoneId < mProxyPhones.length; phoneId++) {
+            if (mProxyPhones[phoneId].getRadioAccessFamily() == raf) {
+                modemUuid = mProxyPhones[phoneId].getModemUuId();
+                break;
+            }
+        }
+        return modemUuid;
     }
 
     private void logd(String string) {
diff --git a/src/java/com/android/internal/telephony/RIL.java b/src/java/com/android/internal/telephony/RIL.java
index fad1228..d834eef 100644
--- a/src/java/com/android/internal/telephony/RIL.java
+++ b/src/java/com/android/internal/telephony/RIL.java
@@ -40,18 +40,22 @@
 import android.os.Message;
 import android.os.Parcel;
 import android.os.PowerManager;
+import android.os.BatteryManager;
 import android.os.SystemProperties;
 import android.os.PowerManager.WakeLock;
+import android.os.SystemClock;
 import android.provider.Settings.SettingNotFoundException;
 import android.telephony.CellInfo;
 import android.telephony.NeighboringCellInfo;
 import android.telephony.PhoneNumberUtils;
+import android.telephony.RadioAccessFamily;
 import android.telephony.Rlog;
 import android.telephony.SignalStrength;
 import android.telephony.SmsManager;
 import android.telephony.SmsMessage;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
+import android.telephony.ModemActivityInfo;
 import android.text.TextUtils;
 import android.util.SparseArray;
 import android.view.Display;
@@ -81,6 +85,7 @@
 import java.io.InputStream;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -227,6 +232,10 @@
     static final String RILJ_LOG_TAG = "RILJ";
     static final boolean RILJ_LOGD = true;
     static final boolean RILJ_LOGV = false; // STOPSHIP if true
+    static final int RADIO_SCREEN_UNSET = -1;
+    static final int RADIO_SCREEN_OFF = 0;
+    static final int RADIO_SCREEN_ON = 1;
+
 
     /**
      * Wake lock timeout should be longer than the longest timeout in
@@ -243,6 +252,8 @@
     RILReceiver mReceiver;
     Display mDefaultDisplay;
     int mDefaultDisplayState = Display.STATE_UNKNOWN;
+    int mRadioScreenState = RADIO_SCREEN_UNSET;
+    boolean mIsDevicePlugged = false;
     WakeLock mWakeLock;
     final int mWakeLockTimeout;
     // The number of wakelock requests currently active.  Don't release the lock
@@ -291,6 +302,22 @@
         @Override
         public void onDisplayChanged(int displayId) {
             if (displayId == Display.DEFAULT_DISPLAY) {
+                final int oldState = mDefaultDisplayState;
+                mDefaultDisplayState = mDefaultDisplay.getState();
+                if (mDefaultDisplayState != oldState) {
+                    updateScreenState();
+                }
+            }
+        }
+    };
+
+    private final BroadcastReceiver mBatteryStateListener = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            boolean oldState = mIsDevicePlugged;
+            // 0 means it's on battery
+            mIsDevicePlugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
+            if (mIsDevicePlugged != oldState) {
                 updateScreenState();
             }
         }
@@ -641,6 +668,13 @@
                     Context.DISPLAY_SERVICE);
             mDefaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY);
             dm.registerDisplayListener(mDisplayListener, null);
+
+            IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+            Intent batteryStatus = context.registerReceiver(mBatteryStateListener, filter);
+            if (batteryStatus != null) {
+                // 0 means it's on battery
+                mIsDevicePlugged = batteryStatus.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
+            }
         }
 
         TelephonyDevController tdc = TelephonyDevController.getInstance();
@@ -712,7 +746,10 @@
     // FIXME This API should take an AID and slot ID
     public void setDataAllowed(boolean allowed, Message result) {
         RILRequest rr = RILRequest.obtain(RIL_REQUEST_ALLOW_DATA, result);
-        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) +
+                    " allowed: " + allowed);
+        }
 
         rr.mParcel.writeInt(1);
         rr.mParcel.writeInt(allowed ? 1 : 0);
@@ -2213,17 +2250,20 @@
     // message should be deleted and replaced with more precise messages to control
     // behavior such as signal strength reporting or power managements based on
     // more robust signals.
+    /**
+     * Update the screen state. Send screen state ON if the default display is ON or the device
+     * is plugged.
+     */
     private void updateScreenState() {
-        final int oldState = mDefaultDisplayState;
-        mDefaultDisplayState = mDefaultDisplay.getState();
-        if (mDefaultDisplayState != oldState) {
-            if (oldState != Display.STATE_ON
-                    && mDefaultDisplayState == Display.STATE_ON) {
-                sendScreenState(true);
-            } else if ((oldState == Display.STATE_ON || oldState == Display.STATE_UNKNOWN)
-                        && mDefaultDisplayState != Display.STATE_ON) {
-                sendScreenState(false);
+        final int oldState = mRadioScreenState;
+        mRadioScreenState = (mDefaultDisplayState == Display.STATE_ON || mIsDevicePlugged)
+                ? RADIO_SCREEN_ON : RADIO_SCREEN_OFF;
+        if (mRadioScreenState != oldState) {
+            if (RILJ_LOGV) {
+                riljLog("defaultDisplayState: " + mDefaultDisplayState
+                        + ", isDevicePlugged: " + mIsDevicePlugged);
             }
+            sendScreenState(mRadioScreenState == RADIO_SCREEN_ON);
         }
     }
 
@@ -2442,7 +2482,7 @@
             case RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE: ret =  responseVoid(p); break;
             case RIL_REQUEST_CONFERENCE: ret =  responseVoid(p); break;
             case RIL_REQUEST_UDUB: ret =  responseVoid(p); break;
-            case RIL_REQUEST_LAST_CALL_FAIL_CAUSE: ret =  responseInts(p); break;
+            case RIL_REQUEST_LAST_CALL_FAIL_CAUSE: ret =  responseFailCause(p); break;
             case RIL_REQUEST_SIGNAL_STRENGTH: ret =  responseSignalStrength(p); break;
             case RIL_REQUEST_VOICE_REGISTRATION_STATE: ret =  responseStrings(p); break;
             case RIL_REQUEST_DATA_REGISTRATION_STATE: ret =  responseStrings(p); break;
@@ -2554,6 +2594,10 @@
             case RIL_REQUEST_SHUTDOWN: ret = responseVoid(p); break;
             case RIL_REQUEST_GET_RADIO_CAPABILITY: ret =  responseRadioCapability(p); break;
             case RIL_REQUEST_SET_RADIO_CAPABILITY: ret =  responseRadioCapability(p); break;
+            case RIL_REQUEST_START_LCE: ret = responseLceStatus(p); break;
+            case RIL_REQUEST_STOP_LCE: ret = responseLceStatus(p); break;
+            case RIL_REQUEST_PULL_LCEDATA: ret = responseLceData(p); break;
+            case RIL_REQUEST_GET_ACTIVITY_INFO: ret = responseActivityData(p); break;
             default:
                 throw new RuntimeException("Unrecognized solicited response: " + rr.mRequest);
             //break;
@@ -2611,10 +2655,29 @@
                         mIccStatusChangedRegistrants.notifyRegistrants();
                     }
                     break;
+                case RIL_REQUEST_GET_RADIO_CAPABILITY: {
+                    // Ideally RIL's would support this or at least give NOT_SUPPORTED
+                    // but the hammerhead RIL reports GENERIC :(
+                    // TODO - remove GENERIC_FAILURE catching: b/21079604
+                    if (REQUEST_NOT_SUPPORTED == error ||
+                            GENERIC_FAILURE == error) {
+                        // we should construct the RAF bitmask the radio
+                        // supports based on preferred network bitmasks
+                        ret = makeStaticRadioCapability();
+                        error = 0;
+                    }
+                    break;
+                }
+                case RIL_REQUEST_GET_ACTIVITY_INFO:
+                    ret = new ModemActivityInfo(0, 0, 0,
+                            new int [ModemActivityInfo.TX_POWER_LEVELS], 0, 0);
+                    error = 0;
+                    break;
             }
 
-            rr.onError(error, ret);
-        } else {
+            if (error != 0) rr.onError(error, ret);
+        }
+        if (error == 0) {
 
             if (RILJ_LOGD) riljLog(rr.serialString() + "< " + requestToString(rr.mRequest)
                     + " " + retToString(rr.mRequest, ret));
@@ -2627,6 +2690,21 @@
         return rr;
     }
 
+    private RadioCapability makeStaticRadioCapability() {
+        // default to UNKNOWN so we fail fast.
+        int raf = RadioAccessFamily.RAF_UNKNOWN;
+
+        String rafString = mContext.getResources().getString(
+                com.android.internal.R.string.config_radio_access_family);
+        if (TextUtils.isEmpty(rafString) == false) {
+            raf = RadioAccessFamily.rafTypeFromString(rafString);
+        }
+        RadioCapability rc = new RadioCapability(mInstanceId.intValue(), 0, 0, raf,
+                "", RadioCapability.RC_STATUS_SUCCESS);
+        if (RILJ_LOGD) riljLog("Faking RIL_REQUEST_GET_RADIO_CAPABILITY response using " + raf);
+        return rc;
+    }
+
     static String
     retToString(int req, Object ret) {
         if (ret == null) return "";
@@ -2769,6 +2847,7 @@
                     ret = responseRadioCapability(p); break;
             case RIL_UNSOL_ON_SS: ret =  responseSsData(p); break;
             case RIL_UNSOL_STK_CC_ALPHA_NOTIFY: ret =  responseString(p); break;
+            case RIL_UNSOL_LCEDATA_RECV: ret = responseLceData(p); break;
 
             default:
                 throw new RuntimeException("Unrecognized unsol response: " + response);
@@ -3062,7 +3141,7 @@
                 break;
 
             case RIL_UNSOL_OEM_HOOK_RAW:
-                if (RILJ_LOGD) unsljLogvRet(response, IccUtils.bytesToHexString((byte[])ret));
+                if (RILJ_LOGD) unsljLogvRet(response, IccUtils.bytesToHexString((byte[]) ret));
                 if (mUnsolOemHookRawRegistrant != null) {
                     mUnsolOemHookRawRegistrant.notifyRegistrant(new AsyncResult(null, ret, null));
                 }
@@ -3125,11 +3204,8 @@
             case RIL_UNSOL_RIL_CONNECTED: {
                 if (RILJ_LOGD) unsljLogRet(response, ret);
 
-                getRadioCapability(mSupportedRafHandler.obtainMessage());
-
                 // Initial conditions
                 setRadioPower(false, null);
-                setPreferredNetworkType(mPreferredNetworkType, null);
                 setCdmaSubscriptionSource(mCdmaSubscription, null);
                 setCellInfoListRate(Integer.MAX_VALUE, null);
                 notifyRegistrantsRilConnectionChanged(((int[])ret)[0]);
@@ -3194,27 +3270,17 @@
                                         new AsyncResult (null, ret, null));
                 }
                 break;
+            case RIL_UNSOL_LCEDATA_RECV:
+                if (RILJ_LOGD) unsljLogRet(response, ret);
+
+                if (mLceInfoRegistrant != null) {
+                    mLceInfoRegistrant.notifyRegistrant(new AsyncResult(null, ret, null));
+                }
+                break;
         }
     }
 
     /**
-     * Receives and stores the capabilities supported by the modem.
-     */
-    private Handler mSupportedRafHandler = new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            AsyncResult ar = (AsyncResult) msg.obj;
-            RadioCapability rc = (RadioCapability) ar.result;
-            if (ar.exception != null) {
-                if (RILJ_LOGD) riljLog("Get supported radio access family fail");
-            } else {
-                mSupportedRaf = rc.getRadioAccessFamily();
-                if (RILJ_LOGD) riljLog("Supported radio access family=" + mSupportedRaf);
-            }
-        }
-    };
-
-    /**
      * Notifiy all registrants that the ril has connected or disconnected.
      *
      * @param rilVer is the version of the ril or -1 if disconnected.
@@ -3243,6 +3309,15 @@
         return response;
     }
 
+    private Object
+    responseFailCause(Parcel p) {
+        LastCallFailCause failCause = new LastCallFailCause();
+        failCause.causeCode = p.readInt();
+        if (p.dataAvail() > 0) {
+          failCause.vendorCause = p.readString();
+        }
+        return failCause;
+    }
 
     private Object
     responseVoid(Parcel p) {
@@ -3370,8 +3445,8 @@
                 + " 0x" + Integer.toHexString(sw2) + " "
                 + s);
 
-
-        return new IccIoResult(sw1, sw2, android.util.Base64.decode(s, android.util.Base64.DEFAULT));
+        return new IccIoResult(sw1, sw2, (s != null)
+                ? android.util.Base64.decode(s, android.util.Base64.DEFAULT) : (byte[]) null);
     }
 
     private Object
@@ -3922,6 +3997,55 @@
         return rc;
     }
 
+    private Object responseLceData(Parcel p) {
+        final ArrayList<Integer> capacityResponse = new ArrayList<Integer>();
+        final int capacityDownKbps = p.readInt();
+        final int confidenceLevel = p.readByte();
+        final int lceSuspended = p.readByte();
+
+        riljLog("LCE capacity information received:" +
+                " capacity=" + capacityDownKbps +
+                " confidence=" + confidenceLevel +
+                " lceSuspended=" + lceSuspended);
+
+        capacityResponse.add(capacityDownKbps);
+        capacityResponse.add(confidenceLevel);
+        capacityResponse.add(lceSuspended);
+        return capacityResponse;
+    }
+
+    private Object responseLceStatus(Parcel p) {
+        final ArrayList<Integer> statusResponse = new ArrayList<Integer>();
+        final int lceStatus = (int)p.readByte();
+        final int actualInterval = p.readInt();
+
+        riljLog("LCE status information received:" +
+                " lceStatus=" + lceStatus +
+                " actualInterval=" + actualInterval);
+        statusResponse.add(lceStatus);
+        statusResponse.add(actualInterval);
+        return statusResponse;
+    }
+
+    private Object responseActivityData(Parcel p) {
+        final int sleepModeTimeMs = p.readInt();
+        final int idleModeTimeMs = p.readInt();
+        int [] txModeTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
+        for (int i = 0; i < ModemActivityInfo.TX_POWER_LEVELS; i++) {
+            txModeTimeMs[i] = p.readInt();
+        }
+        final int rxModeTimeMs = p.readInt();
+
+        riljLog("Modem activity info received:" +
+                " sleepModeTimeMs=" + sleepModeTimeMs +
+                " idleModeTimeMs=" + idleModeTimeMs +
+                " txModeTimeMs[]=" + Arrays.toString(txModeTimeMs) +
+                " rxModeTimeMs=" + rxModeTimeMs);
+
+        return new ModemActivityInfo(SystemClock.elapsedRealtime(), sleepModeTimeMs,
+                        idleModeTimeMs, txModeTimeMs, rxModeTimeMs, 0);
+    }
+
     static String
     requestToString(int request) {
 /*
@@ -4061,6 +4185,10 @@
                     return "RIL_REQUEST_SET_RADIO_CAPABILITY";
             case RIL_REQUEST_GET_RADIO_CAPABILITY:
                     return "RIL_REQUEST_GET_RADIO_CAPABILITY";
+            case RIL_REQUEST_START_LCE: return "RIL_REQUEST_START_LCE";
+            case RIL_REQUEST_STOP_LCE: return "RIL_REQUEST_STOP_LCE";
+            case RIL_REQUEST_PULL_LCEDATA: return "RIL_REQUEST_PULL_LCEDATA";
+            case RIL_REQUEST_GET_ACTIVITY_INFO: return "RIL_REQUEST_GET_ACTIVITY_INFO";
             default: return "<unknown request>";
         }
     }
@@ -4122,6 +4250,7 @@
                     return "RIL_UNSOL_RADIO_CAPABILITY";
             case RIL_UNSOL_ON_SS: return "UNSOL_ON_SS";
             case RIL_UNSOL_STK_CC_ALPHA_NOTIFY: return "UNSOL_STK_CC_ALPHA_NOTIFY";
+            case RIL_UNSOL_LCEDATA_RECV: return "UNSOL_LCE_INFO_RECV";
             default: return "<unknown response>";
         }
     }
@@ -4666,4 +4795,48 @@
 
         send(rr);
     }
+
+    @Override
+    public void startLceService(int reportIntervalMs, boolean pullMode, Message response) {
+        RILRequest rr = RILRequest.obtain(RIL_REQUEST_START_LCE, response);
+        /** solicited command argument: reportIntervalMs, pullMode. */
+        rr.mParcel.writeInt(2);
+        rr.mParcel.writeInt(reportIntervalMs);
+        rr.mParcel.writeInt(pullMode ? 1: 0);  // PULL mode: 1; PUSH mode: 0;
+
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+        }
+
+        send(rr);
+    }
+
+    @Override
+    public void stopLceService(Message response) {
+        RILRequest rr = RILRequest.obtain(RIL_REQUEST_STOP_LCE, response);
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+        }
+        send(rr);
+    }
+
+    @Override
+    public void pullLceData(Message response) {
+        RILRequest rr = RILRequest.obtain(RIL_REQUEST_PULL_LCEDATA, response);
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+        }
+        send(rr);
+    }
+
+    /**
+    * @hide
+    */
+    public void getModemActivityInfo(Message response) {
+        RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_ACTIVITY_INFO, response);
+        if (RILJ_LOGD) {
+            riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
+        }
+        send(rr);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/SMSDispatcher.java b/src/java/com/android/internal/telephony/SMSDispatcher.java
index a12c3fd..015305e 100644
--- a/src/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/SMSDispatcher.java
@@ -15,6 +15,7 @@
  */
 
 package com.android.internal.telephony;
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.PendingIntent;
@@ -354,6 +355,13 @@
         }
     }
 
+    private static int getSendSmsFlag(@Nullable PendingIntent deliveryIntent) {
+        if (deliveryIntent == null) {
+            return 0;
+        }
+        return CarrierMessagingService.SEND_FLAG_REQUEST_DELIVERY_STATUS;
+    }
+
     /**
      * Use the carrier messaging service to send a text SMS.
      */
@@ -370,7 +378,8 @@
             if (text != null) {
                 try {
                     carrierMessagingService.sendTextSms(text, getSubId(),
-                            mTracker.mDestAddress, mSenderCallback);
+                            mTracker.mDestAddress, getSendSmsFlag(mTracker.mDeliveryIntent),
+                            mSenderCallback);
                 } catch (RemoteException e) {
                     Rlog.e(TAG, "Exception sending the SMS: " + e);
                     mSenderCallback.onSendSmsComplete(
@@ -402,7 +411,8 @@
             if (data != null) {
                 try {
                     carrierMessagingService.sendDataSms(data, getSubId(),
-                            mTracker.mDestAddress, destPort, mSenderCallback);
+                            mTracker.mDestAddress, destPort,
+                            getSendSmsFlag(mTracker.mDeliveryIntent), mSenderCallback);
                 } catch (RemoteException e) {
                     Rlog.e(TAG, "Exception sending the SMS: " + e);
                     mSenderCallback.onSendSmsComplete(
@@ -528,7 +538,8 @@
         protected void onServiceReady(ICarrierMessagingService carrierMessagingService) {
             try {
                 carrierMessagingService.sendMultipartTextSms(
-                        mParts, getSubId(), mTrackers[0].mDestAddress, mSenderCallback);
+                        mParts, getSubId(), mTrackers[0].mDestAddress,
+                        getSendSmsFlag(mTrackers[0].mDeliveryIntent), mSenderCallback);
             } catch (RemoteException e) {
                 Rlog.e(TAG, "Exception sending the SMS: " + e);
                 mSenderCallback.onSendMultipartSmsComplete(
@@ -765,10 +776,12 @@
      *  broadcast when the message is delivered to the recipient.  The
      * @param messageUri optional URI of the message if it is already stored in the system
      * @param callingPkg the calling package name
+     * @param persistMessage whether to save the sent message into SMS DB for a
+     *   non-default SMS app.
      */
     protected abstract void sendText(String destAddr, String scAddr, String text,
             PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri,
-            String callingPkg);
+            String callingPkg, boolean persistMessage);
 
     /**
      * Inject an SMS PDU into the android platform.
@@ -818,10 +831,13 @@
      *   to the recipient.  The raw pdu of the status report is in the
      * @param messageUri optional URI of the message if it is already stored in the system
      * @param callingPkg the calling package name
+     * @param persistMessage whether to save the sent message into SMS DB for a
+     *   non-default SMS app.
      */
     protected void sendMultipartText(String destAddr, String scAddr,
             ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
-            ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg) {
+            ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg,
+            boolean persistMessage) {
         final String fullMessageText = getMultipartMessageText(parts);
         int refNumber = getNextConcatenatedRef() & 0x00FF;
         int msgCount = parts.size();
@@ -879,6 +895,7 @@
                 getNewSubmitPduTracker(destAddr, scAddr, parts.get(i), smsHeader, encoding,
                         sentIntent, deliveryIntent, (i == (msgCount - 1)),
                         unsentPartCount, anyPartFailed, messageUri, fullMessageText);
+            trackers[i].mPersistMessage = persistMessage;
         }
 
         if (parts == null || trackers == null || trackers.length == 0
@@ -1265,7 +1282,7 @@
         }
 
         sendMultipartText(destinationAddress, scAddress, parts, sentIntents, deliveryIntents,
-                null/*messageUri*/, null/*callingPkg*/);
+                null/*messageUri*/, null/*callingPkg*/, tracker.mPersistMessage);
     }
 
     /**
@@ -1304,11 +1321,13 @@
         // If this is a text message (instead of data message)
         private boolean mIsText;
 
+        private boolean mPersistMessage;
+
         private SmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
                 PendingIntent deliveryIntent, PackageInfo appInfo, String destAddr, String format,
                 AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
                 SmsHeader smsHeader, boolean isExpectMore, String fullMessageText, int subId,
-                boolean isText) {
+                boolean isText, boolean persistMessage) {
             mData = data;
             mSentIntent = sentIntent;
             mDeliveryIntent = deliveryIntent;
@@ -1326,6 +1345,7 @@
             mFullMessageText = fullMessageText;
             mSubId = subId;
             mIsText = isText;
+            mPersistMessage = persistMessage;
         }
 
         /**
@@ -1386,7 +1406,7 @@
          * @return The telephony provider URI if stored
          */
         private Uri persistSentMessageIfRequired(Context context, int messageType, int errorCode) {
-            if (!mIsText ||
+            if (!mIsText || !mPersistMessage ||
                     !SmsApplication.shouldWriteMessageForPackage(mAppInfo.packageName, context)) {
                 return null;
             }
@@ -1529,7 +1549,7 @@
     protected SmsTracker getSmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
             PendingIntent deliveryIntent, String format, AtomicInteger unsentPartCount,
             AtomicBoolean anyPartFailed, Uri messageUri, SmsHeader smsHeader,
-            boolean isExpectMore, String fullMessageText, boolean isText) {
+            boolean isExpectMore, String fullMessageText, boolean isText, boolean persistMessage) {
         // Get calling app package name via UID from Binder call
         PackageManager pm = mContext.getPackageManager();
         String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid());
@@ -1549,15 +1569,15 @@
         String destAddr = PhoneNumberUtils.extractNetworkPortion((String) data.get("destAddr"));
         return new SmsTracker(data, sentIntent, deliveryIntent, appInfo, destAddr, format,
                 unsentPartCount, anyPartFailed, messageUri, smsHeader, isExpectMore,
-                fullMessageText, getSubId(), isText);
+                fullMessageText, getSubId(), isText, persistMessage);
     }
 
     protected SmsTracker getSmsTracker(HashMap<String, Object> data, PendingIntent sentIntent,
             PendingIntent deliveryIntent, String format, Uri messageUri, boolean isExpectMore,
-            String fullMessageText, boolean isText) {
+            String fullMessageText, boolean isText, boolean persistMessage) {
         return getSmsTracker(data, sentIntent, deliveryIntent, format, null/*unsentPartCount*/,
                 null/*anyPartFailed*/, messageUri, null/*smsHeader*/, isExpectMore,
-                fullMessageText, isText);
+                fullMessageText, isText, persistMessage);
     }
 
     protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr,
diff --git a/src/java/com/android/internal/telephony/ServiceStateTracker.java b/src/java/com/android/internal/telephony/ServiceStateTracker.java
index e7ec0ea..cdd65e5 100644
--- a/src/java/com/android/internal/telephony/ServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/ServiceStateTracker.java
@@ -21,6 +21,7 @@
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
 import android.os.AsyncResult;
+import android.os.BaseBundle;
 import android.os.Handler;
 import android.os.Message;
 import android.os.Registrant;
@@ -28,6 +29,7 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.preference.PreferenceManager;
+import android.telephony.CarrierConfigManager;
 import android.telephony.CellInfo;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
@@ -39,9 +41,13 @@
 import android.util.Log;
 import android.util.Pair;
 import android.util.TimeUtils;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.content.Context;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.Arrays;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -177,6 +183,7 @@
     protected static final int EVENT_CHANGE_IMS_STATE                  = 45;
     protected static final int EVENT_IMS_STATE_CHANGED                 = 46;
     protected static final int EVENT_IMS_STATE_DONE                    = 47;
+    protected static final int EVENT_IMS_CAPABILITY_CHANGED            = 48;
 
     protected static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
 
@@ -228,10 +235,11 @@
     /** Keep track of SPN display rules, so we only broadcast intent if something changes. */
     protected boolean mSpnUpdatePending = false;
     protected String mCurSpn = null;
+    protected String mCurDataSpn = null;
     protected String mCurPlmn = null;
     protected boolean mCurShowPlmn = false;
     protected boolean mCurShowSpn = false;
-
+    protected int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
 
     private boolean mImsRegistered = false;
 
@@ -254,17 +262,12 @@
             if (mPreviousSubId.getAndSet(subId) != subId) {
                 if (SubscriptionManager.isValidSubscriptionId(subId)) {
                     Context context = mPhoneBase.getContext();
-                    int networkType = PhoneFactory.calculatePreferredNetworkType(context, subId);
-                    mCi.setPreferredNetworkType(networkType, null);
 
                     mPhoneBase.notifyCallForwardingIndicator();
 
-                    boolean skipRestoringSelection = context.getResources().getBoolean(
+                    boolean restoreSelection = !context.getResources().getBoolean(
                             com.android.internal.R.bool.skip_restoring_network_selection);
-                    if (!skipRestoringSelection) {
-                        // restore the previous network selection.
-                        mPhoneBase.restoreSavedNetworkSelection(null);
-                    }
+                    mPhoneBase.sendSubscriptionSettings(restoreSelection);
 
                     mPhoneBase.setSystemProperty(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE,
                         ServiceState.rilRadioTechnologyToString(mSS.getRilDataRadioTechnology()));
@@ -280,21 +283,32 @@
                     // older build that did not include subId in the names.
                     SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(
                             context);
-                    String oldNetworkSelectionName = sp.getString(PhoneBase.
-                            NETWORK_SELECTION_NAME_KEY, "");
-                    String oldNetworkSelection = sp.getString(PhoneBase.NETWORK_SELECTION_KEY,
-                            "");
-                    if (!TextUtils.isEmpty(oldNetworkSelectionName) ||
-                            !TextUtils.isEmpty(oldNetworkSelection)) {
+                    String oldNetworkSelection = sp.getString(
+                            PhoneBase.NETWORK_SELECTION_KEY, "");
+                    String oldNetworkSelectionName = sp.getString(
+                            PhoneBase.NETWORK_SELECTION_NAME_KEY, "");
+                    String oldNetworkSelectionShort = sp.getString(
+                            PhoneBase.NETWORK_SELECTION_SHORT_KEY, "");
+                    if (!TextUtils.isEmpty(oldNetworkSelection) ||
+                            !TextUtils.isEmpty(oldNetworkSelectionName) ||
+                            !TextUtils.isEmpty(oldNetworkSelectionShort)) {
                         SharedPreferences.Editor editor = sp.edit();
-                        editor.putString(PhoneBase.NETWORK_SELECTION_NAME_KEY + subId,
-                                oldNetworkSelectionName);
                         editor.putString(PhoneBase.NETWORK_SELECTION_KEY + subId,
                                 oldNetworkSelection);
-                        editor.remove(PhoneBase.NETWORK_SELECTION_NAME_KEY);
+                        editor.putString(PhoneBase.NETWORK_SELECTION_NAME_KEY + subId,
+                                oldNetworkSelectionName);
+                        editor.putString(PhoneBase.NETWORK_SELECTION_SHORT_KEY + subId,
+                                oldNetworkSelectionShort);
                         editor.remove(PhoneBase.NETWORK_SELECTION_KEY);
+                        editor.remove(PhoneBase.NETWORK_SELECTION_NAME_KEY);
+                        editor.remove(PhoneBase.NETWORK_SELECTION_SHORT_KEY);
                         editor.commit();
                     }
+
+                    // Once sub id becomes valid, we need to update the service provider name
+                    // displayed on the UI again. The old SPN update intents sent to
+                    // MobileSignalController earlier were actually ignored due to invalid sub id.
+                    updateSpnDisplay();
                 }
             }
         }
@@ -635,6 +649,7 @@
     public abstract boolean isConcurrentVoiceAndDataAllowed();
 
     public abstract void setImsRegistrationState(boolean registered);
+    public void onImsCapabilityChanged() {}
     public abstract void pollState();
 
     /**
@@ -1058,4 +1073,73 @@
     protected int getPhoneId() {
         return mPhoneBase.getPhoneId();
     }
+
+    /* Reset Service state when IWLAN is enabled as polling in airplane mode
+     * causes state to go to OUT_OF_SERVICE state instead of STATE_OFF
+     */
+    protected void resetServiceStateInIwlanMode() {
+        if (mCi.getRadioState() == CommandsInterface.RadioState.RADIO_OFF) {
+            boolean resetIwlanRatVal = false;
+            log("set service state as POWER_OFF");
+            if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+                        == mNewSS.getRilDataRadioTechnology()) {
+                log("pollStateDone: mNewSS = " + mNewSS);
+                log("pollStateDone: reset iwlan RAT value");
+                resetIwlanRatVal = true;
+            }
+            mNewSS.setStateOff();
+            if (resetIwlanRatVal) {
+                mNewSS.setRilDataRadioTechnology(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN);
+                mNewSS.setDataRegState(ServiceState.STATE_IN_SERVICE);
+                log("pollStateDone: mNewSS = " + mNewSS);
+            }
+        }
+    }
+
+    /**
+     * Check if device is non-roaming and always on home network.
+     *
+     * @param b carrier config bundle obtained from CarrierConfigManager
+     * @return true if network is always on home network, false otherwise
+     * @see CarrierConfigManager
+     */
+    protected final boolean alwaysOnHomeNetwork(BaseBundle b) {
+        return b.getBoolean(CarrierConfigManager.KEY_FORCE_HOME_NETWORK_BOOL);
+    }
+
+    /**
+     * Check if the network identifier has membership in the set of
+     * network identifiers stored in the carrier config bundle.
+     *
+     * @param b carrier config bundle obtained from CarrierConfigManager
+     * @param network The network identifier to check network existence in bundle
+     * @param key The key to index into the bundle presenting a string array of
+     *            networks to check membership
+     * @return true if network has membership in bundle networks, false otherwise
+     * @see CarrierConfigManager
+     */
+    private boolean isInNetwork(BaseBundle b, String network, String key) {
+        String[] networks = b.getStringArray(key);
+
+        if (networks != null && Arrays.asList(networks).contains(network)) {
+            return true;
+        }
+        return false;
+    }
+
+    protected final boolean isRoamingInGsmNetwork(BaseBundle b, String network) {
+        return isInNetwork(b, network, CarrierConfigManager.KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY);
+    }
+
+    protected final boolean isNonRoamingInGsmNetwork(BaseBundle b, String network) {
+        return isInNetwork(b, network, CarrierConfigManager.KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY);
+    }
+
+    protected final boolean isRoamingInCdmaNetwork(BaseBundle b, String network) {
+        return isInNetwork(b, network, CarrierConfigManager.KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY);
+    }
+
+    protected final boolean isNonRoamingInCdmaNetwork(BaseBundle b, String network) {
+        return isInNetwork(b, network, CarrierConfigManager.KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/SmsApplication.java b/src/java/com/android/internal/telephony/SmsApplication.java
index 56ff988..5a95394 100644
--- a/src/java/com/android/internal/telephony/SmsApplication.java
+++ b/src/java/com/android/internal/telephony/SmsApplication.java
@@ -17,19 +17,17 @@
 package com.android.internal.telephony;
 
 import android.Manifest.permission;
-import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
-import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.net.Uri;
 import android.os.Binder;
@@ -58,6 +56,7 @@
     private static final String PHONE_PACKAGE_NAME = "com.android.phone";
     private static final String BLUETOOTH_PACKAGE_NAME = "com.android.bluetooth";
     private static final String MMS_SERVICE_PACKAGE_NAME = "com.android.mms.service";
+    private static final String TELEPHONY_PROVIDER_PACKAGE_NAME = "com.android.providers.telephony";
 
     private static final String SCHEME_SMS = "sms";
     private static final String SCHEME_SMSTO = "smsto";
@@ -385,51 +384,15 @@
                 configurePreferredActivity(packageManager, new ComponentName(
                         applicationData.mPackageName, applicationData.mSendToClass),
                         userId);
-                // Verify that the phone, BT app and MmsService have permissions
-                try {
-                    PackageInfo info = packageManager.getPackageInfo(PHONE_PACKAGE_NAME, 0);
-                    int mode = appOps.checkOp(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
-                            PHONE_PACKAGE_NAME);
-                    if (mode != AppOpsManager.MODE_ALLOWED) {
-                        Rlog.e(LOG_TAG, PHONE_PACKAGE_NAME + " lost OP_WRITE_SMS:  (fixing)");
-                        appOps.setMode(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
-                                PHONE_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
-                    }
-                } catch (NameNotFoundException e) {
-                    // No phone app on this device (unexpected, even for non-phone devices)
-                    Rlog.e(LOG_TAG, "Phone package not found: " + PHONE_PACKAGE_NAME);
-                    applicationData = null;
-                }
-
-                try {
-                    PackageInfo info = packageManager.getPackageInfo(BLUETOOTH_PACKAGE_NAME, 0);
-                    int mode = appOps.checkOp(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
-                            BLUETOOTH_PACKAGE_NAME);
-                    if (mode != AppOpsManager.MODE_ALLOWED) {
-                        Rlog.e(LOG_TAG, BLUETOOTH_PACKAGE_NAME + " lost OP_WRITE_SMS:  (fixing)");
-                        appOps.setMode(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
-                                BLUETOOTH_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
-                    }
-                } catch (NameNotFoundException e) {
-                    // No BT app on this device
-                    Rlog.e(LOG_TAG, "Bluetooth package not found: " + BLUETOOTH_PACKAGE_NAME);
-                }
-
-                try {
-                    PackageInfo info = packageManager.getPackageInfo(MMS_SERVICE_PACKAGE_NAME, 0);
-                    int mode = appOps.checkOp(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
-                            MMS_SERVICE_PACKAGE_NAME);
-                    if (mode != AppOpsManager.MODE_ALLOWED) {
-                        Rlog.e(LOG_TAG, MMS_SERVICE_PACKAGE_NAME + " lost OP_WRITE_SMS:  (fixing)");
-                        appOps.setMode(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
-                                MMS_SERVICE_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
-                    }
-                } catch (NameNotFoundException e) {
-                    // No phone app on this device (unexpected, even for non-phone devices)
-                    Rlog.e(LOG_TAG, "MmsService package not found: " + MMS_SERVICE_PACKAGE_NAME);
-                    applicationData = null;
-                }
-
+                // Assign permission to special system apps
+                assignWriteSmsPermissionToSystemApp(context, packageManager, appOps,
+                        PHONE_PACKAGE_NAME);
+                assignWriteSmsPermissionToSystemApp(context, packageManager, appOps,
+                        BLUETOOTH_PACKAGE_NAME);
+                assignWriteSmsPermissionToSystemApp(context, packageManager, appOps,
+                        MMS_SERVICE_PACKAGE_NAME);
+                assignWriteSmsPermissionToSystemApp(context, packageManager, appOps,
+                        TELEPHONY_PROVIDER_PACKAGE_NAME);
             }
         }
         if (DEBUG_MULTIUSER) {
@@ -500,39 +463,53 @@
             appOps.setMode(AppOpsManager.OP_WRITE_SMS, applicationData.mUid,
                     applicationData.mPackageName, AppOpsManager.MODE_ALLOWED);
 
-            // Phone needs to always have this permission to write to the sms database
-            try {
-                PackageInfo info = packageManager.getPackageInfo(PHONE_PACKAGE_NAME, 0);
-                appOps.setMode(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
-                        PHONE_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
-            } catch (NameNotFoundException e) {
-                // No phone app on this device (unexpected, even for non-phone devices)
-                Rlog.e(LOG_TAG, "Phone package not found: " + PHONE_PACKAGE_NAME);
-            }
-
-            // BT needs to always have this permission to write to the sms database
-            try {
-                PackageInfo info = packageManager.getPackageInfo(BLUETOOTH_PACKAGE_NAME, 0);
-                appOps.setMode(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
-                        BLUETOOTH_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
-            } catch (NameNotFoundException e) {
-                // No BT app on this device
-                Rlog.e(LOG_TAG, "Bluetooth package not found: " + BLUETOOTH_PACKAGE_NAME);
-            }
-
-            // MmsService needs to always have this permission to write to the sms database
-            try {
-                PackageInfo info = packageManager.getPackageInfo(MMS_SERVICE_PACKAGE_NAME, 0);
-                appOps.setMode(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
-                        MMS_SERVICE_PACKAGE_NAME, AppOpsManager.MODE_ALLOWED);
-            } catch (NameNotFoundException e) {
-                // No phone app on this device (unexpected, even for non-phone devices)
-                Rlog.e(LOG_TAG, "MmsService package not found: " + MMS_SERVICE_PACKAGE_NAME);
-            }
+            // Assign permission to special system apps
+            assignWriteSmsPermissionToSystemApp(context, packageManager, appOps,
+                    PHONE_PACKAGE_NAME);
+            assignWriteSmsPermissionToSystemApp(context, packageManager, appOps,
+                    BLUETOOTH_PACKAGE_NAME);
+            assignWriteSmsPermissionToSystemApp(context, packageManager, appOps,
+                    MMS_SERVICE_PACKAGE_NAME);
+            assignWriteSmsPermissionToSystemApp(context, packageManager, appOps,
+                    TELEPHONY_PROVIDER_PACKAGE_NAME);
         }
     }
 
     /**
+     * Assign WRITE_SMS AppOps permission to some special system apps.
+     *
+     * @param context The context
+     * @param packageManager The package manager instance
+     * @param appOps The AppOps manager instance
+     * @param packageName The package name of the system app
+     */
+    private static void assignWriteSmsPermissionToSystemApp(Context context,
+            PackageManager packageManager, AppOpsManager appOps, String packageName) {
+        // First check package signature matches the caller's package signature.
+        // Since this class is only used internally by the system, this check makes sure
+        // the package signature matches system signature.
+        final int result = packageManager.checkSignatures(context.getPackageName(), packageName);
+        if (result != PackageManager.SIGNATURE_MATCH) {
+            Rlog.e(LOG_TAG, packageName + " does not have system signature");
+            return;
+        }
+        try {
+            PackageInfo info = packageManager.getPackageInfo(packageName, 0);
+            int mode = appOps.checkOp(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
+                    packageName);
+            if (mode != AppOpsManager.MODE_ALLOWED) {
+                Rlog.w(LOG_TAG, packageName + " does not have OP_WRITE_SMS:  (fixing)");
+                appOps.setMode(AppOpsManager.OP_WRITE_SMS, info.applicationInfo.uid,
+                        packageName, AppOpsManager.MODE_ALLOWED);
+            }
+        } catch (NameNotFoundException e) {
+            // No whitelisted system app on this device
+            Rlog.e(LOG_TAG, "Package not found: " + packageName);
+        }
+
+    }
+
+    /**
      * Tracks package changes and ensures that the default SMS app is always configured to be the
      * preferred activity for SENDTO sms/mms intents.
      */
@@ -735,24 +712,36 @@
      * Caller must pass in the correct user context if calling from a singleton service.
      */
     public static boolean shouldWriteMessageForPackage(String packageName, Context context) {
-        if (packageName == null) return true;
-
         if (SmsManager.getDefault().getAutoPersisting()) {
             return true;
         }
+        return !isDefaultSmsApplication(context, packageName);
+    }
 
-        String defaultSmsPackage = null;
-        ComponentName component = getDefaultSmsApplication(context, false);
-        if (component != null) {
-            defaultSmsPackage = component.getPackageName();
+    /**
+     * Check if a package is default sms app (or equivalent, like bluetooth)
+     *
+     * @param context context from the calling app
+     * @param packageName the name of the package to be checked
+     * @return true if the package is default sms app or bluetooth
+     */
+    public static boolean isDefaultSmsApplication(Context context, String packageName) {
+        if (packageName == null) {
+            return false;
         }
-
-        if ((defaultSmsPackage == null || !defaultSmsPackage.equals(packageName)) &&
-                !packageName.equals(BLUETOOTH_PACKAGE_NAME)) {
-            // To write the message for someone other than the default SMS and BT app
+        final String defaultSmsPackage = getDefaultSmsApplicationPackageName(context);
+        if ((defaultSmsPackage != null && defaultSmsPackage.equals(packageName))
+                || BLUETOOTH_PACKAGE_NAME.equals(packageName)) {
             return true;
         }
-
         return false;
     }
+
+    private static String getDefaultSmsApplicationPackageName(Context context) {
+        final ComponentName component = getDefaultSmsApplication(context, false);
+        if (component != null) {
+            return component.getPackageName();
+        }
+        return null;
+    }
 }
diff --git a/src/java/com/android/internal/telephony/SmsMessageBase.java b/src/java/com/android/internal/telephony/SmsMessageBase.java
index b027b1e..1d50834 100644
--- a/src/java/com/android/internal/telephony/SmsMessageBase.java
+++ b/src/java/com/android/internal/telephony/SmsMessageBase.java
@@ -16,11 +16,14 @@
 
 package com.android.internal.telephony;
 
+import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
 import com.android.internal.telephony.SmsConstants;
 import com.android.internal.telephony.SmsHeader;
+import java.text.BreakIterator;
 import java.util.Arrays;
 
 import android.provider.Telephony;
+import android.telephony.SmsMessage;
 
 /**
  * Base class declaring the specific methods and members for SmsMessage.
@@ -346,4 +349,70 @@
          mIsEmail = Telephony.Mms.isEmailAddress(mEmailFrom);
     }
 
+    /**
+     * Find the next position to start a new fragment of a multipart SMS.
+     *
+     * @param currentPosition current start position of the fragment
+     * @param byteLimit maximum number of bytes in the fragment
+     * @param msgBody text of the SMS in UTF-16 encoding
+     * @return the position to start the next fragment
+     */
+    public static int findNextUnicodePosition(
+            int currentPosition, int byteLimit, CharSequence msgBody) {
+        int nextPos = Math.min(currentPosition + byteLimit / 2, msgBody.length());
+        // Check whether the fragment ends in a character boundary. Some characters take 4-bytes
+        // in UTF-16 encoding. Many carriers cannot handle
+        // a fragment correctly if it does not end at a character boundary.
+        if (nextPos < msgBody.length()) {
+            BreakIterator breakIterator = BreakIterator.getCharacterInstance();
+            breakIterator.setText(msgBody.toString());
+            if (!breakIterator.isBoundary(nextPos)) {
+                nextPos = breakIterator.preceding(nextPos);
+            }
+        }
+        return nextPos;
+    }
+
+    /**
+     * Calculate the TextEncodingDetails of a message encoded in Unicode.
+     */
+    public static TextEncodingDetails calcUnicodeEncodingDetails(CharSequence msgBody) {
+        TextEncodingDetails ted = new TextEncodingDetails();
+        int octets = msgBody.length() * 2;
+        ted.codeUnitSize = SmsConstants.ENCODING_16BIT;
+        ted.codeUnitCount = msgBody.length();
+        if (octets > SmsConstants.MAX_USER_DATA_BYTES) {
+            // If EMS is not supported, break down EMS into single segment SMS
+            // and add page info " x/y".
+            // In the case of UCS2 encoding type, we need 8 bytes for this
+            // but we only have 6 bytes from UDH, so truncate the limit for
+            // each segment by 2 bytes (1 char).
+            int maxUserDataBytesWithHeader = SmsConstants.MAX_USER_DATA_BYTES_WITH_HEADER;
+            if (!SmsMessage.hasEmsSupport()) {
+                // make sure total number of segments is less than 10
+                if (octets <= 9 * (maxUserDataBytesWithHeader - 2)) {
+                    maxUserDataBytesWithHeader -= 2;
+                }
+            }
+
+            int pos = 0;  // Index in code units.
+            int msgCount = 0;
+            while (pos < msgBody.length()) {
+                int nextPos = findNextUnicodePosition(pos, maxUserDataBytesWithHeader,
+                        msgBody);
+                if (nextPos == msgBody.length()) {
+                    ted.codeUnitsRemaining = pos + maxUserDataBytesWithHeader / 2 -
+                            msgBody.length();
+                }
+                pos = nextPos;
+                msgCount++;
+            }
+            ted.msgCount = msgCount;
+        } else {
+            ted.msgCount = 1;
+            ted.codeUnitsRemaining = (SmsConstants.MAX_USER_DATA_BYTES - octets) / 2;
+        }
+
+        return ted;
+    }
 }
diff --git a/src/java/com/android/internal/telephony/SmsNumberUtils.java b/src/java/com/android/internal/telephony/SmsNumberUtils.java
index 2d03f17..f530ade 100644
--- a/src/java/com/android/internal/telephony/SmsNumberUtils.java
+++ b/src/java/com/android/internal/telephony/SmsNumberUtils.java
@@ -534,7 +534,7 @@
         if (DBG) Rlog.d(TAG, "enter filterDestAddr. destAddr=\"" + destAddr + "\"" );
 
         if (destAddr == null || !PhoneNumberUtils.isGlobalPhoneNumber(destAddr)) {
-            Rlog.w(TAG, "destAddr" + destAddr + " is not a global phone number!");
+            Rlog.w(TAG, "destAddr" + destAddr + " is not a global phone number! Nothing changed.");
             return destAddr;
         }
 
@@ -551,7 +551,10 @@
             }
         }
 
-        if (DBG) Rlog.d(TAG, "leave filterDestAddr, new destAddr=\"" + result + "\"" );
+        if (DBG) {
+            Rlog.d(TAG, "destAddr is " + ((result != null)?"formatted.":"not formatted."));
+            Rlog.d(TAG, "leave filterDestAddr, new destAddr=\"" + (result != null ? result : destAddr) + "\"" );
+        }
         return result != null ? result : destAddr;
     }
 
diff --git a/src/java/com/android/internal/telephony/SmsUsageMonitor.java b/src/java/com/android/internal/telephony/SmsUsageMonitor.java
index b9a0af1..73e9a42 100644
--- a/src/java/com/android/internal/telephony/SmsUsageMonitor.java
+++ b/src/java/com/android/internal/telephony/SmsUsageMonitor.java
@@ -46,6 +46,7 @@
 import java.io.FileOutputStream;
 import java.io.FileReader;
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.HashMap;
@@ -445,7 +446,7 @@
                 try {
                     infile = mPolicyFile.openRead();
                     final XmlPullParser parser = Xml.newPullParser();
-                    parser.setInput(infile, null);
+                    parser.setInput(infile, StandardCharsets.UTF_8.name());
 
                     XmlUtils.beginDocument(parser, TAG_SMS_POLICY_BODY);
 
@@ -502,7 +503,7 @@
                 outfile = mPolicyFile.startWrite();
 
                 XmlSerializer out = new FastXmlSerializer();
-                out.setOutput(outfile, "utf-8");
+                out.setOutput(outfile, StandardCharsets.UTF_8.name());
 
                 out.startDocument(null, true);
 
diff --git a/src/java/com/android/internal/telephony/SubscriptionController.java b/src/java/com/android/internal/telephony/SubscriptionController.java
index 57172bb..ba5e037 100644
--- a/src/java/com/android/internal/telephony/SubscriptionController.java
+++ b/src/java/com/android/internal/telephony/SubscriptionController.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony;
 
+import android.app.AppOpsManager;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
@@ -38,7 +39,7 @@
 import android.text.TextUtils;
 import android.text.format.Time;
 import android.util.Log;
-
+import java.util.Objects;
 import com.android.internal.telephony.IccCardConstants.State;
 
 import java.io.FileDescriptor;
@@ -46,12 +47,13 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * SubscriptionController to provide an inter-process communication to
@@ -124,8 +126,11 @@
     protected TelephonyManager mTelephonyManager;
     protected CallManager mCM;
 
+    private final AppOpsManager mAppOps;
+
     // FIXME: Does not allow for multiple subs in a slot and change to SparseArray
-    private static HashMap<Integer, Integer> mSlotIdxToSubId = new HashMap<Integer, Integer>();
+    private static Map<Integer, Integer> sSlotIdxToSubId =
+            new ConcurrentHashMap<Integer, Integer>();
     private static int mDefaultFallbackSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
     private static int mDefaultPhoneId = SubscriptionManager.DEFAULT_PHONE_INDEX;
 
@@ -166,6 +171,7 @@
         mContext = c;
         mCM = CallManager.getInstance();
         mTelephonyManager = TelephonyManager.from(mContext);
+        mAppOps = mContext.getSystemService(AppOpsManager.class);
 
         if(ServiceManager.getService("isub") == null) {
                 ServiceManager.addService("isub", this);
@@ -175,12 +181,13 @@
     }
 
     private boolean isSubInfoReady() {
-        return mSlotIdxToSubId.size() > 0;
+        return sSlotIdxToSubId.size() > 0;
     }
 
     private SubscriptionController(Phone phone) {
         mContext = phone.getContext();
         mCM = CallManager.getInstance();
+        mAppOps = mContext.getSystemService(AppOpsManager.class);
 
         if(ServiceManager.getService("isub") == null) {
                 ServiceManager.addService("isub", this);
@@ -190,13 +197,34 @@
     }
 
     /**
-     * Make sure the caller has the READ_PHONE_STATE permission.
+     * Make sure the caller can read phone state which requires holding the
+     * READ_PHONE_STATE permission and the OP_READ_PHONE_STATE app op being
+     * set to MODE_ALLOWED.
      *
-     * @throws SecurityException if the caller does not have the required permission
+     * @param callingPackage The package claiming to make the IPC.
+     * @param message The name of the access protected method.
+     *
+     * @throws SecurityException if the caller does not have READ_PHONE_STATE permission.
      */
-    private void enforceSubscriptionPermission() {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE,
-                "Requires READ_PHONE_STATE");
+    private boolean canReadPhoneState(String callingPackage, String message) {
+        try {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, message);
+
+            // SKIP checking run-time permission since self or using PRIVILEDGED permission
+            return true;
+        } catch (SecurityException e) {
+            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE,
+                    message);
+        }
+
+        return mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
+                callingPackage) == AppOpsManager.MODE_ALLOWED;
+    }
+
+    private void enforceModifyPhoneState(String message) {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.MODIFY_PHONE_STATE, message);
     }
 
     /**
@@ -210,22 +238,7 @@
         mContext.sendBroadcast(intent);
      }
 
-     private boolean checkNotifyPermission(String method) {
-         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-                     == PackageManager.PERMISSION_GRANTED) {
-             return true;
-         }
-         if (DBG) {
-             logd("checkNotifyPermission Permission Denial: " + method + " from pid="
-                     + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
-         }
-         return false;
-     }
-
      public void notifySubscriptionInfoChanged() {
-         if (!checkNotifyPermission("notifySubscriptionInfoChanged")) {
-             return;
-         }
          ITelephonyRegistry tr = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
                  "telephony.registry"));
          try {
@@ -280,9 +293,9 @@
                 + " mcc:" + mcc + " mnc:" + mnc + " countIso:" + countryIso);
         }
 
+        // If line1number has been set to a different number, use it instead.
         String line1Number = mTelephonyManager.getLine1NumberForSubscriber(id);
         if (!TextUtils.isEmpty(line1Number) && !line1Number.equals(number)) {
-            logd("Line1Number is different: " + line1Number);
             number = line1Number;
         }
         return new SubscriptionInfo(id, iccId, simSlotIndex, displayName, carrierName,
@@ -345,10 +358,11 @@
 
     /**
      * Find unused color to be set for new SubInfoRecord
+     * @param callingPackage The package making the IPC.
      * @return RGB integer value of color
      */
-    private int getUnusedColor() {
-        List<SubscriptionInfo> availableSubInfos = getActiveSubscriptionInfoList();
+    private int getUnusedColor(String callingPackage) {
+        List<SubscriptionInfo> availableSubInfos = getActiveSubscriptionInfoList(callingPackage);
         colorArr = mContext.getResources().getIntArray(com.android.internal.R.array.sim_colors);
         int colorIdx = 0;
 
@@ -372,187 +386,266 @@
     /**
      * Get the active SubscriptionInfo with the subId key
      * @param subId The unique SubscriptionInfo key in database
+     * @param callingPackage The package making the IPC.
      * @return SubscriptionInfo, maybe null if its not active
      */
     @Override
-    public SubscriptionInfo getActiveSubscriptionInfo(int subId) {
-        enforceSubscriptionPermission();
+    public SubscriptionInfo getActiveSubscriptionInfo(int subId, String callingPackage) {
+        if (!canReadPhoneState(callingPackage, "getActiveSubscriptionInfo")) {
+            return null;
+        }
 
-        List<SubscriptionInfo> subList = getActiveSubscriptionInfoList();
-        if (subList != null) {
-            for (SubscriptionInfo si : subList) {
-                if (si.getSubscriptionId() == subId) {
-                    if (DBG) logd("[getActiveSubInfoForSubscriber]+ subId=" + subId + " subInfo=" + si);
-                    return si;
+        // Now that all security checks passes, perform the operation as ourselves.
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            List<SubscriptionInfo> subList = getActiveSubscriptionInfoList(
+                    mContext.getOpPackageName());
+            if (subList != null) {
+                for (SubscriptionInfo si : subList) {
+                    if (si.getSubscriptionId() == subId) {
+                        if (DBG)
+                            logd("[getActiveSubInfoForSubscriber]+ subId=" + subId
+                                    + " subInfo=" + si);
+                        return si;
+                    }
                 }
             }
+            if (DBG) {
+                logd("[getActiveSubInfoForSubscriber]- subId=" + subId
+                        + " subList=" + subList + " subInfo=null");
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
-        if (DBG) {
-            logd("[getActiveSubInfoForSubscriber]- subId=" + subId
-                    + " subList=" + subList + " subInfo=null");
-        }
+
         return null;
     }
 
     /**
      * Get the active SubscriptionInfo associated with the iccId
      * @param iccId the IccId of SIM card
+     * @param callingPackage The package making the IPC.
      * @return SubscriptionInfo, maybe null if its not active
      */
     @Override
-    public SubscriptionInfo getActiveSubscriptionInfoForIccId(String iccId) {
-        enforceSubscriptionPermission();
+    public SubscriptionInfo getActiveSubscriptionInfoForIccId(String iccId, String callingPackage) {
+        if (!canReadPhoneState(callingPackage, "getActiveSubscriptionInfoForIccId")) {
+            return null;
+        }
 
-        List<SubscriptionInfo> subList = getActiveSubscriptionInfoList();
-        if (subList != null) {
-            for (SubscriptionInfo si : subList) {
-                if (si.getIccId() == iccId) {
-                    if (DBG) logd("[getActiveSubInfoUsingIccId]+ iccId=" + iccId + " subInfo=" + si);
-                    return si;
+        // Now that all security checks passes, perform the operation as ourselves.
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            List<SubscriptionInfo> subList = getActiveSubscriptionInfoList(
+                    mContext.getOpPackageName());
+            if (subList != null) {
+                for (SubscriptionInfo si : subList) {
+                    if (si.getIccId() == iccId) {
+                        if (DBG)
+                            logd("[getActiveSubInfoUsingIccId]+ iccId=" + iccId + " subInfo=" + si);
+                        return si;
+                    }
                 }
             }
+            if (DBG) {
+                logd("[getActiveSubInfoUsingIccId]+ iccId=" + iccId
+                        + " subList=" + subList + " subInfo=null");
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
-        if (DBG) {
-            logd("[getActiveSubInfoUsingIccId]+ iccId=" + iccId
-                    + " subList=" + subList + " subInfo=null");
-        }
+
         return null;
     }
 
     /**
      * Get the active SubscriptionInfo associated with the slotIdx
      * @param slotIdx the slot which the subscription is inserted
+     * @param callingPackage The package making the IPC.
      * @return SubscriptionInfo, maybe null if its not active
      */
     @Override
-    public SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIdx) {
-        enforceSubscriptionPermission();
+    public SubscriptionInfo getActiveSubscriptionInfoForSimSlotIndex(int slotIdx,
+            String callingPackage) {
+        if (!canReadPhoneState(callingPackage, "getActiveSubscriptionInfoForSimSlotIndex")) {
+            return null;
+        }
 
-        List<SubscriptionInfo> subList = getActiveSubscriptionInfoList();
-        if (subList != null) {
-            for (SubscriptionInfo si : subList) {
-                if (si.getSimSlotIndex() == slotIdx) {
-                    if (DBG) {
-                        logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIdx=" + slotIdx
-                            + " subId=" + si);
+        // Now that all security checks passes, perform the operation as ourselves.
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            List<SubscriptionInfo> subList = getActiveSubscriptionInfoList(
+                    mContext.getOpPackageName());
+            if (subList != null) {
+                for (SubscriptionInfo si : subList) {
+                    if (si.getSimSlotIndex() == slotIdx) {
+                        if (DBG) {
+                            logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIdx=" + slotIdx
+                                    + " subId=" + si);
+                        }
+                        return si;
                     }
-                    return si;
+                }
+                if (DBG) {
+                    logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIdx=" + slotIdx
+                            + " subId=null");
+                }
+            } else {
+                if (DBG) {
+                    logd("[getActiveSubscriptionInfoForSimSlotIndex]+ subList=null");
                 }
             }
-            if (DBG) {
-                logd("[getActiveSubscriptionInfoForSimSlotIndex]+ slotIdx=" + slotIdx
-                    + " subId=null");
-            }
-        } else {
-            if (DBG) {
-                logd("[getActiveSubscriptionInfoForSimSlotIndex]+ subList=null");
-            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
+
         return null;
     }
 
     /**
+     * @param callingPackage The package making the IPC.
      * @return List of all SubscriptionInfo records in database,
      * include those that were inserted before, maybe empty but not null.
      * @hide
      */
     @Override
-    public List<SubscriptionInfo> getAllSubInfoList() {
+    public List<SubscriptionInfo> getAllSubInfoList(String callingPackage) {
         if (DBG) logd("[getAllSubInfoList]+");
-        enforceSubscriptionPermission();
 
-        List<SubscriptionInfo> subList = null;
-        subList = getSubInfo(null, null);
-        if (subList != null) {
-            if (DBG) logd("[getAllSubInfoList]- " + subList.size() + " infos return");
-        } else {
-            if (DBG) logd("[getAllSubInfoList]- no info return");
+        if (!canReadPhoneState(callingPackage, "getAllSubInfoList")) {
+            return null;
         }
 
-        return subList;
+        // Now that all security checks passes, perform the operation as ourselves.
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            List<SubscriptionInfo> subList = null;
+            subList = getSubInfo(null, null);
+            if (subList != null) {
+                if (DBG) logd("[getAllSubInfoList]- " + subList.size() + " infos return");
+            } else {
+                if (DBG) logd("[getAllSubInfoList]- no info return");
+            }
+            return subList;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
      * Get the SubInfoRecord(s) of the currently inserted SIM(s)
+     * @param callingPackage The package making the IPC.
      * @return Array list of currently inserted SubInfoRecord(s)
      */
     @Override
-    public List<SubscriptionInfo> getActiveSubscriptionInfoList() {
-        enforceSubscriptionPermission();
+    public List<SubscriptionInfo> getActiveSubscriptionInfoList(String callingPackage) {
         if (DBG) logdl("[getActiveSubInfoList]+");
 
-        List<SubscriptionInfo> subList = null;
-
-        if (!isSubInfoReady()) {
-            if (DBG) logdl("[getActiveSubInfoList] Sub Controller not ready");
-            return subList;
+        if (!canReadPhoneState(callingPackage, "getActiveSubscriptionInfoList")) {
+            return null;
         }
 
-        subList = getSubInfo(SubscriptionManager.SIM_SLOT_INDEX + ">=0", null);
-        if (subList != null) {
-            // FIXME: Unnecessary when an insertion sort is used!
-            Collections.sort(subList, new Comparator<SubscriptionInfo>() {
-                @Override
-                public int compare(SubscriptionInfo arg0, SubscriptionInfo arg1) {
-                    // Primary sort key on SimSlotIndex
-                    int flag = arg0.getSimSlotIndex() - arg1.getSimSlotIndex();
-                    if (flag == 0) {
-                        // Secondary sort on SubscriptionId
-                        return arg0.getSubscriptionId() - arg1.getSubscriptionId();
+        // Now that all security checks passes, perform the operation as ourselves.
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            if (!isSubInfoReady()) {
+                if (DBG) logdl("[getActiveSubInfoList] Sub Controller not ready");
+                return null;
+            }
+
+            List<SubscriptionInfo> subList = getSubInfo(
+                    SubscriptionManager.SIM_SLOT_INDEX + ">=0", null);
+
+            if (subList != null) {
+                // FIXME: Unnecessary when an insertion sort is used!
+                Collections.sort(subList, new Comparator<SubscriptionInfo>() {
+                    @Override
+                    public int compare(SubscriptionInfo arg0, SubscriptionInfo arg1) {
+                        // Primary sort key on SimSlotIndex
+                        int flag = arg0.getSimSlotIndex() - arg1.getSimSlotIndex();
+                        if (flag == 0) {
+                            // Secondary sort on SubscriptionId
+                            return arg0.getSubscriptionId() - arg1.getSubscriptionId();
+                        }
+                        return flag;
                     }
-                    return flag;
-                }
-            });
+                });
 
-            if (DBG) logdl("[getActiveSubInfoList]- " + subList.size() + " infos return");
-        } else {
-            if (DBG) logdl("[getActiveSubInfoList]- no info return");
+                if (DBG) logdl("[getActiveSubInfoList]- " + subList.size() + " infos return");
+            } else {
+                if (DBG) logdl("[getActiveSubInfoList]- no info return");
+            }
+
+            return subList;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
-
-        return subList;
     }
 
     /**
      * Get the SUB count of active SUB(s)
+     * @param callingPackage The package making the IPC.
      * @return active SIM count
      */
     @Override
-    public int getActiveSubInfoCount() {
+    public int getActiveSubInfoCount(String callingPackage) {
         if (DBG) logd("[getActiveSubInfoCount]+");
-        List<SubscriptionInfo> records = getActiveSubscriptionInfoList();
-        if (records == null) {
-            if (DBG) logd("[getActiveSubInfoCount] records null");
+
+        if (!canReadPhoneState(callingPackage, "getActiveSubInfoCount")) {
             return 0;
         }
-        if (DBG) logd("[getActiveSubInfoCount]- count: " + records.size());
-        return records.size();
+
+        // Now that all security checks passes, perform the operation as ourselves.
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            List<SubscriptionInfo> records = getActiveSubscriptionInfoList(
+                    mContext.getOpPackageName());
+            if (records == null) {
+                if (DBG) logd("[getActiveSubInfoCount] records null");
+                return 0;
+            }
+            if (DBG) logd("[getActiveSubInfoCount]- count: " + records.size());
+            return records.size();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
      * Get the SUB count of all SUB(s) in SubscriptoinInfo database
+     * @param callingPackage The package making the IPC.
      * @return all SIM count in database, include what was inserted before
      */
     @Override
-    public int getAllSubInfoCount() {
+    public int getAllSubInfoCount(String callingPackage) {
         if (DBG) logd("[getAllSubInfoCount]+");
-        enforceSubscriptionPermission();
 
-        Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
-                null, null, null, null);
-        try {
-            if (cursor != null) {
-                int count = cursor.getCount();
-                if (DBG) logd("[getAllSubInfoCount]- " + count + " SUB(s) in DB");
-                return count;
-            }
-        } finally {
-            if (cursor != null) {
-                cursor.close();
-            }
+        if (!canReadPhoneState(callingPackage, "getAllSubInfoCount")) {
+            return 0;
         }
-        if (DBG) logd("[getAllSubInfoCount]- no SUB in DB");
 
-        return 0;
+        // Now that all security checks passes, perform the operation as ourselves.
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
+                    null, null, null, null);
+            try {
+                if (cursor != null) {
+                    int count = cursor.getCount();
+                    if (DBG) logd("[getAllSubInfoCount]- " + count + " SUB(s) in DB");
+                    return count;
+                }
+            } finally {
+                if (cursor != null) {
+                    cursor.close();
+                }
+            }
+            if (DBG) logd("[getAllSubInfoCount]- no SUB in DB");
+
+            return 0;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -573,142 +666,158 @@
     @Override
     public int addSubInfoRecord(String iccId, int slotId) {
         if (DBG) logdl("[addSubInfoRecord]+ iccId:" + iccId + " slotId:" + slotId);
-        enforceSubscriptionPermission();
 
-        if (iccId == null) {
-            if (DBG) logdl("[addSubInfoRecord]- null iccId");
-            return -1;
-        }
+        enforceModifyPhoneState("addSubInfoRecord");
 
-        int[] subIds = getSubId(slotId);
-        if (subIds == null || subIds.length == 0) {
-            if (DBG) {
-                logdl("[addSubInfoRecord]- getSubId failed subIds == null || length == 0 subIds="
-                    + subIds);
-            }
-            return -1;
-        }
-
-        String nameToSet;
-        String simCarrierName = mTelephonyManager.getSimOperatorNameForSubscription(subIds[0]);
-
-        if (!TextUtils.isEmpty(simCarrierName)) {
-            nameToSet = simCarrierName;
-        } else {
-            nameToSet = "CARD " + Integer.toString(slotId + 1);
-        }
-        if (DBG) logdl("[addSubInfoRecord] sim name = " + nameToSet);
-        if (DBG) logdl("[addSubInfoRecord] carrier name = " + simCarrierName);
-
-        ContentResolver resolver = mContext.getContentResolver();
-        Cursor cursor = resolver.query(SubscriptionManager.CONTENT_URI,
-                new String[] {SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID,
-                SubscriptionManager.SIM_SLOT_INDEX, SubscriptionManager.NAME_SOURCE},
-                SubscriptionManager.ICC_ID + "=?", new String[] {iccId}, null);
-
-        int color = getUnusedColor();
-
+        // Now that all security checks passes, perform the operation as ourselves.
+        final long identity = Binder.clearCallingIdentity();
         try {
-            if (cursor == null || !cursor.moveToFirst()) {
-                ContentValues value = new ContentValues();
-                value.put(SubscriptionManager.ICC_ID, iccId);
-                // default SIM color differs between slots
-                value.put(SubscriptionManager.COLOR, color);
-                value.put(SubscriptionManager.SIM_SLOT_INDEX, slotId);
-                value.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
-                value.put(SubscriptionManager.CARRIER_NAME, "");
-                Uri uri = resolver.insert(SubscriptionManager.CONTENT_URI, value);
-                if (DBG) logdl("[addSubInfoRecord] New record created: " + uri);
-            } else {
-                int subId = cursor.getInt(0);
-                int oldSimInfoId = cursor.getInt(1);
-                int nameSource = cursor.getInt(2);
-                ContentValues value = new ContentValues();
+            if (iccId == null) {
+                if (DBG) logdl("[addSubInfoRecord]- null iccId");
+                return -1;
+            }
 
-                if (slotId != oldSimInfoId) {
+            ContentResolver resolver = mContext.getContentResolver();
+            Cursor cursor = resolver.query(SubscriptionManager.CONTENT_URI,
+                    new String[]{SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID,
+                            SubscriptionManager.SIM_SLOT_INDEX, SubscriptionManager.NAME_SOURCE},
+                    SubscriptionManager.ICC_ID + "=?", new String[]{iccId}, null);
+
+            int color = getUnusedColor(mContext.getOpPackageName());
+            boolean setDisplayName = false;
+            try {
+                if (cursor == null || !cursor.moveToFirst()) {
+                    setDisplayName = true;
+                    ContentValues value = new ContentValues();
+                    value.put(SubscriptionManager.ICC_ID, iccId);
+                    // default SIM color differs between slots
+                    value.put(SubscriptionManager.COLOR, color);
                     value.put(SubscriptionManager.SIM_SLOT_INDEX, slotId);
-                }
+                    value.put(SubscriptionManager.CARRIER_NAME, "");
+                    Uri uri = resolver.insert(SubscriptionManager.CONTENT_URI, value);
+                    if (DBG) logdl("[addSubInfoRecord] New record created: " + uri);
+                } else {
+                    int subId = cursor.getInt(0);
+                    int oldSimInfoId = cursor.getInt(1);
+                    int nameSource = cursor.getInt(2);
+                    ContentValues value = new ContentValues();
 
-                if (nameSource != SubscriptionManager.NAME_SOURCE_USER_INPUT) {
-                    value.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
-                }
-
-                if (value.size() > 0) {
-                    resolver.update(SubscriptionManager.CONTENT_URI, value,
-                            SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID +
-                            "=" + Long.toString(subId), null);
-                }
-
-                if (DBG) logdl("[addSubInfoRecord] Record already exists");
-            }
-        } finally {
-            if (cursor != null) {
-                cursor.close();
-            }
-        }
-
-        cursor = resolver.query(SubscriptionManager.CONTENT_URI, null,
-                SubscriptionManager.SIM_SLOT_INDEX + "=?",
-                new String[] {String.valueOf(slotId)}, null);
-        try {
-            if (cursor != null && cursor.moveToFirst()) {
-                do {
-                    int subId = cursor.getInt(cursor.getColumnIndexOrThrow(
-                            SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID));
-                    // If mSlotIdToSubIdMap already has a valid subId for a slotId/phoneId,
-                    // do not add another subId for same slotId/phoneId.
-                    Integer currentSubId = mSlotIdxToSubId.get(slotId);
-                    if (currentSubId == null
-                            || !SubscriptionManager.isValidSubscriptionId(currentSubId)) {
-                        // TODO While two subs active, if user deactivats first
-                        // one, need to update the default subId with second one.
-
-                        // FIXME: Currently we assume phoneId == slotId which in the future
-                        // may not be true, for instance with multiple subs per slot.
-                        // But is true at the moment.
-                        mSlotIdxToSubId.put(slotId, subId);
-                        int subIdCountMax = getActiveSubInfoCountMax();
-                        int defaultSubId = getDefaultSubId();
-                        if (DBG) {
-                            logdl("[addSubInfoRecord]"
-                                + " mSlotIdxToSubId.size=" + mSlotIdxToSubId.size()
-                                + " slotId=" + slotId + " subId=" + subId
-                                + " defaultSubId=" + defaultSubId + " simCount=" + subIdCountMax);
-                        }
-
-                        // Set the default sub if not set or if single sim device
-                        if (!SubscriptionManager.isValidSubscriptionId(defaultSubId)
-                                || subIdCountMax == 1) {
-                            setDefaultFallbackSubId(subId);
-                        }
-                        // If single sim device, set this subscription as the default for everything
-                        if (subIdCountMax == 1) {
-                            if (DBG) {
-                                logdl("[addSubInfoRecord] one sim set defaults to subId=" + subId);
-                            }
-                            setDefaultDataSubId(subId);
-                            setDefaultSmsSubId(subId);
-                            setDefaultVoiceSubId(subId);
-                        }
-                    } else {
-                        if (DBG) {
-                            logdl("[addSubInfoRecord] currentSubId != null"
-                                + " && currentSubId is valid, IGNORE");
-                        }
+                    if (slotId != oldSimInfoId) {
+                        value.put(SubscriptionManager.SIM_SLOT_INDEX, slotId);
                     }
-                    if (DBG) logdl("[addSubInfoRecord] hashmap(" + slotId + "," + subId + ")");
-                } while (cursor.moveToNext());
+
+                    if (nameSource != SubscriptionManager.NAME_SOURCE_USER_INPUT) {
+                        setDisplayName = true;
+                    }
+
+                    if (value.size() > 0) {
+                        resolver.update(SubscriptionManager.CONTENT_URI, value,
+                                SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID +
+                                        "=" + Long.toString(subId), null);
+                    }
+
+                    if (DBG) logdl("[addSubInfoRecord] Record already exists");
+                }
+            } finally {
+                if (cursor != null) {
+                    cursor.close();
+                }
             }
+
+            cursor = resolver.query(SubscriptionManager.CONTENT_URI, null,
+                    SubscriptionManager.SIM_SLOT_INDEX + "=?",
+                    new String[] {String.valueOf(slotId)}, null);
+            try {
+                if (cursor != null && cursor.moveToFirst()) {
+                    do {
+                        int subId = cursor.getInt(cursor.getColumnIndexOrThrow(
+                                SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID));
+                        // If sSlotIdxToSubId already has a valid subId for a slotId/phoneId,
+                        // do not add another subId for same slotId/phoneId.
+                        Integer currentSubId = sSlotIdxToSubId.get(slotId);
+                        if (currentSubId == null
+                                || !SubscriptionManager.isValidSubscriptionId(currentSubId)) {
+                            // TODO While two subs active, if user deactivats first
+                            // one, need to update the default subId with second one.
+
+                            // FIXME: Currently we assume phoneId == slotId which in the future
+                            // may not be true, for instance with multiple subs per slot.
+                            // But is true at the moment.
+                            sSlotIdxToSubId.put(slotId, subId);
+                            int subIdCountMax = getActiveSubInfoCountMax();
+                            int defaultSubId = getDefaultSubId();
+                            if (DBG) {
+                                logdl("[addSubInfoRecord]"
+                                        + " sSlotIdxToSubId.size=" + sSlotIdxToSubId.size()
+                                        + " slotId=" + slotId + " subId=" + subId
+                                        + " defaultSubId=" + defaultSubId + " simCount=" + subIdCountMax);
+                            }
+
+                            // Set the default sub if not set or if single sim device
+                            if (!SubscriptionManager.isValidSubscriptionId(defaultSubId)
+                                    || subIdCountMax == 1) {
+                                setDefaultFallbackSubId(subId);
+                            }
+                            // If single sim device, set this subscription as the default for everything
+                            if (subIdCountMax == 1) {
+                                if (DBG) {
+                                    logdl("[addSubInfoRecord] one sim set defaults to subId=" + subId);
+                                }
+                                setDefaultDataSubId(subId);
+                                setDefaultSmsSubId(subId);
+                                setDefaultVoiceSubId(subId);
+                            }
+                        } else {
+                            if (DBG) {
+                                logdl("[addSubInfoRecord] currentSubId != null"
+                                        + " && currentSubId is valid, IGNORE");
+                            }
+                        }
+                        if (DBG) logdl("[addSubInfoRecord] hashmap(" + slotId + "," + subId + ")");
+                    } while (cursor.moveToNext());
+                }
+            } finally {
+                if (cursor != null) {
+                    cursor.close();
+                }
+            }
+
+            // Set Display name after sub id is set above so as to get valid simCarrierName
+            int[] subIds = getSubId(slotId);
+            if (subIds == null || subIds.length == 0) {
+                if (DBG) {
+                    logdl("[addSubInfoRecord]- getSubId failed subIds == null || length == 0 subIds="
+                            + subIds);
+                }
+                return -1;
+            }
+            if (setDisplayName) {
+                String simCarrierName = mTelephonyManager.getSimOperatorNameForSubscription(subIds[0]);
+                String nameToSet;
+
+                if (!TextUtils.isEmpty(simCarrierName)) {
+                    nameToSet = simCarrierName;
+                } else {
+                    nameToSet = "CARD " + Integer.toString(slotId + 1);
+                }
+
+                ContentValues value = new ContentValues();
+                value.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
+                resolver.update(SubscriptionManager.CONTENT_URI, value,
+                        SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID +
+                                "=" + Long.toString(subIds[0]), null);
+
+                if (DBG) logdl("[addSubInfoRecord] sim name = " + nameToSet);
+            }
+
+            // Once the records are loaded, notify DcTracker
+            updateAllDataConnectionTrackers();
+
+            if (DBG) logdl("[addSubInfoRecord]- info size=" + sSlotIdxToSubId.size());
+
         } finally {
-            if (cursor != null) {
-                cursor.close();
-            }
+            Binder.restoreCallingIdentity(identity);
         }
-
-        // Once the records are loaded, notify DcTracker
-        updateAllDataConnectionTrackers();
-
-        if (DBG) logdl("[addSubInfoRecord]- info size=" + mSlotIdxToSubId.size());
         return 0;
     }
 
@@ -740,11 +849,13 @@
             if (showPlmn) {
                 carrierText = plmn;
                 if (showSpn) {
-                    // Need to show both plmn and spn.
-                    String separator = mContext.getString(
-                            com.android.internal.R.string.kg_text_message_separator).toString();
-                    carrierText = new StringBuilder().append(carrierText).append(separator)
-                            .append(spn).toString();
+                    // Need to show both plmn and spn if both are not same.
+                    if(!Objects.equals(spn, plmn)) {
+                        String separator = mContext.getString(
+                                com.android.internal.R.string.kg_text_message_separator).toString();
+                        carrierText = new StringBuilder().append(carrierText).append(separator)
+                                .append(spn).toString();
+                    }
                 }
             } else if (showSpn) {
                 carrierText = spn;
@@ -764,16 +875,24 @@
      */
     private int setCarrierText(String text, int subId) {
         if (DBG) logd("[setCarrierText]+ text:" + text + " subId:" + subId);
-        enforceSubscriptionPermission();
 
-        ContentValues value = new ContentValues(1);
-        value.put(SubscriptionManager.CARRIER_NAME, text);
+        enforceModifyPhoneState("setCarrierText");
 
-        int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
-                SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + Long.toString(subId), null);
-        notifySubscriptionInfoChanged();
+        // Now that all security checks passes, perform the operation as ourselves.
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            ContentValues value = new ContentValues(1);
+            value.put(SubscriptionManager.CARRIER_NAME, text);
 
-        return result;
+            int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
+                    value, SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" +
+                    Long.toString(subId), null);
+            notifySubscriptionInfoChanged();
+
+            return result;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -785,18 +904,26 @@
     @Override
     public int setIconTint(int tint, int subId) {
         if (DBG) logd("[setIconTint]+ tint:" + tint + " subId:" + subId);
-        enforceSubscriptionPermission();
 
-        validateSubId(subId);
-        ContentValues value = new ContentValues(1);
-        value.put(SubscriptionManager.COLOR, tint);
-        if (DBG) logd("[setIconTint]- tint:" + tint + " set");
+        enforceModifyPhoneState("setIconTint");
 
-        int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
-                SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + Long.toString(subId), null);
-        notifySubscriptionInfoChanged();
+        // Now that all security checks passes, perform the operation as ourselves.
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            validateSubId(subId);
+            ContentValues value = new ContentValues(1);
+            value.put(SubscriptionManager.COLOR, tint);
+            if (DBG) logd("[setIconTint]- tint:" + tint + " set");
 
-        return result;
+            int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
+                    value, SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" +
+                    Long.toString(subId), null);
+            notifySubscriptionInfoChanged();
+
+            return result;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
@@ -824,28 +951,36 @@
             logd("[setDisplayName]+  displayName:" + displayName + " subId:" + subId
                 + " nameSource:" + nameSource);
         }
-        enforceSubscriptionPermission();
 
-        validateSubId(subId);
-        String nameToSet;
-        if (displayName == null) {
-            nameToSet = mContext.getString(SubscriptionManager.DEFAULT_NAME_RES);
-        } else {
-            nameToSet = displayName;
+        enforceModifyPhoneState("setDisplayNameUsingSrc");
+
+        // Now that all security checks passes, perform the operation as ourselves.
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            validateSubId(subId);
+            String nameToSet;
+            if (displayName == null) {
+                nameToSet = mContext.getString(SubscriptionManager.DEFAULT_NAME_RES);
+            } else {
+                nameToSet = displayName;
+            }
+            ContentValues value = new ContentValues(1);
+            value.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
+            if (nameSource >= SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE) {
+                if (DBG) logd("Set nameSource=" + nameSource);
+                value.put(SubscriptionManager.NAME_SOURCE, nameSource);
+            }
+            if (DBG) logd("[setDisplayName]- mDisplayName:" + nameToSet + " set");
+
+            int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
+                    value, SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" +
+                    Long.toString(subId), null);
+            notifySubscriptionInfoChanged();
+
+            return result;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
-        ContentValues value = new ContentValues(1);
-        value.put(SubscriptionManager.DISPLAY_NAME, nameToSet);
-        if (nameSource >= SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE) {
-            if (DBG) logd("Set nameSource=" + nameSource);
-            value.put(SubscriptionManager.NAME_SOURCE, nameSource);
-        }
-        if (DBG) logd("[setDisplayName]- mDisplayName:" + nameToSet + " set");
-
-        int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
-                SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + Long.toString(subId), null);
-        notifySubscriptionInfoChanged();
-
-        return result;
     }
 
     /**
@@ -857,31 +992,38 @@
     @Override
     public int setDisplayNumber(String number, int subId) {
         if (DBG) logd("[setDisplayNumber]+ subId:" + subId);
-        enforceSubscriptionPermission();
 
-        validateSubId(subId);
-        int result;
-        int phoneId = getPhoneId(subId);
+        enforceModifyPhoneState("setDisplayNumber");
 
-        if (number == null || phoneId < 0 ||
-                phoneId >= mTelephonyManager.getPhoneCount()) {
-            if (DBG) logd("[setDispalyNumber]- fail");
-            return -1;
+        // Now that all security checks passes, perform the operation as ourselves.
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            validateSubId(subId);
+            int result;
+            int phoneId = getPhoneId(subId);
+
+            if (number == null || phoneId < 0 ||
+                    phoneId >= mTelephonyManager.getPhoneCount()) {
+                if (DBG) logd("[setDispalyNumber]- fail");
+                return -1;
+            }
+            ContentValues value = new ContentValues(1);
+            value.put(SubscriptionManager.NUMBER, number);
+
+            // This function had a call to update number on the SIM (Phone.setLine1Number()) but
+            // that was removed as there doesn't seem to be a reason for that. If it is added
+            // back, watch out for deadlocks.
+
+            result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
+                    SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID
+                            + "=" + Long.toString(subId), null);
+            if (DBG) logd("[setDisplayNumber]- update result :" + result);
+            notifySubscriptionInfoChanged();
+
+            return result;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
-        ContentValues value = new ContentValues(1);
-        value.put(SubscriptionManager.NUMBER, number);
-
-        // This function had a call to update number on the SIM (Phone.setLine1Number()) but that
-        // was removed as there doesn't seem to be a reason for that. If it is added back, watch out
-        // for deadlocks.
-
-        result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
-                SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID
-                    + "=" + Long.toString(subId), null);
-        if (DBG) logd("[setDisplayNumber]- update result :" + result);
-        notifySubscriptionInfoChanged();
-
-        return result;
     }
 
     /**
@@ -893,22 +1035,30 @@
     @Override
     public int setDataRoaming(int roaming, int subId) {
         if (DBG) logd("[setDataRoaming]+ roaming:" + roaming + " subId:" + subId);
-        enforceSubscriptionPermission();
 
-        validateSubId(subId);
-        if (roaming < 0) {
-            if (DBG) logd("[setDataRoaming]- fail");
-            return -1;
+        enforceModifyPhoneState("setDataRoaming");
+
+        // Now that all security checks passes, perform the operation as ourselves.
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            validateSubId(subId);
+            if (roaming < 0) {
+                if (DBG) logd("[setDataRoaming]- fail");
+                return -1;
+            }
+            ContentValues value = new ContentValues(1);
+            value.put(SubscriptionManager.DATA_ROAMING, roaming);
+            if (DBG) logd("[setDataRoaming]- roaming:" + roaming + " set");
+
+            int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI,
+                    value, SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" +
+                    Long.toString(subId), null);
+            notifySubscriptionInfoChanged();
+
+            return result;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
-        ContentValues value = new ContentValues(1);
-        value.put(SubscriptionManager.DATA_ROAMING, roaming);
-        if (DBG) logd("[setDataRoaming]- roaming:" + roaming + " set");
-
-        int result = mContext.getContentResolver().update(SubscriptionManager.CONTENT_URI, value,
-                SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=" + Long.toString(subId), null);
-        notifySubscriptionInfoChanged();
-
-        return result;
     }
 
     /**
@@ -951,7 +1101,7 @@
             return SubscriptionManager.INVALID_SIM_SLOT_INDEX;
         }
 
-        int size = mSlotIdxToSubId.size();
+        int size = sSlotIdxToSubId.size();
 
         if (size == 0)
         {
@@ -959,7 +1109,7 @@
             return SubscriptionManager.SIM_NOT_INSERTED;
         }
 
-        for (Entry<Integer, Integer> entry: mSlotIdxToSubId.entrySet()) {
+        for (Entry<Integer, Integer> entry: sSlotIdxToSubId.entrySet()) {
             int sim = entry.getKey();
             int sub = entry.getValue();
 
@@ -1000,10 +1150,10 @@
         }
 
         // Check if we've got any SubscriptionInfo records using slotIdToSubId as a surrogate.
-        int size = mSlotIdxToSubId.size();
+        int size = sSlotIdxToSubId.size();
         if (size == 0) {
             if (DBG) {
-                logd("[getSubId]- mSlotIdToSubIdMap.size == 0, return DummySubIds slotIdx="
+                logd("[getSubId]- sSlotIdxToSubId.size == 0, return DummySubIds slotIdx="
                         + slotIdx);
             }
             return getDummySubIds(slotIdx);
@@ -1011,7 +1161,7 @@
 
         // Create an array of subIds that are in this slot?
         ArrayList<Integer> subIds = new ArrayList<Integer>();
-        for (Entry<Integer, Integer> entry: mSlotIdxToSubId.entrySet()) {
+        for (Entry<Integer, Integer> entry: sSlotIdxToSubId.entrySet()) {
             int slot = entry.getKey();
             int sub = entry.getValue();
             if (slotIdx == slot) {
@@ -1052,7 +1202,7 @@
             return SubscriptionManager.INVALID_PHONE_INDEX;
         }
 
-        int size = mSlotIdxToSubId.size();
+        int size = sSlotIdxToSubId.size();
         if (size == 0) {
             phoneId = mDefaultPhoneId;
             if (DBG) logdl("[getPhoneId]- no sims, returning default phoneId=" + phoneId);
@@ -1060,7 +1210,7 @@
         }
 
         // FIXME: Assumes phoneId == slotId
-        for (Entry<Integer, Integer> entry: mSlotIdxToSubId.entrySet()) {
+        for (Entry<Integer, Integer> entry: sSlotIdxToSubId.entrySet()) {
             int sim = entry.getKey();
             int sub = entry.getValue();
 
@@ -1089,7 +1239,7 @@
             for (int i = 0; i < numSubs; i++) {
                 dummyValues[i] = SubscriptionManager.DUMMY_SUBSCRIPTION_ID_BASE - slotIdx;
             }
-            if (DBG) {
+            if (VDBG) {
                 logd("getDummySubIds: slotIdx=" + slotIdx
                     + " return " + numSubs + " DummySubIds with each subId=" + dummyValues[0]);
             }
@@ -1104,19 +1254,26 @@
      */
     @Override
     public int clearSubInfo() {
-        enforceSubscriptionPermission();
-        if (DBG) logd("[clearSubInfo]+");
+        enforceModifyPhoneState("clearSubInfo");
 
-        int size = mSlotIdxToSubId.size();
+        // Now that all security checks passes, perform the operation as ourselves.
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            if (DBG) logd("[clearSubInfo]+");
 
-        if (size == 0) {
-            if (DBG) logdl("[clearSubInfo]- no simInfo size=" + size);
-            return 0;
+            int size = sSlotIdxToSubId.size();
+
+            if (size == 0) {
+                if (DBG) logdl("[clearSubInfo]- no simInfo size=" + size);
+                return 0;
+            }
+
+            sSlotIdxToSubId.clear();
+            if (DBG) logdl("[clearSubInfo]- clear size=" + size);
+            return size;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
-
-        mSlotIdxToSubId.clear();
-        if (DBG) logdl("[clearSubInfo]- clear size=" + size);
-        return size;
     }
 
     private void logvl(String msg) {
@@ -1162,7 +1319,7 @@
             subId = getDefaultDataSubId();
             if (VDBG) logdl("[getDefaultSubId] NOT VoiceCapable subId=" + subId);
         }
-        if ( ! isActiveSubId(subId)) {
+        if (!isActiveSubId(subId)) {
             subId = mDefaultFallbackSubId;
             if (VDBG) logdl("[getDefaultSubId] NOT active use fall back subId=" + subId);
         }
@@ -1172,6 +1329,8 @@
 
     @Override
     public void setDefaultSmsSubId(int subId) {
+        enforceModifyPhoneState("setDefaultSmsSubId");
+
         if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
             throw new RuntimeException("setDefaultSmsSubId called with DEFAULT_SUB_ID");
         }
@@ -1201,6 +1360,8 @@
 
     @Override
     public void setDefaultVoiceSubId(int subId) {
+        enforceModifyPhoneState("setDefaultVoiceSubId");
+
         if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
             throw new RuntimeException("setDefaultVoiceSubId called with DEFAULT_SUB_ID");
         }
@@ -1239,37 +1400,42 @@
 
     @Override
     public void setDefaultDataSubId(int subId) {
+        enforceModifyPhoneState("setDefaultDataSubId");
+
         if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
             throw new RuntimeException("setDefaultDataSubId called with DEFAULT_SUB_ID");
         }
         if (DBG) logdl("[setDefaultDataSubId] subId=" + subId);
 
+        ProxyController proxyController = ProxyController.getInstance();
         int len = sProxyPhones.length;
         logdl("[setDefaultDataSubId] num phones=" + len);
 
-        RadioAccessFamily[] rafs = new RadioAccessFamily[len];
-        for (int phoneId = 0; phoneId < len; phoneId++) {
-            PhoneProxy phone = sProxyPhones[phoneId];
-            int raf = phone.getRadioAccessFamily();
-            int id = phone.getSubId();
-            logdl("[setDefaultDataSubId] phoneId=" + phoneId + " subId=" + id + " RAF=" + raf);
-            raf |= RadioAccessFamily.RAF_GSM;
-            if (id == subId) {
-                raf |= RadioAccessFamily.RAF_UMTS;
-            } else {
-                raf &= ~RadioAccessFamily.RAF_UMTS;
+        if (SubscriptionManager.isValidSubscriptionId(subId)) {
+            // Only re-map modems if the new default data sub is valid
+            RadioAccessFamily[] rafs = new RadioAccessFamily[len];
+            boolean atLeastOneMatch = false;
+            for (int phoneId = 0; phoneId < len; phoneId++) {
+                PhoneProxy phone = sProxyPhones[phoneId];
+                int raf;
+                int id = phone.getSubId();
+                if (id == subId) {
+                    // TODO Handle the general case of N modems and M subscriptions.
+                    raf = proxyController.getMaxRafSupported();
+                    atLeastOneMatch = true;
+                } else {
+                    // TODO Handle the general case of N modems and M subscriptions.
+                    raf = proxyController.getMinRafSupported();
+                }
+                logdl("[setDefaultDataSubId] phoneId=" + phoneId + " subId=" + id + " RAF=" + raf);
+                rafs[phoneId] = new RadioAccessFamily(phoneId, raf);
             }
-            logdl("[setDefaultDataSubId] reqRAF=" + raf);
-
-            // Set the raf to the maximum of the requested and the user's preferred.
-            int networkType = PhoneFactory.calculatePreferredNetworkType(mContext, id);
-            logdl("[setDefaultDataSubId] networkType=" + networkType);
-            raf &= RadioAccessFamily.getRafFromNetworkType(networkType);
-
-            logdl("[setDefaultDataSubId] newRAF=" + raf);
-            rafs[phoneId] = new RadioAccessFamily(phoneId, raf);
+            if (atLeastOneMatch) {
+                proxyController.setRadioCapability(rafs);
+            } else {
+                if (DBG) logdl("[setDefaultDataSubId] no valid subId's found - not updating.");
+            }
         }
-        ProxyController.getInstance().setRadioCapability(rafs);
 
         // FIXME is this still needed?
         updateAllDataConnectionTrackers();
@@ -1322,8 +1488,8 @@
                 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
                 SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phoneId, subId);
                 if (DBG) {
-                    logdl("[setDefaultFallbackSubId] broadcast default subId changed phoneId=" + phoneId
-                            + " subId=" + subId);
+                    logdl("[setDefaultFallbackSubId] broadcast default subId changed phoneId=" +
+                            phoneId + " subId=" + subId);
                 }
                 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
             } else {
@@ -1337,19 +1503,28 @@
 
     @Override
     public void clearDefaultsForInactiveSubIds() {
-        final List<SubscriptionInfo> records = getActiveSubscriptionInfoList();
-        if (DBG) logdl("[clearDefaultsForInactiveSubIds] records: " + records);
-        if (shouldDefaultBeCleared(records, getDefaultDataSubId())) {
-            if (DBG) logd("[clearDefaultsForInactiveSubIds] clearing default data sub id");
-            setDefaultDataSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
-        }
-        if (shouldDefaultBeCleared(records, getDefaultSmsSubId())) {
-            if (DBG) logdl("[clearDefaultsForInactiveSubIds] clearing default sms sub id");
-            setDefaultSmsSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
-        }
-        if (shouldDefaultBeCleared(records, getDefaultVoiceSubId())) {
-            if (DBG) logdl("[clearDefaultsForInactiveSubIds] clearing default voice sub id");
-            setDefaultVoiceSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+        enforceModifyPhoneState("clearDefaultsForInactiveSubIds");
+
+        // Now that all security checks passes, perform the operation as ourselves.
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            final List<SubscriptionInfo> records = getActiveSubscriptionInfoList(
+                    mContext.getOpPackageName());
+            if (DBG) logdl("[clearDefaultsForInactiveSubIds] records: " + records);
+            if (shouldDefaultBeCleared(records, getDefaultDataSubId())) {
+                if (DBG) logd("[clearDefaultsForInactiveSubIds] clearing default data sub id");
+                setDefaultDataSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+            }
+            if (shouldDefaultBeCleared(records, getDefaultSmsSubId())) {
+                if (DBG) logdl("[clearDefaultsForInactiveSubIds] clearing default sms sub id");
+                setDefaultSmsSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+            }
+            if (shouldDefaultBeCleared(records, getDefaultVoiceSubId())) {
+                if (DBG) logdl("[clearDefaultsForInactiveSubIds] clearing default voice sub id");
+                setDefaultVoiceSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
     }
 
@@ -1390,49 +1565,57 @@
         return getSubId(slotId);
     }
 
-    public List<SubscriptionInfo> getSubInfoUsingSlotIdWithCheck(int slotId, boolean needCheck) {
+    public List<SubscriptionInfo> getSubInfoUsingSlotIdWithCheck(int slotId, boolean needCheck,
+            String callingPackage) {
         if (DBG) logd("[getSubInfoUsingSlotIdWithCheck]+ slotId:" + slotId);
-        enforceSubscriptionPermission();
 
-        if (slotId == SubscriptionManager.DEFAULT_SIM_SLOT_INDEX) {
-            slotId = getSlotId(getDefaultSubId());
-        }
-        if (!SubscriptionManager.isValidSlotId(slotId)) {
-            if (DBG) logd("[getSubInfoUsingSlotIdWithCheck]- invalid slotId");
+        if (!canReadPhoneState(callingPackage, "getSubInfoUsingSlotIdWithCheck")) {
             return null;
         }
 
-        if (needCheck && !isSubInfoReady()) {
-            if (DBG) logd("[getSubInfoUsingSlotIdWithCheck]- not ready");
-            return null;
-        }
-
-        Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
-                null, SubscriptionManager.SIM_SLOT_INDEX + "=?",
-                new String[] {String.valueOf(slotId)}, null);
-        ArrayList<SubscriptionInfo> subList = null;
+        // Now that all security checks passes, perform the operation as ourselves.
+        final long identity = Binder.clearCallingIdentity();
         try {
-            if (cursor != null) {
-                while (cursor.moveToNext()) {
-                    SubscriptionInfo subInfo = getSubInfoRecord(cursor);
-                    if (subInfo != null)
-                    {
-                        if (subList == null)
-                        {
-                            subList = new ArrayList<SubscriptionInfo>();
+            if (slotId == SubscriptionManager.DEFAULT_SIM_SLOT_INDEX) {
+                slotId = getSlotId(getDefaultSubId());
+            }
+            if (!SubscriptionManager.isValidSlotId(slotId)) {
+                if (DBG) logd("[getSubInfoUsingSlotIdWithCheck]- invalid slotId");
+                return null;
+            }
+
+            if (needCheck && !isSubInfoReady()) {
+                if (DBG) logd("[getSubInfoUsingSlotIdWithCheck]- not ready");
+                return null;
+            }
+
+            Cursor cursor = mContext.getContentResolver().query(SubscriptionManager.CONTENT_URI,
+                    null, SubscriptionManager.SIM_SLOT_INDEX + "=?",
+                    new String[]{String.valueOf(slotId)}, null);
+            ArrayList<SubscriptionInfo> subList = null;
+            try {
+                if (cursor != null) {
+                    while (cursor.moveToNext()) {
+                        SubscriptionInfo subInfo = getSubInfoRecord(cursor);
+                        if (subInfo != null) {
+                            if (subList == null) {
+                                subList = new ArrayList<SubscriptionInfo>();
+                            }
+                            subList.add(subInfo);
                         }
-                        subList.add(subInfo);
                     }
                 }
+            } finally {
+                if (cursor != null) {
+                    cursor.close();
+                }
             }
-        } finally {
-            if (cursor != null) {
-                cursor.close();
-            }
-        }
-        if (DBG) logd("[getSubInfoUsingSlotId]- null info return");
+            if (DBG) logd("[getSubInfoUsingSlotId]- null info return");
 
-        return subList;
+            return subList;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     private void validateSubId(int subId) {
@@ -1453,7 +1636,7 @@
      */
     @Override
     public int[] getActiveSubIdList() {
-        Set<Entry<Integer, Integer>> simInfoSet = mSlotIdxToSubId.entrySet();
+        Set<Entry<Integer, Integer>> simInfoSet = sSlotIdxToSubId.entrySet();
         if (DBG) logdl("[getActiveSubIdList] simInfoSet=" + simInfoSet);
 
         int[] subIdArr = new int[simInfoSet.size()];
@@ -1468,39 +1651,28 @@
         return subIdArr;
     }
 
-    private boolean isActiveSubId(int subId) {
-        boolean retVal = false;
-
-        if (SubscriptionManager.isValidSubscriptionId(subId)) {
-            Set<Entry<Integer, Integer>> simInfoSet = mSlotIdxToSubId.entrySet();
-            if (VDBG) logdl("[isActiveSubId] simInfoSet=" + simInfoSet);
-
-            for (Entry<Integer, Integer> entry: simInfoSet) {
-                if (subId == entry.getValue()) {
-                    retVal = true;
-                    break;
-                }
-            }
-        }
+    @Override
+    public boolean isActiveSubId(int subId) {
+        boolean retVal = SubscriptionManager.isValidSubscriptionId(subId)
+                && sSlotIdxToSubId.containsValue(subId);
 
         if (VDBG) logdl("[isActiveSubId]- " + retVal);
         return retVal;
     }
 
     /**
-     * Get the SIM state for the subscriber
+     * Get the SIM state for the slot idx
      * @return SIM state as the ordinal of {@See IccCardConstants.State}
      */
     @Override
-    public int getSimStateForSubscriber(int subId) {
+    public int getSimStateForSlotIdx(int slotIdx) {
         State simState;
         String err;
-        int phoneIdx = getPhoneId(subId);
-        if (phoneIdx < 0) {
+        if (slotIdx < 0) {
             simState = IccCardConstants.State.UNKNOWN;
-            err = "invalid PhoneIdx";
+            err = "invalid slotIdx";
         } else {
-            Phone phone = PhoneFactory.getPhone(phoneIdx);
+            Phone phone = PhoneFactory.getPhone(slotIdx);
             if (phone == null) {
                 simState = IccCardConstants.State.UNKNOWN;
                 err = "phone == null";
@@ -1515,11 +1687,106 @@
                 }
             }
         }
-        if (DBG) logd("getSimStateForSubscriber: " + err + " simState=" + simState
+        if (DBG) logd("getSimStateForSlotIdx: " + err + " simState=" + simState
                 + " ordinal=" + simState.ordinal());
         return simState.ordinal();
     }
 
+    /**
+     * Store properties associated with SubscriptionInfo in database
+     * @param subId Subscription Id of Subscription
+     * @param propKey Column name in database associated with SubscriptionInfo
+     * @param propValue Value to store in DB for particular subId & column name
+     * @hide
+     */
+    @Override
+    public void setSubscriptionProperty(int subId, String propKey, String propValue) {
+        enforceModifyPhoneState("setSubscriptionProperty");
+        final long token = Binder.clearCallingIdentity();
+        ContentResolver resolver = mContext.getContentResolver();
+        ContentValues value = new ContentValues();
+        switch (propKey) {
+            case SubscriptionManager.CB_EXTREME_THREAT_ALERT:
+            case SubscriptionManager.CB_SEVERE_THREAT_ALERT:
+            case SubscriptionManager.CB_AMBER_ALERT:
+            case SubscriptionManager.CB_EMERGENCY_ALERT:
+            case SubscriptionManager.CB_ALERT_SOUND_DURATION:
+            case SubscriptionManager.CB_ALERT_REMINDER_INTERVAL:
+            case SubscriptionManager.CB_ALERT_VIBRATE:
+            case SubscriptionManager.CB_ALERT_SPEECH:
+            case SubscriptionManager.CB_ETWS_TEST_ALERT:
+            case SubscriptionManager.CB_CHANNEL_50_ALERT:
+            case SubscriptionManager.CB_CMAS_TEST_ALERT:
+            case SubscriptionManager.CB_OPT_OUT_DIALOG:
+                value.put(propKey, Integer.parseInt(propValue));
+                break;
+            default:
+                if(DBG) logd("Invalid column name");
+                break;
+        }
+
+        resolver.update(SubscriptionManager.CONTENT_URI, value,
+                SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID +
+                        "=" + Integer.toString(subId), null);
+        Binder.restoreCallingIdentity(token);
+    }
+
+    /**
+     * Store properties associated with SubscriptionInfo in database
+     * @param subId Subscription Id of Subscription
+     * @param propKey Column name in SubscriptionInfo database
+     * @return Value associated with subId and propKey column in database
+     * @hide
+     */
+    @Override
+    public String getSubscriptionProperty(int subId, String propKey, String callingPackage) {
+        if (!canReadPhoneState(callingPackage, "getSubInfoUsingSlotIdWithCheck")) {
+            return null;
+        }
+        String resultValue = null;
+        ContentResolver resolver = mContext.getContentResolver();
+        Cursor cursor = resolver.query(SubscriptionManager.CONTENT_URI,
+                new String[]{propKey},
+                SubscriptionManager.UNIQUE_KEY_SUBSCRIPTION_ID + "=?",
+                new String[]{subId + ""}, null);
+
+        try {
+            if (cursor != null) {
+                if (cursor.moveToFirst()) {
+                    switch (propKey) {
+                        case SubscriptionManager.CB_EXTREME_THREAT_ALERT:
+                        case SubscriptionManager.CB_SEVERE_THREAT_ALERT:
+                        case SubscriptionManager.CB_AMBER_ALERT:
+                        case SubscriptionManager.CB_EMERGENCY_ALERT:
+                        case SubscriptionManager.CB_ALERT_SOUND_DURATION:
+                        case SubscriptionManager.CB_ALERT_REMINDER_INTERVAL:
+                        case SubscriptionManager.CB_ALERT_VIBRATE:
+                        case SubscriptionManager.CB_ALERT_SPEECH:
+                        case SubscriptionManager.CB_ETWS_TEST_ALERT:
+                        case SubscriptionManager.CB_CHANNEL_50_ALERT:
+                        case SubscriptionManager.CB_CMAS_TEST_ALERT:
+                        case SubscriptionManager.CB_OPT_OUT_DIALOG:
+                            resultValue = cursor.getInt(0) + "";
+                            break;
+                        default:
+                            if(DBG) logd("Invalid column name");
+                            break;
+                    }
+                } else {
+                    if(DBG) logd("Valid row not present in db");
+                }
+            } else {
+                if(DBG) logd("Query failed");
+            }
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+        if (DBG) logd("getSubscriptionProperty Query value = " + resultValue);
+        return resultValue;
+    }
+
     private static void printStackTrace(String msg) {
         RuntimeException re = new RuntimeException();
         slogd("StackTrace - " + msg);
@@ -1553,13 +1820,14 @@
                     .from(mContext).getDefaultSmsPhoneId());
             pw.flush();
 
-            for (Entry<Integer, Integer> entry : mSlotIdxToSubId.entrySet()) {
-                pw.println(" mSlotIdToSubIdMap[" + entry.getKey() + "]: subId=" + entry.getValue());
+            for (Entry<Integer, Integer> entry : sSlotIdxToSubId.entrySet()) {
+                pw.println(" sSlotIdxToSubId[" + entry.getKey() + "]: subId=" + entry.getValue());
             }
             pw.flush();
             pw.println("++++++++++++++++++++++++++++++++");
 
-            List<SubscriptionInfo> sirl = getActiveSubscriptionInfoList();
+            List<SubscriptionInfo> sirl = getActiveSubscriptionInfoList(
+                    mContext.getOpPackageName());
             if (sirl != null) {
                 pw.println(" ActiveSubInfoList:");
                 for (SubscriptionInfo entry : sirl) {
@@ -1571,7 +1839,7 @@
             pw.flush();
             pw.println("++++++++++++++++++++++++++++++++");
 
-            sirl = getAllSubInfoList();
+            sirl = getAllSubInfoList(mContext.getOpPackageName());
             if (sirl != null) {
                 pw.println(" AllSubInfoList:");
                 for (SubscriptionInfo entry : sirl) {
diff --git a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
index 736cf7c..64eaf15 100644
--- a/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
+++ b/src/java/com/android/internal/telephony/SubscriptionInfoUpdater.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.READ_PHONE_STATE;
 
 import android.app.ActivityManagerNative;
+import android.app.IUserSwitchObserver;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.ContentResolver;
@@ -26,14 +27,18 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
+import android.content.pm.IPackageManager;
 import android.os.AsyncResult;
 import android.os.Handler;
+import android.os.IRemoteCallback;
 import android.os.Message;
-import android.os.SystemProperties;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.preference.PreferenceManager;
 import android.provider.Settings;
 import android.telephony.Rlog;
+import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionInfo;
 import android.telephony.TelephonyManager;
@@ -42,8 +47,11 @@
 import com.android.internal.telephony.uicc.IccFileHandler;
 import com.android.internal.telephony.uicc.IccRecords;
 import com.android.internal.telephony.uicc.IccUtils;
+
 import android.text.TextUtils;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.List;
 
 /**
@@ -58,6 +66,8 @@
     private static final int EVENT_SIM_LOADED = 3;
     private static final int EVENT_SIM_ABSENT = 4;
     private static final int EVENT_SIM_LOCKED = 5;
+    private static final int EVENT_SIM_IO_ERROR = 6;
+    private static final int EVENT_SIM_UNKNOWN = 7;
 
     private static final String ICCID_STRING_FOR_NO_SIM = "";
     /**
@@ -91,6 +101,10 @@
     private static String mIccId[] = new String[PROJECT_SIM_NUM];
     private static int[] mInsertSimState = new int[PROJECT_SIM_NUM];
     private SubscriptionManager mSubscriptionManager = null;
+    private IPackageManager mPackageManager;
+    // The current foreground user ID.
+    private int mCurrentlyActiveUserId;
+    private CarrierServiceBindHelper mCarrierServiceBindHelper;
 
     public SubscriptionInfoUpdater(Context context, Phone[] phoneProxy, CommandsInterface[] ci) {
         logd("Constructor invoked");
@@ -98,11 +112,56 @@
         mContext = context;
         mPhone = phoneProxy;
         mSubscriptionManager = SubscriptionManager.from(mContext);
+        mPackageManager = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
 
         IntentFilter intentFilter = new IntentFilter(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+        intentFilter.addAction(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED);
         mContext.registerReceiver(sReceiver, intentFilter);
-        intentFilter = new IntentFilter(IccCardProxy.ACTION_INTERNAL_SIM_STATE_CHANGED);
-        mContext.registerReceiver(sReceiver, intentFilter);
+
+        mCarrierServiceBindHelper = new CarrierServiceBindHelper(mContext);
+        initializeCarrierApps();
+    }
+
+    private void initializeCarrierApps() {
+        // Initialize carrier apps:
+        // -Now (on system startup)
+        // -Whenever new carrier privilege rules might change (new SIM is loaded)
+        // -Whenever we switch to a new user
+        mCurrentlyActiveUserId = 0;
+        try {
+            ActivityManagerNative.getDefault().registerUserSwitchObserver(
+                    new IUserSwitchObserver.Stub() {
+                @Override
+                public void onUserSwitching(int newUserId, IRemoteCallback reply)
+                        throws RemoteException {
+                    mCurrentlyActiveUserId = newUserId;
+                    CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(),
+                            mPackageManager, TelephonyManager.getDefault(), mCurrentlyActiveUserId);
+
+                    if (reply != null) {
+                        try {
+                            reply.sendResult(null);
+                        } catch (RemoteException e) {
+                        }
+                    }
+                }
+
+                @Override
+                public void onUserSwitchComplete(int newUserId) {
+                    // Ignore.
+                }
+
+                @Override
+                public void onForegroundProfileSwitch(int newProfileId) throws RemoteException {
+                    // Ignore.
+                }
+            });
+            mCurrentlyActiveUserId = ActivityManagerNative.getDefault().getCurrentUser().id;
+        } catch (RemoteException e) {
+            logd("Couldn't get current user ID; guessing it's 0: " + e.getMessage());
+        }
+        CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(),
+                mPackageManager, TelephonyManager.getDefault(), mCurrentlyActiveUserId);
     }
 
     private final BroadcastReceiver sReceiver = new  BroadcastReceiver() {
@@ -130,6 +189,10 @@
             if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
                 if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(simStatus)) {
                     sendMessage(obtainMessage(EVENT_SIM_ABSENT, slotId, -1));
+                } else if (IccCardConstants.INTENT_VALUE_ICC_UNKNOWN.equals(simStatus)) {
+                    sendMessage(obtainMessage(EVENT_SIM_UNKNOWN, slotId, -1));
+                } else if (IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR.equals(simStatus)) {
+                    sendMessage(obtainMessage(EVENT_SIM_IO_ERROR, slotId, -1));
                 } else {
                     logd("Ignoring simStatus: " + simStatus);
                 }
@@ -208,6 +271,9 @@
                 }
                 broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_LOCKED,
                                          uObj.reason);
+                if (!ICCID_STRING_FOR_NO_SIM.equals(mIccId[slotId])) {
+                    updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_LOCKED);
+                }
                 break;
             }
 
@@ -237,6 +303,14 @@
                 handleSimLocked(msg.arg1, (String) msg.obj);
                 break;
 
+            case EVENT_SIM_UNKNOWN:
+                updateCarrierServices(msg.arg1, IccCardConstants.INTENT_VALUE_ICC_UNKNOWN);
+                break;
+
+            case EVENT_SIM_IO_ERROR:
+                updateCarrierServices(msg.arg1, IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR);
+                break;
+
             default:
                 logd("Unknown msg:" + msg.what);
         }
@@ -271,6 +345,8 @@
                                 new QueryIccIdUserObj(reason, slotId)));
             } else {
                 logd("NOT Querying IccId its already set sIccid[" + slotId + "]=" + iccId);
+                updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_LOCKED);
+                broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_LOCKED, reason);
             }
         } else {
             logd("sFh[" + slotId + "] is null, ignore");
@@ -373,7 +449,19 @@
             logd("Invalid subId, could not update ContentResolver");
         }
 
+        // Update set of enabled carrier apps now that the privilege rules may have changed.
+        CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(),
+                mPackageManager, TelephonyManager.getDefault(), mCurrentlyActiveUserId);
+
         broadcastSimStateChanged(slotId, IccCardConstants.INTENT_VALUE_ICC_LOADED, null);
+        updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_LOADED);
+    }
+
+    private void updateCarrierServices(int slotId, String simState) {
+        CarrierConfigManager configManager = (CarrierConfigManager)
+                mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        configManager.updateConfigForPhoneId(slotId, simState);
+        mCarrierServiceBindHelper.updateForPhoneId(slotId, simState);
     }
 
     private void handleSimAbsent(int slotId) {
@@ -384,6 +472,7 @@
         if (isAllIccIdQueryDone()) {
             updateSubscriptionInfoByIccId();
         }
+        updateCarrierServices(slotId, IccCardConstants.INTENT_VALUE_ICC_ABSENT);
     }
 
     /**
@@ -428,7 +517,8 @@
         for (int i = 0; i < PROJECT_SIM_NUM; i++) {
             oldIccId[i] = null;
             List<SubscriptionInfo> oldSubInfo =
-                    SubscriptionController.getInstance().getSubInfoUsingSlotIdWithCheck(i, false);
+                    SubscriptionController.getInstance().getSubInfoUsingSlotIdWithCheck(i, false,
+                    mContext.getOpPackageName());
             if (oldSubInfo != null) {
                 oldIccId[i] = oldSubInfo.get(0).getIccId();
                 logd("updateSubscriptionInfoByIccId: oldSubId = "
@@ -523,6 +613,9 @@
             }
         }
 
+        // Ensure the modems are mapped correctly
+        mSubscriptionManager.setDefaultDataSubId(mSubscriptionManager.getDefaultDataSubId());
+
         SubscriptionController.getInstance().notifySubscriptionInfoChanged();
         logd("updateSubscriptionInfoByIccId:- SsubscriptionInfo update complete");
     }
@@ -567,4 +660,9 @@
     private void logd(String message) {
         Rlog.d(LOG_TAG, message);
     }
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("SubscriptionInfoUpdater:");
+        mCarrierServiceBindHelper.dump(fd, pw, args);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/UiccSmsController.java b/src/java/com/android/internal/telephony/UiccSmsController.java
index 5f86aa5..fecd851 100755
--- a/src/java/com/android/internal/telephony/UiccSmsController.java
+++ b/src/java/com/android/internal/telephony/UiccSmsController.java
@@ -18,6 +18,7 @@
 
 package com.android.internal.telephony;
 
+import android.annotation.Nullable;
 import android.app.ActivityThread;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -25,7 +26,9 @@
 import android.os.Binder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.provider.Telephony.Sms.Intents;
 import android.telephony.Rlog;
+import android.telephony.SmsManager;
 import android.telephony.SubscriptionInfo;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
@@ -49,13 +52,7 @@
         }
     }
 
-    public boolean
-    updateMessageOnIccEf(String callingPackage, int index, int status, byte[] pdu)
-            throws android.os.RemoteException {
-        return  updateMessageOnIccEfForSubscriber(getPreferredSmsSubscription(), callingPackage,
-                index, status, pdu);
-    }
-
+    @Override
     public boolean
     updateMessageOnIccEfForSubscriber(int subId, String callingPackage, int index, int status,
                 byte[] pdu) throws android.os.RemoteException {
@@ -63,53 +60,39 @@
         if (iccSmsIntMgr != null) {
             return iccSmsIntMgr.updateMessageOnIccEf(callingPackage, index, status, pdu);
         } else {
-            Rlog.e(LOG_TAG,"updateMessageOnIccEf iccSmsIntMgr is null" +
+            Rlog.e(LOG_TAG,"updateMessageOnIccEfForSubscriber iccSmsIntMgr is null" +
                           " for Subscription: " + subId);
             return false;
         }
     }
 
-    public boolean copyMessageToIccEf(String callingPackage, int status, byte[] pdu, byte[] smsc)
-            throws android.os.RemoteException {
-        return copyMessageToIccEfForSubscriber(getPreferredSmsSubscription(), callingPackage, status,
-                pdu, smsc);
-    }
-
+    @Override
     public boolean copyMessageToIccEfForSubscriber(int subId, String callingPackage, int status,
             byte[] pdu, byte[] smsc) throws android.os.RemoteException {
         IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
         if (iccSmsIntMgr != null) {
             return iccSmsIntMgr.copyMessageToIccEf(callingPackage, status, pdu, smsc);
         } else {
-            Rlog.e(LOG_TAG,"copyMessageToIccEf iccSmsIntMgr is null" +
+            Rlog.e(LOG_TAG,"copyMessageToIccEfForSubscriber iccSmsIntMgr is null" +
                           " for Subscription: " + subId);
             return false;
         }
     }
 
-    public List<SmsRawData> getAllMessagesFromIccEf(String callingPackage)
-            throws android.os.RemoteException {
-        return getAllMessagesFromIccEfForSubscriber(getPreferredSmsSubscription(), callingPackage);
-    }
-
+    @Override
     public List<SmsRawData> getAllMessagesFromIccEfForSubscriber(int subId, String callingPackage)
                 throws android.os.RemoteException {
         IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
         if (iccSmsIntMgr != null) {
             return iccSmsIntMgr.getAllMessagesFromIccEf(callingPackage);
         } else {
-            Rlog.e(LOG_TAG,"getAllMessagesFromIccEf iccSmsIntMgr is" +
+            Rlog.e(LOG_TAG,"getAllMessagesFromIccEfForSubscriber iccSmsIntMgr is" +
                           " null for Subscription: " + subId);
             return null;
         }
     }
 
-    public void sendData(String callingPackage, String destAddr, String scAddr, int destPort,
-            byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
-         sendDataForSubscriber(getPreferredSmsSubscription(), callingPackage, destAddr, scAddr,
-                 destPort, data, sentIntent, deliveryIntent);
-    }
-
+    @Override
     public void sendDataForSubscriber(int subId, String callingPackage, String destAddr,
             String scAddr, int destPort, byte[] data, PendingIntent sentIntent,
             PendingIntent deliveryIntent) {
@@ -118,6 +101,21 @@
             iccSmsIntMgr.sendData(callingPackage, destAddr, scAddr, destPort, data,
                     sentIntent, deliveryIntent);
         } else {
+            Rlog.e(LOG_TAG,"sendDataForSubscriber iccSmsIntMgr is null for" +
+                          " Subscription: " + subId);
+            // TODO: Use a more specific error code to replace RESULT_ERROR_GENERIC_FAILURE.
+            sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_GENERIC_FAILURE);
+        }
+    }
+
+    public void sendDataForSubscriberWithSelfPermissions(int subId, String callingPackage,
+            String destAddr, String scAddr, int destPort, byte[] data, PendingIntent sentIntent,
+            PendingIntent deliveryIntent) {
+        IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
+        if (iccSmsIntMgr != null) {
+            iccSmsIntMgr.sendDataWithSelfPermissions(callingPackage, destAddr, scAddr, destPort, data,
+                    sentIntent, deliveryIntent);
+        } else {
             Rlog.e(LOG_TAG,"sendText iccSmsIntMgr is null for" +
                           " Subscription: " + subId);
         }
@@ -126,15 +124,31 @@
     public void sendText(String callingPackage, String destAddr, String scAddr,
             String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
         sendTextForSubscriber(getPreferredSmsSubscription(), callingPackage, destAddr, scAddr,
-            text, sentIntent, deliveryIntent);
+            text, sentIntent, deliveryIntent, true /* persistMessageForNonDefaultSmsApp*/);
     }
 
+    @Override
     public void sendTextForSubscriber(int subId, String callingPackage, String destAddr,
-            String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent) {
+            String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent,
+            boolean persistMessageForNonDefaultSmsApp) {
         IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
         if (iccSmsIntMgr != null) {
             iccSmsIntMgr.sendText(callingPackage, destAddr, scAddr, text, sentIntent,
-                    deliveryIntent);
+                    deliveryIntent, persistMessageForNonDefaultSmsApp);
+        } else {
+            Rlog.e(LOG_TAG,"sendTextForSubscriber iccSmsIntMgr is null for" +
+                          " Subscription: " + subId);
+            sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_GENERIC_FAILURE);
+        }
+    }
+
+    public void sendTextForSubscriberWithSelfPermissions(int subId, String callingPackage,
+            String destAddr, String scAddr, String text, PendingIntent sentIntent,
+            PendingIntent deliveryIntent) {
+        IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
+        if (iccSmsIntMgr != null) {
+            iccSmsIntMgr.sendTextWithSelfPermissions(callingPackage, destAddr, scAddr, text,
+                    sentIntent, deliveryIntent);
         } else {
             Rlog.e(LOG_TAG,"sendText iccSmsIntMgr is null for" +
                           " Subscription: " + subId);
@@ -145,83 +159,67 @@
             List<String> parts, List<PendingIntent> sentIntents,
             List<PendingIntent> deliveryIntents) throws android.os.RemoteException {
          sendMultipartTextForSubscriber(getPreferredSmsSubscription(), callingPackage, destAddr,
-                 scAddr, parts, sentIntents, deliveryIntents);
+                 scAddr, parts, sentIntents, deliveryIntents,
+                 true /* persistMessageForNonDefaultSmsApp */);
     }
 
+    @Override
     public void sendMultipartTextForSubscriber(int subId, String callingPackage, String destAddr,
             String scAddr, List<String> parts, List<PendingIntent> sentIntents,
-            List<PendingIntent> deliveryIntents)
+            List<PendingIntent> deliveryIntents, boolean persistMessageForNonDefaultSmsApp)
             throws android.os.RemoteException {
         IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
         if (iccSmsIntMgr != null ) {
             iccSmsIntMgr.sendMultipartText(callingPackage, destAddr, scAddr, parts, sentIntents,
-                    deliveryIntents);
+                    deliveryIntents, persistMessageForNonDefaultSmsApp);
         } else {
-            Rlog.e(LOG_TAG,"sendMultipartText iccSmsIntMgr is null for" +
+            Rlog.e(LOG_TAG,"sendMultipartTextForSubscriber iccSmsIntMgr is null for" +
                           " Subscription: " + subId);
+            sendErrorInPendingIntents(sentIntents, SmsManager.RESULT_ERROR_GENERIC_FAILURE);
         }
     }
 
-    public boolean enableCellBroadcast(int messageIdentifier, int ranType)
-            throws android.os.RemoteException {
-        return enableCellBroadcastForSubscriber(getPreferredSmsSubscription(), messageIdentifier,
-                ranType);
-    }
-
+    @Override
     public boolean enableCellBroadcastForSubscriber(int subId, int messageIdentifier, int ranType)
                 throws android.os.RemoteException {
         return enableCellBroadcastRangeForSubscriber(subId, messageIdentifier, messageIdentifier,
                 ranType);
     }
 
-    public boolean enableCellBroadcastRange(int startMessageId, int endMessageId, int ranType)
-            throws android.os.RemoteException {
-        return enableCellBroadcastRangeForSubscriber(getPreferredSmsSubscription(), startMessageId,
-                endMessageId, ranType);
-    }
-
+    @Override
     public boolean enableCellBroadcastRangeForSubscriber(int subId, int startMessageId,
             int endMessageId, int ranType) throws android.os.RemoteException {
         IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
         if (iccSmsIntMgr != null ) {
             return iccSmsIntMgr.enableCellBroadcastRange(startMessageId, endMessageId, ranType);
         } else {
-            Rlog.e(LOG_TAG,"enableCellBroadcast iccSmsIntMgr is null for" +
+            Rlog.e(LOG_TAG,"enableCellBroadcastRangeForSubscriber iccSmsIntMgr is null for" +
                           " Subscription: " + subId);
         }
         return false;
     }
 
-    public boolean disableCellBroadcast(int messageIdentifier, int ranType)
-            throws android.os.RemoteException {
-        return disableCellBroadcastForSubscriber(getPreferredSmsSubscription(), messageIdentifier,
-                ranType);
-    }
-
+    @Override
     public boolean disableCellBroadcastForSubscriber(int subId, int messageIdentifier, int ranType)
                 throws android.os.RemoteException {
         return disableCellBroadcastRangeForSubscriber(subId, messageIdentifier, messageIdentifier,
                 ranType);
     }
 
-    public boolean disableCellBroadcastRange(int startMessageId, int endMessageId, int ranType)
-            throws android.os.RemoteException {
-        return disableCellBroadcastRangeForSubscriber(getPreferredSmsSubscription(), startMessageId,
-                endMessageId, ranType);
-    }
-
+    @Override
     public boolean disableCellBroadcastRangeForSubscriber(int subId, int startMessageId,
             int endMessageId, int ranType) throws android.os.RemoteException {
         IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
         if (iccSmsIntMgr != null ) {
             return iccSmsIntMgr.disableCellBroadcastRange(startMessageId, endMessageId, ranType);
         } else {
-            Rlog.e(LOG_TAG,"disableCellBroadcast iccSmsIntMgr is null for" +
+            Rlog.e(LOG_TAG,"disableCellBroadcastRangeForSubscriber iccSmsIntMgr is null for" +
                           " Subscription:"+subId);
         }
        return false;
     }
 
+    @Override
     public int getPremiumSmsPermission(String packageName) {
         return getPremiumSmsPermissionForSubscriber(getPreferredSmsSubscription(), packageName);
     }
@@ -232,12 +230,13 @@
         if (iccSmsIntMgr != null ) {
             return iccSmsIntMgr.getPremiumSmsPermission(packageName);
         } else {
-            Rlog.e(LOG_TAG, "getPremiumSmsPermission iccSmsIntMgr is null");
+            Rlog.e(LOG_TAG, "getPremiumSmsPermissionForSubscriber iccSmsIntMgr is null");
         }
         //TODO Rakesh
         return 0;
     }
 
+    @Override
     public void setPremiumSmsPermission(String packageName, int permission) {
          setPremiumSmsPermissionForSubscriber(getPreferredSmsSubscription(), packageName, permission);
     }
@@ -248,21 +247,17 @@
         if (iccSmsIntMgr != null ) {
             iccSmsIntMgr.setPremiumSmsPermission(packageName, permission);
         } else {
-            Rlog.e(LOG_TAG, "setPremiumSmsPermission iccSmsIntMgr is null");
+            Rlog.e(LOG_TAG, "setPremiumSmsPermissionForSubscriber iccSmsIntMgr is null");
         }
     }
 
-    public boolean isImsSmsSupported() {
-        return isImsSmsSupportedForSubscriber(getPreferredSmsSubscription());
-    }
-
     @Override
     public boolean isImsSmsSupportedForSubscriber(int subId) {
         IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
         if (iccSmsIntMgr != null ) {
             return iccSmsIntMgr.isImsSmsSupported();
         } else {
-            Rlog.e(LOG_TAG, "isImsSmsSupported iccSmsIntMgr is null");
+            Rlog.e(LOG_TAG, "isImsSmsSupportedForSubscriber iccSmsIntMgr is null");
         }
         return false;
     }
@@ -300,35 +295,39 @@
         return false;
     }
 
-    public String getImsSmsFormat() {
-        return getImsSmsFormatForSubscriber(getPreferredSmsSubscription());
-    }
-
     @Override
     public String getImsSmsFormatForSubscriber(int subId) {
-       IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
+        IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
         if (iccSmsIntMgr != null ) {
             return iccSmsIntMgr.getImsSmsFormat();
         } else {
-            Rlog.e(LOG_TAG, "getImsSmsFormat iccSmsIntMgr is null");
+            Rlog.e(LOG_TAG, "getImsSmsFormatForSubscriber iccSmsIntMgr is null");
         }
         return null;
     }
 
     @Override
-    public void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) {
-        injectSmsPdu(SubscriptionManager.getDefaultSmsSubId(), pdu, format, receivedIntent);
-    }
-
-    // FIXME: Add injectSmsPdu to ISms.aidl
-    public void injectSmsPdu(int subId, byte[] pdu, String format, PendingIntent receivedIntent) {
-        getIccSmsInterfaceManager(subId).injectSmsPdu(pdu, format, receivedIntent);
+    public void injectSmsPduForSubscriber(
+            int subId, byte[] pdu, String format, PendingIntent receivedIntent) {
+        IccSmsInterfaceManager iccSmsIntMgr = getIccSmsInterfaceManager(subId);
+        if (iccSmsIntMgr != null) {
+            iccSmsIntMgr.injectSmsPdu(pdu, format, receivedIntent);
+        } else {
+            Rlog.e(LOG_TAG, "injectSmsPduForSubscriber iccSmsIntMgr is null");
+            // RESULT_SMS_GENERIC_ERROR is documented for injectSmsPdu
+            sendErrorInPendingIntent(receivedIntent, Intents.RESULT_SMS_GENERIC_ERROR);
+        }
     }
 
     /**
      * get sms interface manager object based on subscription.
      **/
-    private IccSmsInterfaceManager getIccSmsInterfaceManager(int subId) {
+    private @Nullable IccSmsInterfaceManager getIccSmsInterfaceManager(int subId) {
+        if (!isActiveSubId(subId)) {
+            Rlog.e(LOG_TAG, "Subscription " + subId + " is inactive.");
+            return null;
+        }
+
         int phoneId = SubscriptionController.getInstance().getPhoneId(subId) ;
         //Fixme: for multi-subscription case
         if (!SubscriptionManager.isValidPhoneId(phoneId)
@@ -341,24 +340,26 @@
                 ((PhoneProxy)mPhone[(int)phoneId]).getIccSmsInterfaceManager();
         } catch (NullPointerException e) {
             Rlog.e(LOG_TAG, "Exception is :"+e.toString()+" For subscription :"+subId );
-            e.printStackTrace(); //This will print stact trace
+            e.printStackTrace();
             return null;
         } catch (ArrayIndexOutOfBoundsException e) {
             Rlog.e(LOG_TAG, "Exception is :"+e.toString()+" For subscription :"+subId );
-            e.printStackTrace(); //This will print stack trace
+            e.printStackTrace();
             return null;
         }
     }
 
     /**
        Gets User preferred SMS subscription */
+    @Override
     public int getPreferredSmsSubscription() {
-        return  SubscriptionController.getInstance().getDefaultSmsSubId();
+        return SubscriptionController.getInstance().getDefaultSmsSubId();
     }
 
     /**
      * Get SMS prompt property,  enabled or not
      **/
+    @Override
     public boolean isSMSPromptEnabled() {
         return PhoneFactory.isSMSPromptEnabled();
     }
@@ -372,6 +373,7 @@
                     deliveryIntent);
         } else {
             Rlog.e(LOG_TAG,"sendStoredText iccSmsIntMgr is null for subscription: " + subId);
+            sendErrorInPendingIntent(sentIntent, SmsManager.RESULT_ERROR_GENERIC_FAILURE);
         }
     }
 
@@ -386,7 +388,29 @@
         } else {
             Rlog.e(LOG_TAG,"sendStoredMultipartText iccSmsIntMgr is null for subscription: "
                     + subId);
+            sendErrorInPendingIntents(sentIntents, SmsManager.RESULT_ERROR_GENERIC_FAILURE);
         }
     }
 
+    /*
+     * @return true if the subId is active.
+     */
+    private boolean isActiveSubId(int subId) {
+        return SubscriptionController.getInstance().isActiveSubId(subId);
+    }
+
+    private void sendErrorInPendingIntent(@Nullable PendingIntent intent, int errorCode) {
+        if (intent != null) {
+            try {
+                intent.send(errorCode);
+            } catch (PendingIntent.CanceledException ex) {
+            }
+        }
+    }
+
+    private void sendErrorInPendingIntents(List<PendingIntent> intents, int errorCode) {
+        for (PendingIntent intent : intents) {
+            sendErrorInPendingIntent(intent, errorCode);
+        }
+    }
 }
diff --git a/src/java/com/android/internal/telephony/WapPushOverSms.java b/src/java/com/android/internal/telephony/WapPushOverSms.java
index a89e2f0..8b9d629 100755
--- a/src/java/com/android/internal/telephony/WapPushOverSms.java
+++ b/src/java/com/android/internal/telephony/WapPushOverSms.java
@@ -21,6 +21,7 @@
 import static com.google.android.mms.pdu.PduHeaders.MESSAGE_TYPE_READ_ORIG_IND;
 import android.app.Activity;
 import android.app.AppOpsManager;
+import android.app.BroadcastOptions;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentValues;
@@ -34,7 +35,9 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.IDeviceIdleController;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.provider.Telephony;
 import android.provider.Telephony.Sms.Intents;
@@ -60,9 +63,12 @@
  */
 public class WapPushOverSms implements ServiceConnection {
     private static final String TAG = "WAP PUSH";
-    private static final boolean DBG = true;
+    private static final boolean DBG = false;
 
     private final Context mContext;
+    IDeviceIdleController mDeviceIdleController;
+
+    private String mWapPushManagerPackage;
 
     /** Assigned from ServiceConnection callback on main threaad. */
     private volatile IWapPushManager mWapPushManager;
@@ -81,12 +87,15 @@
 
     public WapPushOverSms(Context context) {
         mContext = context;
+        mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
+                ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
         Intent intent = new Intent(IWapPushManager.class.getName());
         ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0);
         intent.setComponent(comp);
         if (comp == null || !context.bindService(intent, this, Context.BIND_AUTO_CREATE)) {
             Rlog.e(TAG, "bindService() for wappush manager failed");
         } else {
+            mWapPushManagerPackage = comp.getPackageName();
             if (DBG) Rlog.v(TAG, "bindService() for wappush manager succeeded");
         }
     }
@@ -230,6 +239,9 @@
                     if (wapPushMan == null) {
                         if (DBG) Rlog.w(TAG, "wap push manager not found!");
                     } else {
+                        mDeviceIdleController.addPowerSaveTempWhitelistAppForMms(
+                                mWapPushManagerPackage, 0, "mms-mgr");
+
                         Intent intent = new Intent();
                         intent.putExtra("transactionId", transactionId);
                         intent.putExtra("pduType", pduType);
@@ -283,14 +295,23 @@
             // Direct the intent to only the default MMS app. If we can't find a default MMS app
             // then sent it to all broadcast receivers.
             ComponentName componentName = SmsApplication.getDefaultMmsApplication(mContext, true);
+            Bundle options = null;
             if (componentName != null) {
                 // Deliver MMS message only to this receiver
                 intent.setComponent(componentName);
                 if (DBG) Rlog.v(TAG, "Delivering MMS to: " + componentName.getPackageName() +
                         " " + componentName.getClassName());
+                try {
+                    long duration = mDeviceIdleController.addPowerSaveTempWhitelistAppForMms(
+                            componentName.getPackageName(), 0, "mms-app");
+                    BroadcastOptions bopts = BroadcastOptions.makeBasic();
+                    bopts.setTemporaryAppWhitelistDuration(duration);
+                    options = bopts.toBundle();
+                } catch (RemoteException e) {
+                }
             }
 
-            handler.dispatchIntent(intent, permission, appOp, receiver, UserHandle.OWNER);
+            handler.dispatchIntent(intent, permission, appOp, options, receiver, UserHandle.OWNER);
             return Activity.RESULT_OK;
         } catch (ArrayIndexOutOfBoundsException aie) {
             // 0-byte WAP PDU or other unexpected WAP PDU contents can easily throw this;
diff --git a/src/java/com/android/internal/telephony/cat/CatService.java b/src/java/com/android/internal/telephony/cat/CatService.java
index 098b481..c4f1dfc 100644
--- a/src/java/com/android/internal/telephony/cat/CatService.java
+++ b/src/java/com/android/internal/telephony/cat/CatService.java
@@ -961,6 +961,7 @@
         case PRFRMD_WITH_MODIFICATION:
         case PRFRMD_NAA_NOT_ACTIVE:
         case PRFRMD_TONE_NOT_PLAYED:
+        case LAUNCH_BROWSER_ERROR:
         case TERMINAL_CRNTLY_UNABLE_TO_PROCESS:
             switch (type) {
             case SET_UP_MENU:
diff --git a/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java b/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
index 882741f..ad84a82 100644
--- a/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
+++ b/src/java/com/android/internal/telephony/cat/CommandParamsFactory.java
@@ -65,6 +65,22 @@
     static final int DTTZ_SETTING                           = 0x03;
     static final int LANGUAGE_SETTING                       = 0x04;
 
+    // As per TS 102.223 Annex C, Structure of CAT communications,
+    // the APDU length can be max 255 bytes. This leaves only 239 bytes for user
+    // input string. CMD details TLV + Device IDs TLV + Result TLV + Other
+    // details of TextString TLV not including user input take 16 bytes.
+    //
+    // If UCS2 encoding is used, maximum 118 UCS2 chars can be encoded in 238 bytes.
+    // Each UCS2 char takes 2 bytes. Byte Order Mask(BOM), 0xFEFF takes 2 bytes.
+    //
+    // If GSM 7 bit default(use 8 bits to represent a 7 bit char) format is used,
+    // maximum 239 chars can be encoded in 239 bytes since each char takes 1 byte.
+    //
+    // No issues for GSM 7 bit packed format encoding.
+
+    private static final int MAX_GSM7_DEFAULT_CHARS = 239;
+    private static final int MAX_UCS2_CHARS = 118;
+
     static synchronized CommandParamsFactory getInstance(RilMessageDecoder caller,
             IccFileHandler fh) {
         if (sInstance != null) {
@@ -499,6 +515,18 @@
         input.packed = (cmdDet.commandQualifier & 0x08) != 0;
         input.helpAvailable = (cmdDet.commandQualifier & 0x80) != 0;
 
+        // Truncate the maxLen if it exceeds the max number of chars that can
+        // be encoded. Limit depends on DCS in Command Qualifier.
+        if (input.ucs2 && input.maxLen > MAX_UCS2_CHARS) {
+            CatLog.d(this, "UCS2: received maxLen = " + input.maxLen +
+                  ", truncating to " + MAX_UCS2_CHARS);
+            input.maxLen = MAX_UCS2_CHARS;
+        } else if (!input.packed && input.maxLen > MAX_GSM7_DEFAULT_CHARS) {
+            CatLog.d(this, "GSM 7Bit Default: received maxLen = " + input.maxLen +
+                  ", truncating to " + MAX_GSM7_DEFAULT_CHARS);
+            input.maxLen = MAX_GSM7_DEFAULT_CHARS;
+        }
+
         mCmdParams = new GetInputParams(cmdDet, input);
 
         if (iconId != null) {
diff --git a/src/java/com/android/internal/telephony/cdma/CDMALTEPhone.java b/src/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
index 21cabf6..f3f04ae 100644
--- a/src/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
+++ b/src/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
@@ -237,6 +237,12 @@
         return (mSimRecords != null) ? mSimRecords.getGid1() : "";
     }
 
+    // return GID2 from USIM
+    @Override
+    public String getGroupIdLevel2() {
+        return (mSimRecords != null) ? mSimRecords.getGid2() : "";
+    }
+
     @Override
     public String getImei() {
         return mImei;
diff --git a/src/java/com/android/internal/telephony/cdma/CDMAPhone.java b/src/java/com/android/internal/telephony/cdma/CDMAPhone.java
index 0fdd3db..6bed5ac 100644
--- a/src/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/src/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -24,6 +24,7 @@
 import android.database.SQLException;
 import android.net.Uri;
 import android.os.AsyncResult;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
 import android.os.PowerManager;
@@ -70,7 +71,6 @@
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.uicc.IccException;
 import com.android.internal.telephony.uicc.IccRecords;
-import com.android.internal.telephony.uicc.RuimRecords;
 import com.android.internal.telephony.uicc.UiccCard;
 import com.android.internal.telephony.uicc.UiccCardApplication;
 import com.android.internal.telephony.uicc.UiccController;
@@ -407,33 +407,60 @@
     @Override
     public Connection
     dial (String dialString, int videoState) throws CallStateException {
-        ImsPhone imsPhone = mImsPhone;
+        return dial(dialString, null, videoState, null);
+    }
 
-        boolean imsUseEnabled =
-                ImsManager.isVolteEnabledByPlatform(mContext) &&
-                ImsManager.isEnhanced4gLteModeSettingEnabledByUser(mContext) &&
-                ImsManager.isNonTtyOrTtyOnVolteEnabled(mContext);
-        if (!imsUseEnabled) {
-            Rlog.w(LOG_TAG, "IMS is disabled: forced to CS");
+
+    @Override
+    protected Connection
+    dialInternal (String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
+            throws CallStateException {
+        // Need to make sure dialString gets parsed properly
+        String newDialString = PhoneNumberUtils.stripSeparators(dialString);
+        return mCT.dial(newDialString);
+    }
+
+    @Override
+    public Connection dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
+            throws CallStateException {
+        if (uusInfo != null) {
+            throw new CallStateException("Sending UUS information NOT supported in CDMA!");
         }
 
+        boolean isEmergency = PhoneNumberUtils.isEmergencyNumber(dialString);
+        ImsPhone imsPhone = mImsPhone;
+
+        boolean imsUseEnabled = isImsUseEnabled()
+                 && imsPhone != null
+                 && (imsPhone.isVolteEnabled() || imsPhone.isVowifiEnabled())
+                 && (imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE);
+
+        boolean useImsForEmergency = ImsManager.isVolteEnabledByPlatform(mContext)
+                && imsPhone != null
+                && isEmergency
+                &&  mContext.getResources().getBoolean(
+                        com.android.internal.R.bool.useImsAlwaysForEmergencyCall)
+                && ImsManager.isNonTtyOrTtyOnVolteEnabled(mContext)
+                && (imsPhone.getServiceState().getState() != ServiceState.STATE_POWER_OFF);
+
         if (DBG) {
-            Rlog.d(LOG_TAG, "imsUseEnabled=" + imsUseEnabled + ", imsPhone=" + imsPhone
+            Rlog.d(LOG_TAG, "imsUseEnabled=" + imsUseEnabled
+                    + ", useImsForEmergency=" + useImsForEmergency
+                    + ", imsPhone=" + imsPhone
                     + ", imsPhone.isVolteEnabled()="
                     + ((imsPhone != null) ? imsPhone.isVolteEnabled() : "N/A")
+                    + ", imsPhone.isVowifiEnabled()="
+                    + ((imsPhone != null) ? imsPhone.isVowifiEnabled() : "N/A")
                     + ", imsPhone.getServiceState().getState()="
                     + ((imsPhone != null) ? imsPhone.getServiceState().getState() : "N/A"));
         }
 
-        if (imsUseEnabled && imsPhone != null && imsPhone.isVolteEnabled()
-                && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE
-                && !PhoneNumberUtils.isEmergencyNumber(dialString))
-                || (PhoneNumberUtils.isEmergencyNumber(dialString)
-                && mContext.getResources().getBoolean(
-                        com.android.internal.R.bool.useImsAlwaysForEmergencyCall))) ) {
+        ImsPhone.checkWfcWifiOnlyModeBeforeDial(mImsPhone, mContext);
+
+        if (imsUseEnabled || useImsForEmergency) {
             try {
                 if (DBG) Rlog.d(LOG_TAG, "Trying IMS PS call");
-                return imsPhone.dial(dialString, videoState);
+                return imsPhone.dial(dialString, uusInfo, videoState, intentExtras);
             } catch (CallStateException e) {
                 if (DBG) Rlog.d(LOG_TAG, "IMS PS call exception " + e +
                         "imsUseEnabled =" + imsUseEnabled + ", imsPhone =" + imsPhone);
@@ -445,24 +472,12 @@
             }
         }
 
+        if (mSST != null && mSST.mSS.getState() == ServiceState.STATE_OUT_OF_SERVICE
+                && mSST.mSS.getDataRegState() != ServiceState.STATE_IN_SERVICE && !isEmergency) {
+            throw new CallStateException("cannot dial in current state");
+        }
         if (DBG) Rlog.d(LOG_TAG, "Trying (non-IMS) CS call");
-        return dialInternal(dialString, null, videoState);
-    }
-
-
-    @Override
-    protected Connection
-    dialInternal (String dialString, UUSInfo uusInfo,
-            int videoState) throws CallStateException {
-        // Need to make sure dialString gets parsed properly
-        String newDialString = PhoneNumberUtils.stripSeparators(dialString);
-        return mCT.dial(newDialString);
-    }
-
-    @Override
-    public Connection dial(String dialString, UUSInfo uusInfo, int videoState)
-            throws CallStateException {
-        throw new CallStateException("Sending UUS information NOT supported in CDMA!");
+        return dialInternal(dialString, null, videoState, intentExtras);
     }
 
     @Override
@@ -614,6 +629,12 @@
     }
 
     @Override
+    public String getGroupIdLevel2() {
+        Rlog.e(LOG_TAG, "GID2 is not available in CDMA");
+        return null;
+    }
+
+    @Override
     public String getImei() {
         Rlog.e(LOG_TAG, "getImei() called for CDMAPhone");
         return mImei;
@@ -1123,9 +1144,14 @@
             sendEmergencyCallbackModeChange();
             // Re-initiate data connection
             mDcTracker.setInternalDataEnabled(true);
+            notifyEmergencyCallRegistrants(false);
         }
     }
 
+    protected void notifyEmergencyCallRegistrants(boolean started) {
+        mEmergencyCallToggledRegistrants.notifyResult(started ? 1 : 0);
+    }
+
     /**
      * Handle to cancel or restart Ecm timer in emergency call back mode
      * if action is CANCEL_ECM_TIMER, cancel Ecm timer and notify apps the timer is canceled;
@@ -1195,6 +1221,7 @@
 
                 mCi.getDeviceIdentity(obtainMessage(EVENT_GET_DEVICE_IDENTITY_DONE));
                 mCi.getRadioCapability(obtainMessage(EVENT_GET_RADIO_CAPABILITY));
+                startLceAfterRadioIsAvailable();
             }
             break;
 
@@ -1257,6 +1284,9 @@
             case EVENT_RADIO_ON:{
                 Rlog.d(LOG_TAG, "Event EVENT_RADIO_ON Received");
                 handleCdmaSubscriptionSource(mCdmaSSM.getCdmaSubscriptionSource());
+                // If this is on APM off, SIM may already be loaded. Send setPreferredNetworkType
+                // request to RIL to preserve user setting across APM toggling
+                setPreferredNetworkTypeIfSimLoaded();
             }
             break;
 
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaCallTracker.java b/src/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
index 59115f5..3903a2f 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaCallTracker.java
@@ -36,6 +36,7 @@
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.DriverCall;
+import com.android.internal.telephony.LastCallFailCause;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneBase;
 import com.android.internal.telephony.PhoneConstants;
@@ -687,6 +688,7 @@
             if( mPendingCallInEcm) {
                 mPendingCallInEcm = false;
             }
+            checkAndEnableDataCallAfterEmergencyCallDropped();
         }
 
         if (newRinging != null) {
@@ -1006,6 +1008,7 @@
 
             case EVENT_GET_LAST_CALL_FAIL_CAUSE:
                 int causeCode;
+                String vendorCause = null;
                 ar = (AsyncResult)msg.obj;
 
                 operationComplete();
@@ -1017,7 +1020,9 @@
                     Rlog.i(LOG_TAG,
                             "Exception during getLastCallFailCause, assuming normal disconnect");
                 } else {
-                    causeCode = ((int[])ar.result)[0];
+                    LastCallFailCause failCause = (LastCallFailCause)ar.result;
+                    causeCode = failCause.causeCode;
+                    vendorCause = failCause.vendorCause;
                 }
 
                 for (int i = 0, s =  mDroppedDuringPoll.size()
@@ -1025,7 +1030,7 @@
                 ) {
                     CdmaConnection conn = mDroppedDuringPoll.get(i);
 
-                    conn.onRemoteDisconnect(causeCode);
+                    conn.onRemoteDisconnect(causeCode, vendorCause);
                 }
 
                 updatePhoneState();
@@ -1118,6 +1123,7 @@
             if (Phone.DEBUG_PHONE) log("disableDataCallInEmergencyCall");
             mIsInEmergencyCall = true;
             mPhone.mDcTracker.setInternalDataEnabled(false);
+            mPhone.notifyEmergencyCallRegistrants(true);
         }
     }
 
@@ -1135,6 +1141,7 @@
             if (inEcm.compareTo("false") == 0) {
                 // Re-initiate data connection
                 mPhone.mDcTracker.setInternalDataEnabled(true);
+                mPhone.notifyEmergencyCallRegistrants(false);
             }
         }
     }
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaConnection.java b/src/java/com/android/internal/telephony/cdma/CdmaConnection.java
index 57389b4..9ad3a99 100755
--- a/src/java/com/android/internal/telephony/cdma/CdmaConnection.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaConnection.java
@@ -64,6 +64,7 @@
     int mCause = DisconnectCause.NOT_DISCONNECTED;
     PostDialState mPostDialState = PostDialState.NOT_STARTED;
     int mPreciseCause = 0;
+    String mVendorCause;
 
     Handler mHandler;
 
@@ -136,9 +137,10 @@
         mHandler = new MyHandler(mOwner.getLooper());
 
         mDialString = dialString;
-        Rlog.d(LOG_TAG, "[CDMAConn] CdmaConnection: dialString=" + dialString);
+        Rlog.d(LOG_TAG, "[CDMAConn] CdmaConnection: dialString=" + maskDialString(dialString));
         dialString = formatDialString(dialString);
-        Rlog.d(LOG_TAG, "[CDMAConn] CdmaConnection:formated dialString=" + dialString);
+        Rlog.d(LOG_TAG,
+                "[CDMAConn] CdmaConnection:formated dialString=" + maskDialString(dialString));
 
         mAddress = PhoneNumberUtils.extractNetworkPortionAlt(dialString);
         mPostDialString = PhoneNumberUtils.extractPostDialPortion(dialString);
@@ -322,6 +324,7 @@
     onHangupLocal() {
         mCause = DisconnectCause.LOCAL;
         mPreciseCause = 0;
+        mVendorCause = null;
     }
 
     /**
@@ -399,8 +402,9 @@
     }
 
     /*package*/ void
-    onRemoteDisconnect(int causeCode) {
+    onRemoteDisconnect(int causeCode, String vendorCause) {
         this.mPreciseCause = causeCode;
+        this.mVendorCause = vendorCause;
         onDisconnect(disconnectCauseFromCode(causeCode));
     }
 
@@ -911,6 +915,14 @@
         Rlog.d(LOG_TAG, "[CDMAConn] " + msg);
     }
 
+    private String maskDialString(String dialString) {
+        if (VDBG) {
+            return dialString;
+        }
+
+        return "<MASKED>";
+    }
+
     @Override
     public int getNumberPresentation() {
         return mNumberPresentation;
@@ -927,6 +939,11 @@
     }
 
     @Override
+    public String getVendorDisconnectCause() {
+        return mVendorCause;
+    }
+
+    @Override
     public Connection getOrigConnection() {
         return null;
     }
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaInformationRecords.java b/src/java/com/android/internal/telephony/cdma/CdmaInformationRecords.java
index 981dec6..7531bb9 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaInformationRecords.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaInformationRecords.java
@@ -167,7 +167,7 @@
         public String toString() {
             return "CdmaNumberInfoRec: {" +
                     " id: " + CdmaInformationRecords.idToString(id) +
-                    ", number: " + number +
+                    ", number: <MASKED>" +
                     ", numberType: " + numberType +
                     ", numberPlan: " + numberPlan +
                     ", pi: " + pi +
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java b/src/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
index fac1012b..18689e2 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
@@ -25,7 +25,10 @@
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.dataconnection.DcTrackerBase;
+import com.android.internal.telephony.PhoneConstants;
 
 import android.telephony.CellInfo;
 import android.telephony.CellInfoLte;
@@ -276,8 +279,10 @@
                 setSignalStrengthDefaultValues();
                 mGotCountryCode = false;
 
-                pollStateDone();
-                break;
+                if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+                        != mSS.getRilDataRadioTechnology()) {
+                    pollStateDone();
+                }
 
             default:
                 // Issue all poll-related commands at once, then count
@@ -303,22 +308,7 @@
 
     @Override
     protected void pollStateDone() {
-        log("pollStateDone: lte 1 ss=[" + mSS + "] newSS=[" + mNewSS + "]");
-
-        if (mPhone.isMccMncMarkedAsNonRoaming(mNewSS.getOperatorNumeric()) ||
-                mPhone.isSidMarkedAsNonRoaming(mNewSS.getSystemId())) {
-            log("pollStateDone: override - marked as non-roaming.");
-            mNewSS.setVoiceRoaming(false);
-            mNewSS.setDataRoaming(false);
-            mNewSS.setCdmaEriIconIndex(EriInfo.ROAMING_INDICATOR_OFF);
-        } else if (mPhone.isMccMncMarkedAsRoaming(mNewSS.getOperatorNumeric()) ||
-                mPhone.isSidMarkedAsRoaming(mNewSS.getSystemId())) {
-            log("pollStateDone: override - marked as roaming.");
-            mNewSS.setVoiceRoaming(true);
-            mNewSS.setDataRoaming(true);
-            mNewSS.setCdmaEriIconIndex(EriInfo.ROAMING_INDICATOR_ON);
-            mNewSS.setCdmaEriIconMode(EriInfo.ROAMING_ICON_MODE_NORMAL);
-        }
+        updateRoamingState();
 
         if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean(PROP_FORCE_ROAMING, false)) {
             mNewSS.setVoiceRoaming(true);
@@ -326,6 +316,8 @@
         }
 
         useDataRegStateForDataOnlyDevices();
+        resetServiceStateInIwlanMode();
+        log("pollStateDone: lte 1 ss=[" + mSS + "] newSS=[" + mNewSS + "]");
 
         boolean hasRegistered = mSS.getVoiceRegState() != ServiceState.STATE_IN_SERVICE
                 && mNewSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE;
@@ -427,7 +419,12 @@
 
         if (hasDataRadioTechnologyChanged) {
             tm.setDataNetworkTypeForPhone(mPhone.getPhoneId(), mSS.getRilDataRadioTechnology());
-        }
+        
+			if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+                        == mSS.getRilDataRadioTechnology()) {
+                log("pollStateDone: IWLAN enabled");
+            }
+		}
 
         if (hasRegistered) {
             mNetworkAttachedRegistrants.notifyRegistrants();
@@ -463,9 +460,11 @@
             }
 
             if (mUiccApplcation != null && mUiccApplcation.getState() == AppState.APPSTATE_READY &&
-                    mIccRecords != null && (mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE)) {
+                    mIccRecords != null && (mSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE)
+                    && mSS.getRilVoiceRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_LTE) {
                 // SIM is found on the device. If ERI roaming is OFF, and SID/NID matches
-                // one configured in SIM, use operator name from CSIM record.
+                // one configured in SIM, use operator name from CSIM record. Note that ERI, SID,
+                // and NID are CDMA only, not applicable to LTE.
                 boolean showSpn =
                     ((RuimRecords)mIccRecords).getCsimSpnDisplayCondition();
                 int iconIndex = mSS.getCdmaEriIconIndex();
@@ -538,7 +537,12 @@
 
         if ((hasCdmaDataConnectionChanged || hasDataRadioTechnologyChanged)) {
             notifyDataRegStateRilRadioTechnologyChanged();
-            mPhone.notifyDataConnection(null);
+            if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+                        == mSS.getRilDataRadioTechnology()) {
+                mPhone.notifyDataConnection(Phone.REASON_IWLAN_AVAILABLE);
+            } else {
+                mPhone.notifyDataConnection(null);
+            }
         }
 
         if (hasVoiceRoamingOn) {
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index f585c0f..8660d98 100755
--- a/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -109,32 +109,44 @@
             byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
         SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
                 scAddr, destAddr, destPort, data, (deliveryIntent != null));
-        HashMap map = getSmsTrackerMap(destAddr, scAddr, destPort, data, pdu);
-        SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
-                null /*messageUri*/, false /*isExpectMore*/, null /*fullMessageText*/,
-                false /*isText*/);
+        if (pdu != null) {
+            HashMap map = getSmsTrackerMap(destAddr, scAddr, destPort, data, pdu);
+            SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
+                    null /*messageUri*/, false /*isExpectMore*/, null /*fullMessageText*/,
+                    false /*isText*/, true /*persistMessage*/);
 
-        String carrierPackage = getCarrierAppPackageName();
-        if (carrierPackage != null) {
-            Rlog.d(TAG, "Found carrier package.");
-            DataSmsSender smsSender = new DataSmsSender(tracker);
-            smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender));
+            String carrierPackage = getCarrierAppPackageName();
+            if (carrierPackage != null) {
+                Rlog.d(TAG, "Found carrier package.");
+                DataSmsSender smsSender = new DataSmsSender(tracker);
+                smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender));
+            } else {
+                Rlog.v(TAG, "No carrier package.");
+                sendSubmitPdu(tracker);
+            }
         } else {
-            Rlog.v(TAG, "No carrier package.");
-            sendSubmitPdu(tracker);
+            Rlog.e(TAG, "CdmaSMSDispatcher.sendData(): getSubmitPdu() returned null");
+            if (sentIntent != null) {
+                try {
+                    sentIntent.send(SmsManager.RESULT_ERROR_GENERIC_FAILURE);
+                } catch (CanceledException ex) {
+                    Rlog.e(TAG, "Intent has been canceled!");
+                }
+            }
         }
     }
 
     /** {@inheritDoc} */
     @Override
     protected void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent,
-            PendingIntent deliveryIntent, Uri messageUri, String callingPkg) {
+            PendingIntent deliveryIntent, Uri messageUri, String callingPkg,
+            boolean persistMessage) {
         SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
                 scAddr, destAddr, text, (deliveryIntent != null), null);
         if (pdu != null) {
             HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu);
             SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
-                    messageUri, false /*isExpectMore*/, text, true /*isText*/);
+                    messageUri, false /*isExpectMore*/, text, true /*isText*/, persistMessage);
 
             String carrierPackage = getCarrierAppPackageName();
             if (carrierPackage != null) {
@@ -147,6 +159,13 @@
             }
         } else {
             Rlog.e(TAG, "CdmaSMSDispatcher.sendText(): getSubmitPdu() returned null");
+            if (sentIntent != null) {
+                try {
+                    sentIntent.send(SmsManager.RESULT_ERROR_GENERIC_FAILURE);
+                } catch (CanceledException ex) {
+                    Rlog.e(TAG, "Intent has been canceled!");
+                }
+            }
         }
     }
 
@@ -191,7 +210,8 @@
                 message, submitPdu);
         return getSmsTracker(map, sentIntent, deliveryIntent,
                 getFormat(), unsentPartCount, anyPartFailed, messageUri, smsHeader,
-                false /*isExpextMore*/, fullMessageText, true /*isText*/);
+                false /*isExpextMore*/, fullMessageText, true /*isText*/,
+                true /*persistMessage*/);
     }
 
     @Override
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/src/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
index 2ff9c51..d036165 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -25,9 +25,12 @@
 import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
+import android.os.PersistableBundle;
 import android.os.PowerManager;
 import android.os.Registrant;
 import android.os.RegistrantList;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -49,6 +52,7 @@
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.CommandsInterface.RadioState;
 import com.android.internal.telephony.EventLogTags;
+import com.android.internal.telephony.ICarrierConfigLoader;
 import com.android.internal.telephony.MccTable;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
@@ -568,7 +572,13 @@
         String plmn = mSS.getOperatorAlphaLong();
         boolean showPlmn = false;
 
-        if (!TextUtils.equals(plmn, mCurPlmn)) {
+        int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+        int[] subIds = SubscriptionManager.getSubId(mPhone.getPhoneId());
+        if (subIds != null && subIds.length > 0) {
+            subId = subIds[0];
+        }
+
+        if (mSubId != subId || !TextUtils.equals(plmn, mCurPlmn)) {
             // Allow A blank plmn, "" to set showPlmn to true. Previously, we
             // would set showPlmn to true only if plmn was not empty, i.e. was not
             // null and not blank. But this would cause us to incorrectly display
@@ -576,7 +586,7 @@
             showPlmn = plmn != null;
             if (DBG) {
                 log(String.format("updateSpnDisplay: changed sending intent" +
-                            " showPlmn='%b' plmn='%s'", showPlmn, plmn));
+                            " showPlmn='%b' plmn='%s' subId='%d'", showPlmn, plmn, subId));
             }
             Intent intent = new Intent(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
             intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
@@ -593,6 +603,7 @@
             }
         }
 
+        mSubId = subId;
         mCurShowSpn = false;
         mCurShowPlmn = showPlmn;
         mCurSpn = "";
@@ -815,12 +826,6 @@
                 return;
             }
 
-            if (!mCi.getRadioState().isOn()) {
-                // Radio has crashed or turned off.
-                cancelPollState();
-                return;
-            }
-
             if (err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW) {
                 loge("handlePollStateResult: RIL returned an error where it must succeed"
                         + ar.exception);
@@ -1022,8 +1027,10 @@
             setSignalStrengthDefaultValues();
             mGotCountryCode = false;
 
-            pollStateDone();
-            break;
+            if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+                        != mSS.getRilDataRadioTechnology()) {
+                pollStateDone();
+            }
 
         default:
             // Issue all poll-related commands at once, then count
@@ -1108,30 +1115,67 @@
         }
     }
 
-    protected void pollStateDone() {
-        if (DBG) log("pollStateDone: cdma oldSS=[" + mSS + "] newSS=[" + mNewSS + "]");
+    /**
+     * Query the carrier configuration to determine if there are any network overrides
+     * for roaming or not roaming for the current service state.
+     */
+    protected void updateRoamingState() {
+        // Save the roaming state before carrier config possibly overrides it.
+        mNewSS.setDataRoamingFromRegistration(mNewSS.getDataRoaming());
 
-        if (mPhone.isMccMncMarkedAsNonRoaming(mNewSS.getOperatorNumeric()) ||
-                mPhone.isSidMarkedAsNonRoaming(mNewSS.getSystemId())) {
-            log("pollStateDone: override - marked as non-roaming.");
-            mNewSS.setVoiceRoaming(false);
-            mNewSS.setDataRoaming(false);
-            mNewSS.setCdmaEriIconIndex(EriInfo.ROAMING_INDICATOR_OFF);
-        } else if (mPhone.isMccMncMarkedAsRoaming(mNewSS.getOperatorNumeric()) ||
-                mPhone.isSidMarkedAsRoaming(mNewSS.getSystemId())) {
-            log("pollStateDone: override - marked as roaming.");
-            mNewSS.setVoiceRoaming(true);
-            mNewSS.setDataRoaming(true);
-            mNewSS.setCdmaEriIconIndex(EriInfo.ROAMING_INDICATOR_ON);
-            mNewSS.setCdmaEriIconMode(EriInfo.ROAMING_ICON_MODE_NORMAL);
+        ICarrierConfigLoader configLoader =
+            (ICarrierConfigLoader) ServiceManager.getService(Context.CARRIER_CONFIG_SERVICE);
+        if (configLoader != null) {
+            try {
+                PersistableBundle b = configLoader.getConfigForSubId(mPhone.getSubId());
+                String systemId = Integer.toString(mNewSS.getSystemId());
+
+                if (alwaysOnHomeNetwork(b)) {
+                    log("updateRoamingState: carrier config override always on home network");
+                    setRoamingOff();
+                } else if (isNonRoamingInGsmNetwork(b, mNewSS.getOperatorNumeric())
+                        || isNonRoamingInCdmaNetwork(b, systemId)) {
+                    log("updateRoamingState: carrier config override set non-roaming:"
+                            + mNewSS.getOperatorNumeric() + ", " + systemId);
+                    setRoamingOff();
+                } else if (isRoamingInGsmNetwork(b, mNewSS.getOperatorNumeric())
+                        || isRoamingInCdmaNetwork(b, systemId)) {
+                    log("updateRoamingState: carrier config override set roaming:"
+                            + mNewSS.getOperatorNumeric() + ", " + systemId);
+                    setRoamingOn();
+                }
+            } catch (RemoteException e) {
+                loge("updateRoamingState: unable to access carrier config service");
+            }
+        } else {
+            log("updateRoamingState: no carrier config service available");
         }
 
         if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean(PROP_FORCE_ROAMING, false)) {
             mNewSS.setVoiceRoaming(true);
             mNewSS.setDataRoaming(true);
         }
+    }
+
+    private void setRoamingOn() {
+        mNewSS.setVoiceRoaming(true);
+        mNewSS.setDataRoaming(true);
+        mNewSS.setCdmaEriIconIndex(EriInfo.ROAMING_INDICATOR_ON);
+        mNewSS.setCdmaEriIconMode(EriInfo.ROAMING_ICON_MODE_NORMAL);
+    }
+
+    private void setRoamingOff() {
+        mNewSS.setVoiceRoaming(false);
+        mNewSS.setDataRoaming(false);
+        mNewSS.setCdmaEriIconIndex(EriInfo.ROAMING_INDICATOR_OFF);
+    }
+
+    protected void pollStateDone() {
+        updateRoamingState();
 
         useDataRegStateForDataOnlyDevices();
+        resetServiceStateInIwlanMode();
+        if (DBG) log("pollStateDone: cdma oldSS=[" + mSS + "] newSS=[" + mNewSS + "]");
 
         boolean hasRegistered =
             mSS.getVoiceRegState() != ServiceState.STATE_IN_SERVICE
@@ -1198,6 +1242,11 @@
 
         if (hasRilDataRadioTechnologyChanged) {
             tm.setDataNetworkTypeForPhone(mPhone.getPhoneId(), mSS.getRilDataRadioTechnology());
+
+            if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+                        == mSS.getRilDataRadioTechnology()) {
+                log("pollStateDone: IWLAN enabled");
+            }
         }
 
         if (hasRegistered) {
@@ -1283,7 +1332,12 @@
 
         if (hasCdmaDataConnectionChanged || hasRilDataRadioTechnologyChanged) {
             notifyDataRegStateRilRadioTechnologyChanged();
-            mPhone.notifyDataConnection(null);
+            if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+                        == mSS.getRilDataRadioTechnology()) {
+                mPhone.notifyDataConnection(Phone.REASON_IWLAN_AVAILABLE);
+            } else {
+                mPhone.notifyDataConnection(null);
+            }
         }
 
         if (hasVoiceRoamingOn) {
diff --git a/src/java/com/android/internal/telephony/cdma/sms/BearerData.java b/src/java/com/android/internal/telephony/cdma/sms/BearerData.java
index 05800b5..58c6e55 100644
--- a/src/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/src/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -24,9 +24,10 @@
 import android.telephony.Rlog;
 
 import com.android.internal.telephony.GsmAlphabet;
+import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
 import com.android.internal.telephony.SmsConstants;
 import com.android.internal.telephony.SmsHeader;
-import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
+import com.android.internal.telephony.SmsMessageBase;
 import com.android.internal.telephony.uicc.IccUtils;
 import com.android.internal.util.BitwiseInputStream;
 import com.android.internal.util.BitwiseOutputStream;
@@ -489,31 +490,7 @@
                     isEntireMsg) {
                 // We don't support single-segment EMS, so calculate for 16-bit
                 // TODO: Consider supporting single-segment EMS
-                ted.codeUnitCount = msg.length();
-                int octets = ted.codeUnitCount * 2;
-                if (octets > SmsConstants.MAX_USER_DATA_BYTES) {
-                    // If EMS is not supported, break down EMS into single segment SMS
-                    // and add page info " x/y".
-                    // In the case of UCS2 encoding type, we need 8 bytes for this
-                    // but we only have 6 bytes from UDH, so truncate the limit for
-                    // each segment by 2 bytes (1 char).
-                    int max_user_data_bytes_with_header =
-                            SmsConstants.MAX_USER_DATA_BYTES_WITH_HEADER;
-                    if (!android.telephony.SmsMessage.hasEmsSupport()) {
-                        // make sure total number of segments is less than 10
-                        if (octets <= 9 * (max_user_data_bytes_with_header - 2))
-                            max_user_data_bytes_with_header -= 2;
-                    }
-
-                    ted.msgCount = (octets + (max_user_data_bytes_with_header - 1)) /
-                            max_user_data_bytes_with_header;
-                    ted.codeUnitsRemaining = ((ted.msgCount *
-                            max_user_data_bytes_with_header) - octets) / 2;
-                } else {
-                    ted.msgCount = 1;
-                    ted.codeUnitsRemaining = (SmsConstants.MAX_USER_DATA_BYTES - octets)/2;
-                }
-                ted.codeUnitSize = SmsConstants.ENCODING_16BIT;
+                return SmsMessageBase.calcUnicodeEncodingDetails(msg);
             }
         }
         return ted;
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java b/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
index 2a1f1c5..081e9b7 100644
--- a/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
+++ b/src/java/com/android/internal/telephony/dataconnection/ApnContext.java
@@ -18,12 +18,17 @@
 
 import android.app.PendingIntent;
 import android.content.Context;
+import android.content.res.Resources;
 import android.net.NetworkConfig;
 import android.telephony.Rlog;
+import android.text.TextUtils;
+import android.util.LocalLog;
+import android.util.SparseIntArray;
 
 import com.android.internal.R;
 import com.android.internal.telephony.DctConstants;
 import com.android.internal.telephony.Phone;
+import com.android.internal.util.IndentingPrintWriter;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -118,6 +123,14 @@
         mDcAc = dcac;
     }
 
+    public synchronized void releaseDataConnection(String reason) {
+        if (mDcAc != null) {
+            mDcAc.tearDown(this, reason, null);
+            mDcAc = null;
+        }
+        setState(DctConstants.State.IDLE);
+    }
+
     public synchronized PendingIntent getReconnectIntent() {
         return mReconnectAlarmIntent;
     }
@@ -127,12 +140,12 @@
     }
 
     public synchronized ApnSetting getApnSetting() {
-        log("getApnSetting: apnSetting=" + mApnSetting);
+        if (DBG) log("getApnSetting: apnSetting=" + mApnSetting);
         return mApnSetting;
     }
 
     public synchronized void setApnSetting(ApnSetting apnSetting) {
-        log("setApnSetting: apnSetting=" + apnSetting);
+        if (DBG) log("setApnSetting: apnSetting=" + apnSetting);
         mApnSetting = apnSetting;
     }
 
@@ -262,43 +275,148 @@
     public boolean isProvisioningApn() {
         String provisioningApn = mContext.getResources()
                 .getString(R.string.mobile_provisioning_apn);
-        if ((mApnSetting != null) && (mApnSetting.apn != null)) {
+        if (!TextUtils.isEmpty(provisioningApn) &&
+                (mApnSetting != null) && (mApnSetting.apn != null)) {
             return (mApnSetting.apn.equals(provisioningApn));
         } else {
             return false;
         }
     }
 
-    public void incRefCount() {
+    private final ArrayList<LocalLog> mLocalLogs = new ArrayList<LocalLog>();
+
+    public void requestLog(String str) {
         synchronized (mRefCountLock) {
+            for (LocalLog l : mLocalLogs) {
+                l.log(str);
+            }
+        }
+    }
+
+    public void incRefCount(LocalLog log) {
+        synchronized (mRefCountLock) {
+            if (mRefCount == 0) {
+               // we wanted to leave the last in so it could actually capture the tear down
+               // of the network
+               requestLog("clearing log with size=" + mLocalLogs.size());
+               mLocalLogs.clear();
+            }
+            if (mLocalLogs.contains(log)) {
+                log.log("ApnContext.incRefCount has duplicate add - " + mRefCount);
+            } else {
+                mLocalLogs.add(log);
+                log.log("ApnContext.incRefCount - " + mRefCount);
+            }
             if (mRefCount++ == 0) {
                 mDcTracker.setEnabled(mDcTracker.apnTypeToId(mApnType), true);
             }
         }
     }
 
-    public void decRefCount() {
+    public void decRefCount(LocalLog log) {
         synchronized (mRefCountLock) {
+            // leave the last log alive to capture the actual tear down
+            if (mRefCount != 1) {
+                if (mLocalLogs.remove(log)) {
+                    log.log("ApnContext.decRefCount - " + mRefCount);
+                } else {
+                    log.log("ApnContext.decRefCount didn't find log - " + mRefCount);
+                }
+            } else {
+                log.log("ApnContext.decRefCount - 1");
+            }
             if (mRefCount-- == 1) {
                 mDcTracker.setEnabled(mDcTracker.apnTypeToId(mApnType), false);
             }
         }
     }
 
+    private final SparseIntArray mRetriesLeftPerErrorCode = new SparseIntArray();
+
+    public void resetErrorCodeRetries() {
+        requestLog("ApnContext.resetErrorCodeRetries");
+        if (DBG) log("ApnContext.resetErrorCodeRetries");
+
+        String[] config = Resources.getSystem().getStringArray(
+                com.android.internal.R.array.config_cell_retries_per_error_code);
+        synchronized (mRetriesLeftPerErrorCode) {
+            mRetriesLeftPerErrorCode.clear();
+
+            for (String c : config) {
+                String errorValue[] = c.split(",");
+                if (errorValue != null && errorValue.length == 2) {
+                    int count = 0;
+                    int errorCode = 0;
+                    try {
+                        errorCode = Integer.parseInt(errorValue[0]);
+                        count = Integer.parseInt(errorValue[1]);
+                    } catch (NumberFormatException e) {
+                        log("Exception parsing config_retries_per_error_code: " + e);
+                        continue;
+                    }
+                    if (count > 0 && errorCode > 0) {
+                        mRetriesLeftPerErrorCode.put(errorCode, count);
+                    }
+                } else {
+                    log("Exception parsing config_retries_per_error_code: " + c);
+                }
+            }
+        }
+    }
+
+    public boolean restartOnError(int errorCode) {
+        boolean result = false;
+        int retriesLeft = 0;
+        synchronized(mRetriesLeftPerErrorCode) {
+            retriesLeft = mRetriesLeftPerErrorCode.get(errorCode);
+            switch (retriesLeft) {
+                case 0: {
+                    // not set, never restart modem
+                    break;
+                }
+                case 1: {
+                    resetErrorCodeRetries();
+                    result = true;
+                    break;
+                }
+                default: {
+                    mRetriesLeftPerErrorCode.put(errorCode, retriesLeft - 1);
+                    result = false;
+                }
+            }
+        }
+        String str = "ApnContext.restartOnError(" + errorCode + ") found " + retriesLeft +
+                " and returned " + result;
+        if (DBG) log(str);
+        requestLog(str);
+        return result;
+    }
+
     @Override
     public synchronized String toString() {
         // We don't print mDataConnection because its recursive.
-        return "{mApnType=" + mApnType + " mState=" + getState() + " mWaitingApns={" + mWaitingApns +
-                "} mWaitingApnsPermanentFailureCountDown=" + mWaitingApnsPermanentFailureCountDown +
-                " mApnSetting={" + mApnSetting + "} mReason=" + mReason +
-                " mDataEnabled=" + mDataEnabled + " mDependencyMet=" + mDependencyMet + "}";
+        return "{mApnType=" + mApnType + " mState=" + getState() + " mWaitingApns={" +
+                mWaitingApns + "} mWaitingApnsPermanentFailureCountDown=" +
+                mWaitingApnsPermanentFailureCountDown + " mApnSetting={" + mApnSetting +
+                "} mReason=" + mReason + " mDataEnabled=" + mDataEnabled + " mDependencyMet=" +
+                mDependencyMet + "}";
     }
 
-    protected void log(String s) {
+    private void log(String s) {
         Rlog.d(LOG_TAG, "[ApnContext:" + mApnType + "] " + s);
     }
 
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println("ApnContext: " + this.toString());
+    public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+        final IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
+        synchronized (mRefCountLock) {
+            pw.println(toString());
+            if (mRefCount > 0) {
+                pw.increaseIndent();
+                for (LocalLog l : mLocalLogs) {
+                    l.dump(fd, pw, args);
+                }
+                pw.decreaseIndent();
+            }
+        }
     }
 }
diff --git a/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java b/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java
index c759413..4cc7550 100755
--- a/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java
+++ b/src/java/com/android/internal/telephony/dataconnection/ApnSetting.java
@@ -16,10 +16,12 @@
 
 package com.android.internal.telephony.dataconnection;
 
+import android.telephony.ServiceState;
 import android.text.TextUtils;
 
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.RILConstants;
+import com.android.internal.telephony.uicc.IccRecords;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -56,12 +58,20 @@
       */
     public final boolean carrierEnabled;
     /**
+     * Radio Access Technology info
+     * To check what values can hold, refer to ServiceState.java.
+     * This should be spread to other technologies,
+     * but currently only used for LTE(14) and EHRPD(13).
+     */
+    private final int bearer;
+    /**
       * Radio Access Technology info
-      * To check what values can hold, refer to ServiceState.java.
+      * To check what values can hold, refer to ServiceState.java. This is a bitmask of radio
+      * technologies in ServiceState.
       * This should be spread to other technologies,
       * but currently only used for LTE(14) and EHRPD(13).
       */
-    public final int bearer;
+    public final int bearerBitmask;
 
     /* ID of the profile in the modem */
     public final int profileId;
@@ -90,8 +100,8 @@
             String mmsc, String mmsProxy, String mmsPort,
             String user, String password, int authType, String[] types,
             String protocol, String roamingProtocol, boolean carrierEnabled, int bearer,
-            int profileId, boolean modemCognitive, int maxConns, int waitTime, int maxConnsTime,
-            int mtu, String mvnoType, String mvnoMatchData) {
+            int bearerBitmask, int profileId, boolean modemCognitive, int maxConns, int waitTime,
+            int maxConnsTime, int mtu, String mvnoType, String mvnoMatchData) {
         this.id = id;
         this.numeric = numeric;
         this.carrier = carrier;
@@ -112,6 +122,7 @@
         this.roamingProtocol = roamingProtocol;
         this.carrierEnabled = carrierEnabled;
         this.bearer = bearer;
+        this.bearerBitmask = (bearerBitmask | ServiceState.getBitmaskForTech(bearer));
         this.profileId = profileId;
         this.modemCognitive = modemCognitive;
         this.maxConns = maxConns;
@@ -139,12 +150,12 @@
      * v2 format:
      *   [ApnSettingV2] <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>,
      *   <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>,
-     *   <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearer>,
+     *   <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearerBitmask>,
      *
      * v3 format:
      *   [ApnSettingV3] <carrier>, <apn>, <proxy>, <port>, <user>, <password>, <server>,
      *   <mmsc>, <mmsproxy>, <mmsport>, <mcc>, <mnc>, <authtype>,
-     *   <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearer>,
+     *   <type>[| <type>...], <protocol>, <roaming_protocol>, <carrierEnabled>, <bearerBitmask>,
      *   <profileId>, <modemCognitive>, <maxConns>, <waitTime>, <maxConnsTime>, <mtu>,
      *   <mvnoType>, <mvnoMatchData>
      *
@@ -181,7 +192,7 @@
         String[] typeArray;
         String protocol, roamingProtocol;
         boolean carrierEnabled;
-        int bearer = 0;
+        int bearerBitmask = 0;
         int profileId = 0;
         boolean modemCognitive = false;
         int maxConns = 0;
@@ -196,7 +207,6 @@
             protocol = RILConstants.SETUP_DATA_PROTOCOL_IP;
             roamingProtocol = RILConstants.SETUP_DATA_PROTOCOL_IP;
             carrierEnabled = true;
-            bearer = 0;
         } else {
             if (a.length < 18) {
                 return null;
@@ -206,10 +216,7 @@
             roamingProtocol = a[15];
             carrierEnabled = Boolean.parseBoolean(a[16]);
 
-            try {
-                bearer = Integer.parseInt(a[17]);
-            } catch (NumberFormatException ex) {
-            }
+            bearerBitmask = ServiceState.getBitmaskFromString(a[17]);
 
             if (a.length > 22) {
                 modemCognitive = Boolean.parseBoolean(a[19]);
@@ -234,8 +241,8 @@
         }
 
         return new ApnSetting(-1,a[10]+a[11],a[0],a[1],a[2],a[3],a[7],a[8],
-                a[9],a[4],a[5],authType,typeArray,protocol,roamingProtocol,carrierEnabled,bearer,
-                profileId, modemCognitive, maxConns, waitTime, maxConnsTime, mtu,
+                a[9],a[4],a[5],authType,typeArray,protocol,roamingProtocol,carrierEnabled,0,
+                bearerBitmask, profileId, modemCognitive, maxConns, waitTime, maxConnsTime, mtu,
                 mvnoType, mvnoMatchData);
     }
 
@@ -286,6 +293,7 @@
         sb.append(", ").append(roamingProtocol);
         sb.append(", ").append(carrierEnabled);
         sb.append(", ").append(bearer);
+        sb.append(", ").append(bearerBitmask);
         sb.append(", ").append(profileId);
         sb.append(", ").append(modemCognitive);
         sb.append(", ").append(maxConns);
@@ -318,6 +326,52 @@
         return false;
     }
 
+    private static boolean imsiMatches(String imsiDB, String imsiSIM) {
+        // Note: imsiDB value has digit number or 'x' character for seperating USIM information
+        // for MVNO operator. And then digit number is matched at same order and 'x' character
+        // could replace by any digit number.
+        // ex) if imsiDB inserted '310260x10xxxxxx' for GG Operator,
+        //     that means first 6 digits, 8th and 9th digit
+        //     should be set in USIM for GG Operator.
+        int len = imsiDB.length();
+        int idxCompare = 0;
+
+        if (len <= 0) return false;
+        if (len > imsiSIM.length()) return false;
+
+        for (int idx=0; idx<len; idx++) {
+            char c = imsiDB.charAt(idx);
+            if ((c == 'x') || (c == 'X') || (c == imsiSIM.charAt(idx))) {
+                continue;
+            } else {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public static boolean mvnoMatches(IccRecords r, String mvnoType, String mvnoMatchData) {
+        if (mvnoType.equalsIgnoreCase("spn")) {
+            if ((r.getServiceProviderName() != null) &&
+                    r.getServiceProviderName().equalsIgnoreCase(mvnoMatchData)) {
+                return true;
+            }
+        } else if (mvnoType.equalsIgnoreCase("imsi")) {
+            String imsiSIM = r.getIMSI();
+            if ((imsiSIM != null) && imsiMatches(mvnoMatchData, imsiSIM)) {
+                return true;
+            }
+        } else if (mvnoType.equalsIgnoreCase("gid")) {
+            String gid1 = r.getGid1();
+            int mvno_match_data_length = mvnoMatchData.length();
+            if ((gid1 != null) && (gid1.length() >= mvno_match_data_length) &&
+                    gid1.substring(0, mvno_match_data_length).equalsIgnoreCase(mvnoMatchData)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     // TODO - if we have this function we should also have hashCode.
     // Also should handle changes in type order and perhaps case-insensitivity
     @Override
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
index 09b3c9b..4a84b6d 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataConnection.java
@@ -16,7 +16,7 @@
 
 package com.android.internal.telephony.dataconnection;
 
-
+import com.android.internal.telephony.CallTracker;
 import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.DctConstants;
 import com.android.internal.telephony.Phone;
@@ -24,6 +24,7 @@
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.RetryManager;
+import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.Protocol;
@@ -210,8 +211,13 @@
     static final int EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED = BASE + 11;
     static final int EVENT_DATA_CONNECTION_ROAM_ON = BASE + 12;
     static final int EVENT_DATA_CONNECTION_ROAM_OFF = BASE + 13;
+    static final int EVENT_BW_REFRESH_RESPONSE = BASE + 14;
+    static final int EVENT_DATA_CONNECTION_VOICE_CALL_STARTED = BASE + 15;
+    static final int EVENT_DATA_CONNECTION_VOICE_CALL_ENDED = BASE + 16;
 
-    private static final int CMD_TO_STRING_COUNT = EVENT_DATA_CONNECTION_ROAM_OFF - BASE + 1;
+    private static final int CMD_TO_STRING_COUNT =
+            EVENT_DATA_CONNECTION_VOICE_CALL_ENDED - BASE + 1;
+
     private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT];
     static {
         sCmdToString[EVENT_CONNECT - BASE] = "EVENT_CONNECT";
@@ -230,6 +236,11 @@
                 "EVENT_DATA_CONNECTION_DRS_OR_RAT_CHANGED";
         sCmdToString[EVENT_DATA_CONNECTION_ROAM_ON - BASE] = "EVENT_DATA_CONNECTION_ROAM_ON";
         sCmdToString[EVENT_DATA_CONNECTION_ROAM_OFF - BASE] = "EVENT_DATA_CONNECTION_ROAM_OFF";
+        sCmdToString[EVENT_BW_REFRESH_RESPONSE - BASE] = "EVENT_BW_REFRESH_RESPONSE";
+        sCmdToString[EVENT_DATA_CONNECTION_VOICE_CALL_STARTED - BASE] =
+                "EVENT_DATA_CONNECTION_VOICE_CALL_STARTED";
+        sCmdToString[EVENT_DATA_CONNECTION_VOICE_CALL_ENDED - BASE] =
+                "EVENT_DATA_CONNECTION_VOICE_CALL_ENDED";
     }
     // Convert cmd to string or null if unknown
     static String cmdToString(int cmd) {
@@ -505,6 +516,7 @@
         if (DBG) log("onConnect: carrier='" + mApnSetting.carrier
                 + "' APN='" + mApnSetting.apn
                 + "' proxy='" + mApnSetting.proxy + "' port='" + mApnSetting.port + "'");
+        if (cp.mApnContext != null) cp.mApnContext.requestLog("DataConnection.onConnect");
 
         // Check if we should fake an error.
         if (mDcTesterFailBringUpAll.getDcFailBringUp().mCounter  > 0) {
@@ -549,7 +561,7 @@
         }
 
         String protocol;
-        if (mPhone.getServiceState().getDataRoaming()) {
+        if (mPhone.getServiceState().getDataRoamingFromRegistration()) {
             protocol = mApnSetting.roamingProtocol;
         } else {
             protocol = mApnSetting.protocol;
@@ -572,21 +584,28 @@
      */
     private void tearDownData(Object o) {
         int discReason = RILConstants.DEACTIVATE_REASON_NONE;
+        ApnContext apnContext = null;
         if ((o != null) && (o instanceof DisconnectParams)) {
             DisconnectParams dp = (DisconnectParams)o;
-
+            apnContext = dp.mApnContext;
             if (TextUtils.equals(dp.mReason, Phone.REASON_RADIO_TURNED_OFF)) {
                 discReason = RILConstants.DEACTIVATE_REASON_RADIO_OFF;
             } else if (TextUtils.equals(dp.mReason, Phone.REASON_PDP_RESET)) {
                 discReason = RILConstants.DEACTIVATE_REASON_PDP_RESET;
             }
         }
-        if (mPhone.mCi.getRadioState().isOn()) {
-            if (DBG) log("tearDownData radio is on, call deactivateDataCall");
+        if (mPhone.mCi.getRadioState().isOn()
+                || (mPhone.getServiceState().getRilDataRadioTechnology()
+                        == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN )) {
+            String str = "tearDownData radio is on, call deactivateDataCall";
+            if (DBG) log(str);
+            if (apnContext != null) apnContext.requestLog(str);
             mPhone.mCi.deactivateDataCall(mCid, discReason,
                     obtainMessage(EVENT_DEACTIVATE_DONE, mTag, 0, o));
         } else {
-            if (DBG) log("tearDownData radio is off sendMessage EVENT_DEACTIVATE_DONE immediately");
+            String str = "tearDownData radio is off sendMessage EVENT_DEACTIVATE_DONE immediately";
+            if (DBG) log(str);
+            if (apnContext != null) apnContext.requestLog(str);
             AsyncResult ar = new AsyncResult(o, null, null);
             sendMessage(obtainMessage(EVENT_DEACTIVATE_DONE, mTag, 0, ar));
         }
@@ -1221,6 +1240,7 @@
                     mNetworkInfo.setSubtype(networkType,
                             TelephonyManager.getNetworkTypeName(networkType));
                     if (mNetworkAgent != null) {
+                        updateNetworkInfoSuspendState();
                         mNetworkAgent.sendNetworkCapabilities(makeNetworkCapabilities());
                         mNetworkAgent.sendNetworkInfo(mNetworkInfo);
                         mNetworkAgent.sendLinkProperties(mLinkProperties);
@@ -1246,6 +1266,33 @@
             return retVal;
         }
     }
+
+    private boolean updateNetworkInfoSuspendState() {
+        final NetworkInfo.DetailedState oldState = mNetworkInfo.getDetailedState();
+
+        // this is only called when we are either connected or suspended.  Decide which.
+        if (mNetworkAgent == null) {
+            Rlog.e(getName(), "Setting suspend state without a NetworkAgent");
+        }
+
+        // if we are not in-service change to SUSPENDED
+        final ServiceStateTracker sst = mPhone.getServiceStateTracker();
+        if (sst.getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE) {
+            mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.SUSPENDED, null, null);
+        } else {
+            // check for voice call and concurrency issues
+            if (sst.isConcurrentVoiceAndDataAllowed() == false) {
+                final CallTracker ct = mPhone.getCallTracker();
+                if (ct.getState() != PhoneConstants.State.IDLE) {
+                    mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.SUSPENDED, null, null);
+                    return (oldState != NetworkInfo.DetailedState.SUSPENDED);
+                }
+            }
+            mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
+        }
+        return (oldState != mNetworkInfo.getDetailedState());
+    }
+
     private DcDefaultState mDefaultState = new DcDefaultState();
 
     /**
@@ -1565,6 +1612,9 @@
                         log("DcActivatingState onSetupConnectionCompleted result=" + result
                                 + " dc=" + DataConnection.this);
                     }
+                    if (cp.mApnContext != null) {
+                        cp.mApnContext.requestLog("onSetupConnectionCompleted result=" + result);
+                    }
                     switch (result) {
                         case SUCCESS:
                             // All is well
@@ -1591,17 +1641,20 @@
                         case ERR_RilError:
                             int delay = mDcRetryAlarmController.getSuggestedRetryTime(
                                                                     DataConnection.this, ar);
-                            if (DBG) {
-                                log("DcActivatingState: ERR_RilError "
-                                        + " delay=" + delay
-                                        + " isRetryNeeded=" + mRetryManager.isRetryNeeded()
-                                        + " result=" + result
-                                        + " result.isRestartRadioFail=" +
-                                        result.mFailCause.isRestartRadioFail()
-                                        + " result.isPermanentFail=" +
-                                        mDct.isPermanentFail(result.mFailCause));
-                            }
-                            if (result.mFailCause.isRestartRadioFail()) {
+                            String str = "DcActivatingState: ERR_RilError "
+                                    + " delay=" + delay
+                                    + " isRetryNeeded=" + mRetryManager.isRetryNeeded()
+                                    + " result=" + result
+                                    + " result.isRestartRadioFail=" +
+                                    result.mFailCause.isRestartRadioFail()
+                                    + " result.isPermanentFail=" +
+                                    mDct.isPermanentFail(result.mFailCause);
+                            if (DBG) log(str);
+                            if (cp.mApnContext != null) cp.mApnContext.requestLog(str);
+                            if (result.mFailCause.isRestartRadioFail() ||
+                                    (cp.mApnContext != null &&
+                                    cp.mApnContext.restartOnError(
+                                    result.mFailCause.getErrorCode()))) {
                                 if (DBG) log("DcActivatingState: ERR_RilError restart radio");
                                 mDct.sendRestartRadio();
                                 mInactiveState.setEnterNotificationParams(cp, result.mFailCause);
@@ -1720,6 +1773,11 @@
             // If we were retrying there maybe more than one, otherwise they'll only be one.
             notifyAllOfConnected(Phone.REASON_CONNECTED);
 
+            mPhone.getCallTracker().registerForVoiceCallStarted(getHandler(),
+                    DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_STARTED, null);
+            mPhone.getCallTracker().registerForVoiceCallEnded(getHandler(),
+                    DataConnection.EVENT_DATA_CONNECTION_VOICE_CALL_ENDED, null);
+
             // If the EVENT_CONNECT set the current max retry restore it here
             // if it didn't then this is effectively a NOP.
             mRetryManager.restoreCurMaxRetryCount();
@@ -1740,8 +1798,19 @@
         @Override
         public void exit() {
             if (DBG) log("DcActiveState: exit dc=" + this);
+            String reason = mNetworkInfo.getReason();
+            if(mDcController.isExecutingCarrierChange()) {
+                reason = Phone.REASON_CARRIER_CHANGE;
+            } else if (mDisconnectParams != null && mDisconnectParams.mReason != null) {
+                reason = mDisconnectParams.mReason;
+            } else if (mDcFailCause != null) {
+                reason = mDcFailCause.toString();
+            }
+            mPhone.getCallTracker().unregisterForVoiceCallStarted(getHandler());
+            mPhone.getCallTracker().unregisterForVoiceCallEnded(getHandler());
+
             mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED,
-                    mNetworkInfo.getReason(), mNetworkInfo.getExtraInfo());
+                    reason, mNetworkInfo.getExtraInfo());
             mNetworkAgent.sendNetworkInfo(mNetworkInfo);
             mNetworkAgent = null;
         }
@@ -1847,6 +1916,33 @@
                     retVal = HANDLED;
                     break;
                 }
+                case EVENT_BW_REFRESH_RESPONSE: {
+                    AsyncResult ar = (AsyncResult)msg.obj;
+                    if (ar.exception != null) {
+                        log("EVENT_BW_REFRESH_RESPONSE: error ignoring, e=" + ar.exception);
+                    } else {
+                        final ArrayList<Integer> capInfo = (ArrayList<Integer>)ar.result;
+                        final int lceBwDownKbps = capInfo.get(0);
+                        NetworkCapabilities nc = makeNetworkCapabilities();
+                        if (mPhone.getLceStatus() == RILConstants.LCE_ACTIVE) {
+                            nc.setLinkDownstreamBandwidthKbps(lceBwDownKbps);
+                            if (mNetworkAgent != null) {
+                                mNetworkAgent.sendNetworkCapabilities(nc);
+                            }
+                        }
+                    }
+                    retVal = HANDLED;
+                    break;
+                }
+                case EVENT_DATA_CONNECTION_VOICE_CALL_STARTED:
+                case EVENT_DATA_CONNECTION_VOICE_CALL_ENDED: {
+                    if (updateNetworkInfoSuspendState()) {
+                        // state changed
+                        mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+                    }
+                    retVal = HANDLED;
+                    break;
+                }
                 default:
                     if (VDBG) {
                         log("DcActiveState not handled msg.what=" + getWhatToString(msg.what));
@@ -1876,10 +1972,14 @@
                     break;
 
                 case EVENT_DEACTIVATE_DONE:
-                    if (DBG) log("DcDisconnectingState msg.what=EVENT_DEACTIVATE_DONE RefCount="
-                            + mApnContexts.size());
                     AsyncResult ar = (AsyncResult) msg.obj;
                     DisconnectParams dp = (DisconnectParams) ar.userObj;
+
+                    String str = "DcDisconnectingState msg.what=EVENT_DEACTIVATE_DONE RefCount="
+                            + mApnContexts.size();
+                    if (DBG) log(str);
+                    if (dp.mApnContext != null) dp.mApnContext.requestLog(str);
+
                     if (dp.mTag == mTag) {
                         // Transition to inactive but send notifications after
                         // we've entered the mInactive state.
@@ -1918,10 +2018,10 @@
                     AsyncResult ar = (AsyncResult) msg.obj;
                     ConnectionParams cp = (ConnectionParams) ar.userObj;
                     if (cp.mTag == mTag) {
-                        if (DBG) {
-                            log("DcDisconnectionErrorCreatingConnection" +
-                                " msg.what=EVENT_DEACTIVATE_DONE");
-                        }
+                        String str = "DcDisconnectionErrorCreatingConnection" +
+                                " msg.what=EVENT_DEACTIVATE_DONE";
+                        if (DBG) log(str);
+                        if (cp.mApnContext != null) cp.mApnContext.requestLog(str);
 
                         // Transition to inactive but send notifications after
                         // we've entered the mInactive state.
@@ -1975,6 +2075,13 @@
                         obtainMessage(EVENT_DISCONNECT, dp));
             }
         }
+
+        @Override
+        protected void pollLceData() {
+            if(mPhone.getLceStatus() == RILConstants.LCE_ACTIVE) {  // active LCE service
+                mPhone.mCi.pullLceData(DataConnection.this.obtainMessage(EVENT_BW_REFRESH_RESPONSE));
+            }
+        }
     }
 
     // ******* "public" interface
diff --git a/src/java/com/android/internal/telephony/dataconnection/DataProfile.java b/src/java/com/android/internal/telephony/dataconnection/DataProfile.java
index 45a6b6b..f459f27 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DataProfile.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DataProfile.java
@@ -72,9 +72,9 @@
 
     DataProfile(ApnSetting apn, boolean isRoaming) {
         this(apn.profileId, apn.apn, isRoaming? apn.protocol : apn.roamingProtocol,
-                apn.authType, apn.user, apn.password, apn.bearer == 0 ? TYPE_COMMON :
-                (ServiceState.isCdma(apn.bearer) ? TYPE_3GPP2 : TYPE_3GPP), apn.maxConnsTime,
-                apn.maxConns, apn.waitTime, apn.carrierEnabled);
+                apn.authType, apn.user, apn.password, apn.bearerBitmask == 0 ? TYPE_COMMON :
+                        (ServiceState.hasCdma(apn.bearerBitmask) ? TYPE_3GPP2 : TYPE_3GPP),
+                apn.maxConnsTime, apn.maxConns, apn.waitTime, apn.carrierEnabled);
     }
 
     public static Parcel toParcel(Parcel pc, DataProfile[] dps) {
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcController.java b/src/java/com/android/internal/telephony/dataconnection/DcController.java
index c7ef159..98d1c3b 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcController.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcController.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.telephony.dataconnection;
 
+import android.content.Context;
 import android.net.LinkAddress;
 import android.net.NetworkUtils;
 import android.net.LinkProperties.CompareResult;
@@ -25,6 +26,8 @@
 import android.os.Message;
 import android.os.SystemClock;
 import android.telephony.DataConnectionRealTimeInfo;
+import android.telephony.TelephonyManager;
+import android.telephony.PhoneStateListener;
 import android.telephony.Rlog;
 
 import com.android.internal.telephony.DctConstants;
@@ -73,6 +76,13 @@
 
     private DccDefaultState mDccDefaultState = new DccDefaultState();
 
+    TelephonyManager mTelephonyManager;
+    private PhoneStateListener mPhoneStateListener;
+
+    //mExecutingCarrierChange tracks whether the phone is currently executing
+    //carrier network change
+    private volatile boolean mExecutingCarrierChange;
+
     /**
      * Constructor.
      *
@@ -91,6 +101,19 @@
         addState(mDccDefaultState);
         setInitialState(mDccDefaultState);
         log("X ctor");
+
+        mPhoneStateListener = new PhoneStateListener(handler.getLooper()) {
+            @Override
+            public void onCarrierNetworkChange(boolean active) {
+                mExecutingCarrierChange = active;
+            }
+        };
+
+        mTelephonyManager = (TelephonyManager) phone.getContext().getSystemService(Context.TELEPHONY_SERVICE);
+        if(mTelephonyManager != null) {
+            mTelephonyManager.listen(mPhoneStateListener,
+                    PhoneStateListener.LISTEN_CARRIER_NETWORK_CHANGE);
+        }
     }
 
     static DcController makeDcc(PhoneBase phone, DcTrackerBase dct, Handler handler) {
@@ -101,6 +124,7 @@
 
     void dispose() {
         log("dispose: call quiteNow()");
+        if(mTelephonyManager != null) mTelephonyManager.listen(mPhoneStateListener, 0);
         quitNow();
     }
 
@@ -127,6 +151,10 @@
         }
     }
 
+    boolean isExecutingCarrierChange() {
+        return mExecutingCarrierChange;
+    }
+
     private class DccDefaultState extends State {
         @Override
         public void enter() {
@@ -351,28 +379,28 @@
                 }
             }
 
-            // Temporary notification until RIL implementation is complete.
-            if (mOverallDataConnectionActiveState != newOverallDataConnectionActiveState) {
-                mOverallDataConnectionActiveState = newOverallDataConnectionActiveState;
-                long time = SystemClock.elapsedRealtimeNanos();
-                int dcPowerState;
-                switch (mOverallDataConnectionActiveState) {
-                    case DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE:
-                    case DATA_CONNECTION_ACTIVE_PH_LINK_DORMANT:
-                        dcPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
-                        break;
-                    case DATA_CONNECTION_ACTIVE_PH_LINK_UP:
-                        dcPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH;
-                        break;
-                    default:
-                        dcPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_UNKNOWN;
-                        break;
-                }
-                DataConnectionRealTimeInfo dcRtInfo =
-                        new DataConnectionRealTimeInfo(time , dcPowerState);
-                log("onDataStateChanged: notify DcRtInfo changed dcRtInfo=" + dcRtInfo);
-                mPhone.notifyDataConnectionRealTimeInfo(dcRtInfo);
-            }
+            // TODO: b/23319188 Enable/Disable this based on enable/disable of dormancy indications 
+            //if (mOverallDataConnectionActiveState != newOverallDataConnectionActiveState) {
+            //    mOverallDataConnectionActiveState = newOverallDataConnectionActiveState;
+            //    long time = SystemClock.elapsedRealtimeNanos();
+            //    int dcPowerState;
+            //    switch (mOverallDataConnectionActiveState) {
+            //        case DATA_CONNECTION_ACTIVE_PH_LINK_INACTIVE:
+            //        case DATA_CONNECTION_ACTIVE_PH_LINK_DORMANT:
+            //            dcPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
+            //            break;
+            //        case DATA_CONNECTION_ACTIVE_PH_LINK_UP:
+            //            dcPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH;
+            //            break;
+            //        default:
+            //            dcPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_UNKNOWN;
+            //            break;
+            //    }
+            //    DataConnectionRealTimeInfo dcRtInfo =
+            //            new DataConnectionRealTimeInfo(time , dcPowerState);
+            //    log("onDataStateChanged: notify DcRtInfo changed dcRtInfo=" + dcRtInfo);
+            //    mPhone.notifyDataConnectionRealTimeInfo(dcRtInfo); 
+            //}
 
             if (DBG) {
                 lr("onDataStateChanged: dcsToRetry=" + dcsToRetry
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcSwitchAsyncChannel.java b/src/java/com/android/internal/telephony/dataconnection/DcSwitchAsyncChannel.java
index 95e3ab7..461249a 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcSwitchAsyncChannel.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcSwitchAsyncChannel.java
@@ -16,13 +16,14 @@
 
 package com.android.internal.telephony.dataconnection;
 
-import com.android.internal.util.AsyncChannel;
-import com.android.internal.util.Protocol;
-import com.android.internal.telephony.PhoneConstants;
-
 import android.net.NetworkRequest;
 import android.os.Message;
 import android.telephony.Rlog;
+import android.util.LocalLog;
+
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Protocol;
+import com.android.internal.telephony.PhoneConstants;
 
 public class DcSwitchAsyncChannel extends AsyncChannel {
     private static final boolean DBG = true;
@@ -34,50 +35,61 @@
 
     // ***** Event codes for driving the state machine
     private static final int BASE = Protocol.BASE_DATA_CONNECTION_TRACKER + 0x00002000;
-    static final int REQ_CONNECT = BASE + 0;
-    static final int RSP_CONNECT = BASE + 1;
-    static final int REQ_DISCONNECT = BASE + 2;
-    static final int RSP_DISCONNECT = BASE + 3;
-    static final int REQ_DISCONNECT_ALL = BASE + 4;
-    static final int RSP_DISCONNECT_ALL = BASE + 5;
-    static final int REQ_IS_IDLE_STATE = BASE + 6;
-    static final int RSP_IS_IDLE_STATE = BASE + 7;
-    static final int REQ_IS_IDLE_OR_DETACHING_STATE = BASE + 8;
-    static final int RSP_IS_IDLE_OR_DETACHING_STATE = BASE + 9;
-    static final int EVENT_DATA_ATTACHED = BASE + 10;
-    static final int EVENT_DATA_DETACHED = BASE + 11;
+    static final int REQ_CONNECT =                    BASE + 0;
+    static final int REQ_RETRY_CONNECT  =             BASE + 1;
+    static final int REQ_DISCONNECT_ALL =             BASE + 2;
+    static final int REQ_IS_IDLE_STATE =              BASE + 3;
+    static final int RSP_IS_IDLE_STATE =              BASE + 4;
+    static final int REQ_IS_IDLE_OR_DETACHING_STATE = BASE + 5;
+    static final int RSP_IS_IDLE_OR_DETACHING_STATE = BASE + 6;
+    static final int EVENT_DATA_ATTACHED =            BASE + 7;
+    static final int EVENT_DATA_DETACHED =            BASE + 8;
+    static final int EVENT_EMERGENCY_CALL_STARTED =   BASE + 9;
+    static final int EVENT_EMERGENCY_CALL_ENDED =     BASE + 10;
 
-    private static final int CMD_TO_STRING_COUNT = EVENT_DATA_DETACHED - BASE + 1;
+    private static final int CMD_TO_STRING_COUNT = EVENT_EMERGENCY_CALL_ENDED - BASE + 1;
     private static String[] sCmdToString = new String[CMD_TO_STRING_COUNT];
     static {
         sCmdToString[REQ_CONNECT - BASE] = "REQ_CONNECT";
-        sCmdToString[RSP_CONNECT - BASE] = "RSP_CONNECT";
-        sCmdToString[REQ_DISCONNECT - BASE] = "REQ_DISCONNECT";
-        sCmdToString[RSP_DISCONNECT - BASE] = "RSP_DISCONNECT";
+        sCmdToString[REQ_RETRY_CONNECT - BASE] = "REQ_RETRY_CONNECT";
         sCmdToString[REQ_DISCONNECT_ALL - BASE] = "REQ_DISCONNECT_ALL";
-        sCmdToString[RSP_DISCONNECT_ALL - BASE] = "RSP_DISCONNECT_ALL";
         sCmdToString[REQ_IS_IDLE_STATE - BASE] = "REQ_IS_IDLE_STATE";
         sCmdToString[RSP_IS_IDLE_STATE - BASE] = "RSP_IS_IDLE_STATE";
         sCmdToString[REQ_IS_IDLE_OR_DETACHING_STATE - BASE] = "REQ_IS_IDLE_OR_DETACHING_STATE";
         sCmdToString[RSP_IS_IDLE_OR_DETACHING_STATE - BASE] = "RSP_IS_IDLE_OR_DETACHING_STATE";
         sCmdToString[EVENT_DATA_ATTACHED - BASE] = "EVENT_DATA_ATTACHED";
         sCmdToString[EVENT_DATA_DETACHED - BASE] = "EVENT_DATA_DETACHED";
+        sCmdToString[EVENT_EMERGENCY_CALL_STARTED - BASE] = "EVENT_EMERGENCY_CALL_STARTED";
+        sCmdToString[EVENT_EMERGENCY_CALL_ENDED - BASE] = "EVENT_EMERGENCY_CALL_ENDED";
     }
 
     public static class RequestInfo {
         boolean executed;
-        NetworkRequest request;
-        int priority;
+        final NetworkRequest request;
+        final int priority;
+        final int phoneId;
+        private final LocalLog requestLog;
 
-        public RequestInfo(NetworkRequest request, int priority) {
+        public RequestInfo(NetworkRequest request, int priority, LocalLog l, int phoneId) {
             this.request = request;
             this.priority = priority;
+            this.requestLog = l;
+            this.executed = false;
+            this.phoneId = phoneId;
+        }
+
+        public void log(String str) {
+            requestLog.log(str);
+        }
+
+        public LocalLog getLog() {
+            return requestLog;
         }
 
         @Override
         public String toString() {
             return "[ request=" + request + ", executed=" + executed +
-                ", priority=" + priority + "]";
+                ", priority=" + priority + ", phoneId=" + phoneId + "]";
         }
     }
 
@@ -95,62 +107,34 @@
         tagId = id;
     }
 
-    private int rspConnect(Message response) {
-        int retVal = response.arg1;
-        if (DBG) log("rspConnect=" + retVal);
-        return retVal;
+    public int connect(RequestInfo apnRequest) {
+        sendMessage(REQ_CONNECT, apnRequest);
+        return PhoneConstants.APN_REQUEST_STARTED;
     }
 
-    public int connectSync(RequestInfo apnRequest) {
-        Message response = sendMessageSynchronously(REQ_CONNECT, apnRequest);
-        if ((response != null) && (response.what == RSP_CONNECT)) {
-            return rspConnect(response);
-        } else {
-            if (DBG) log("rspConnect error response=" + response);
-            return PhoneConstants.APN_REQUEST_FAILED;
-        }
+    public void retryConnect() {
+        sendMessage(REQ_RETRY_CONNECT);
     }
 
-    private int rspDisconnect(Message response) {
-        int retVal = response.arg1;
-        if (DBG) log("rspDisconnect=" + retVal);
-        return retVal;
-    }
-
-    public int disconnectSync(RequestInfo apnRequest) {
-        Message response = sendMessageSynchronously(REQ_DISCONNECT, apnRequest);
-        if ((response != null) && (response.what == RSP_DISCONNECT)) {
-            return rspDisconnect(response);
-        } else {
-            if (DBG) log("rspDisconnect error response=" + response);
-            return PhoneConstants.APN_REQUEST_FAILED;
-        }
-    }
-
-    private int rspDisconnectAll(Message response) {
-        int retVal = response.arg1;
-        if (DBG) log("rspDisconnectAll=" + retVal);
-        return retVal;
-    }
-
-    public int disconnectAllSync() {
-        Message response = sendMessageSynchronously(REQ_DISCONNECT_ALL);
-        if ((response != null) && (response.what == RSP_DISCONNECT_ALL)) {
-            return rspDisconnectAll(response);
-        } else {
-            if (DBG) log("rspDisconnectAll error response=" + response);
-            return PhoneConstants.APN_REQUEST_FAILED;
-        }
+    public int disconnectAll() {
+        sendMessage(REQ_DISCONNECT_ALL);
+        return PhoneConstants.APN_REQUEST_STARTED;
     }
 
     public void notifyDataAttached() {
         sendMessage(EVENT_DATA_ATTACHED);
-        if (DBG) log("notifyDataAttached");
     }
 
     public void notifyDataDetached() {
         sendMessage(EVENT_DATA_DETACHED);
-        if (DBG) log("EVENT_DATA_DETACHED");
+    }
+
+    public void notifyEmergencyCallToggled(int start) {
+        if (start != 0) {
+            sendMessage(EVENT_EMERGENCY_CALL_STARTED);
+        } else {
+            sendMessage(EVENT_EMERGENCY_CALL_ENDED);
+        }
     }
 
     private boolean rspIsIdle(Message response) {
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcSwitchStateMachine.java b/src/java/com/android/internal/telephony/dataconnection/DcSwitchStateMachine.java
index ed87a99..ae2d2ff 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcSwitchStateMachine.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcSwitchStateMachine.java
@@ -17,15 +17,18 @@
 package com.android.internal.telephony.dataconnection;
 
 import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.IState;
 import com.android.internal.util.Protocol;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
+import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.PhoneBase;
 import com.android.internal.telephony.PhoneProxy;
 import com.android.internal.telephony.dataconnection.DcSwitchAsyncChannel.RequestInfo;
 
+import android.os.AsyncResult;
 import android.os.Message;
 import android.telephony.Rlog;
 
@@ -36,18 +39,26 @@
 
     // ***** Event codes for driving the state machine
     private static final int BASE = Protocol.BASE_DATA_CONNECTION_TRACKER + 0x00001000;
-    private static final int EVENT_CONNECTED = BASE + 0;
+    private static final int EVENT_CONNECTED       = BASE + 0;
+    private static final int EVENT_DATA_ALLOWED    = BASE + 1;
+    private static final int CMD_RETRY_ATTACH      = BASE + 2;
+    private static final int EVENT_DATA_DISALLOWED = BASE + 3;
 
     private int mId;
     private Phone mPhone;
     private AsyncChannel mAc;
 
     private IdleState mIdleState = new IdleState();
+    private EmergencyState mEmergencyState = new EmergencyState();
     private AttachingState mAttachingState = new AttachingState();
     private AttachedState mAttachedState = new AttachedState();
     private DetachingState mDetachingState = new DetachingState();
     private DefaultState mDefaultState = new DefaultState();
 
+    // In case of transition to emergency state, this tracks the state of the state machine prior
+    // to entering emergency state
+    private IState mPreEmergencyState;
+
     protected DcSwitchStateMachine(Phone phone, String name, int id) {
         super(name);
         if (DBG) log("DcSwitchState constructor E");
@@ -56,6 +67,7 @@
 
         addState(mDefaultState);
         addState(mIdleState, mDefaultState);
+        addState(mEmergencyState, mDefaultState);
         addState(mAttachingState, mDefaultState);
         addState(mAttachedState, mDefaultState);
         addState(mDetachingState, mDefaultState);
@@ -89,21 +101,25 @@
 
             switch (msg.what) {
                 case DcSwitchAsyncChannel.REQ_CONNECT: {
-                    if (DBG) {
-                        log("IdleState: REQ_CONNECT");
-                    }
-
-                    PhoneBase pb = (PhoneBase)((PhoneProxy)mPhone).getActivePhone();
-                    pb.mCi.setDataAllowed(true, null);
-
-                    mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_CONNECT,
-                            PhoneConstants.APN_REQUEST_STARTED);
-
+                    RequestInfo apnRequest = (RequestInfo)msg.obj;
+                    apnRequest.log("DcSwitchStateMachine.IdleState: REQ_CONNECT");
+                    if (DBG) log("IdleState: REQ_CONNECT, apnRequest=" + apnRequest);
                     transitionTo(mAttachingState);
                     retVal = HANDLED;
                     break;
                 }
 
+                case DcSwitchAsyncChannel.REQ_DISCONNECT_ALL: {
+                    if (DBG) log("AttachingState: REQ_DISCONNECT_ALL" );
+
+                    // Shouldn't have any requests, but why not try..
+                    DctController.getInstance().releaseAllRequests(mId);
+
+                    retVal = HANDLED;
+                    break;
+                }
+
+
                 case DcSwitchAsyncChannel.EVENT_DATA_ATTACHED:
                     if (DBG) {
                         log("AttachingState: EVENT_DATA_ATTACHED");
@@ -131,10 +147,73 @@
         }
     }
 
+    private class EmergencyState extends State {
+        @Override
+        public boolean processMessage(Message msg) {
+            final PhoneBase pb = (PhoneBase)((PhoneProxy)mPhone).getActivePhone();
+            if (!pb.mDcTracker.isEmergency()) {
+                loge("EmergencyState: isEmergency() is false. deferMessage msg.what=0x" +
+                        Integer.toHexString(msg.what));
+                deferMessage(msg);
+                transitionTo(mPreEmergencyState);
+                return HANDLED;
+            }
+
+            switch (msg.what) {
+                case DcSwitchAsyncChannel.EVENT_EMERGENCY_CALL_ENDED: {
+                    transitionTo(mPreEmergencyState);
+                    break;
+                }
+
+                case DcSwitchAsyncChannel.EVENT_EMERGENCY_CALL_STARTED: {
+                    loge("EmergencyState: ignoring EVENT_EMERGENCY_CALL_STARTED");
+                    break;
+                }
+
+                // explicitly call out the messages we must defer
+                // anything not listed falls through to the default state
+                case DcSwitchAsyncChannel.REQ_CONNECT:
+                case DcSwitchAsyncChannel.REQ_RETRY_CONNECT:
+                case DcSwitchAsyncChannel.REQ_DISCONNECT_ALL:
+                case DcSwitchAsyncChannel.EVENT_DATA_ATTACHED:
+                case DcSwitchAsyncChannel.EVENT_DATA_DETACHED: {
+                    log("EmergencyState: deferMessage msg.what=0x" + Integer.toHexString(msg.what));
+                    deferMessage(msg);
+                    break;
+                }
+
+                default: {
+                    if (VDBG) {
+                        log("EmergencyState: nothandled msg.what=0x" +
+                                Integer.toHexString(msg.what));
+                    }
+                    return NOT_HANDLED;
+                }
+            }
+
+            return HANDLED;
+        }
+    }
+
     private class AttachingState extends State {
+        private int mCurrentAllowedSequence = 0;
         @Override
         public void enter() {
             log("AttachingState: enter");
+            doEnter();
+        }
+
+        private void doEnter() {
+            final PhoneBase pb = (PhoneBase)((PhoneProxy)mPhone).getActivePhone();
+            pb.mCi.setDataAllowed(true, obtainMessage(EVENT_DATA_ALLOWED,
+                    ++mCurrentAllowedSequence, 0));
+            // if we're on a carrier that unattaches us if we're idle for too long
+            // (on wifi) and they won't re-attach until we poke them.  Poke them!
+            // essentially react as Attached does here in Attaching.
+            if (pb.mDcTracker.getAutoAttachOnCreation()) {
+                if (DBG) log("AttachingState executeAll due to autoAttach");
+                DctController.getInstance().executeAllRequests(mId);
+            }
         }
 
         @Override
@@ -143,36 +222,75 @@
 
             switch (msg.what) {
                 case DcSwitchAsyncChannel.REQ_CONNECT: {
-                    if (DBG) {
-                        log("AttachingState: REQ_CONNECT");
+                    RequestInfo apnRequest = (RequestInfo)msg.obj;
+                    apnRequest.log("DcSwitchStateMachine.AttachingState: REQ_CONNECT");
+                    if (DBG) log("AttachingState: REQ_CONNECT, apnRequest=" + apnRequest);
+
+                    final PhoneBase pb = (PhoneBase)((PhoneProxy)mPhone).getActivePhone();
+                    if (pb.mDcTracker.getAutoAttachOnCreation() == false) {
+                        // do nothing - wait til we attach and then we'll execute all requests
+                    } else {
+                        apnRequest.log("DcSwitchStateMachine processing due to autoAttach");
+                        DctController.getInstance().executeRequest(apnRequest);
                     }
-
-                    PhoneBase pb = (PhoneBase) ((PhoneProxy) mPhone).getActivePhone();
-                    pb.mCi.setDataAllowed(true, null);
-
-                    mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_CONNECT,
-                            PhoneConstants.APN_REQUEST_STARTED);
+                    retVal = HANDLED;
+                    break;
+                }
+                case EVENT_DATA_ALLOWED: {
+                    AsyncResult ar = (AsyncResult)msg.obj;
+                    if (mCurrentAllowedSequence != msg.arg1) {
+                        loge("EVENT_DATA_ALLOWED ignored arg1=" + msg.arg1 + ", seq=" +
+                                mCurrentAllowedSequence);
+                    } else if (ar.exception != null) {
+                        if (ar.exception instanceof CommandException) {
+                            CommandException e = (CommandException)ar.exception;
+                            if (e.getCommandError() ==
+                                    CommandException.Error.REQUEST_NOT_SUPPORTED) {
+                                // must be on a single-sim device so stay in Attaching
+                                // this is important to avoid an infinite loop
+                                retVal = HANDLED;
+                                break;
+                            }
+                        }
+                        loge("EVENT_DATA_ALLOWED failed, " + ar.exception);
+                        transitionTo(mIdleState);
+                    }
                     retVal = HANDLED;
                     break;
                 }
 
-                case DcSwitchAsyncChannel.EVENT_DATA_ATTACHED:
+                case DcSwitchAsyncChannel.REQ_RETRY_CONNECT: {
+                    if (DBG) log("AttachingState going to retry");
+                    doEnter();
+                    retVal = HANDLED;
+                    break;
+                }
+
+                case DcSwitchAsyncChannel.EVENT_DATA_ATTACHED: {
                     if (DBG) {
                         log("AttachingState: EVENT_DATA_ATTACHED");
                     }
                     transitionTo(mAttachedState);
                     retVal = HANDLED;
                     break;
+                }
 
                 case DcSwitchAsyncChannel.REQ_DISCONNECT_ALL: {
                     if (DBG) {
                         log("AttachingState: REQ_DISCONNECT_ALL" );
                     }
-                    DctController.getInstance().releaseAllRequests(mId);
-                    mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_DISCONNECT_ALL,
-                            PhoneConstants.APN_REQUEST_STARTED);
+                    final PhoneBase pb = (PhoneBase)((PhoneProxy)mPhone).getActivePhone();
+                    if (pb.mDcTracker.getAutoAttachOnCreation()) {
+                        // if AutoAttachOnCreation, then we may have executed requests
+                        // without ever actually getting to Attached, so release the request
+                        // here in that case.
+                        if (DBG) log("releasingAll due to autoAttach");
+                        DctController.getInstance().releaseAllRequests(mId);
+                    }
 
-                    transitionTo(mDetachingState);
+                    // modem gets unhappy if we try to detach while attaching
+                    // wait til attach finishes.
+                    deferMessage(msg);
                     retVal = HANDLED;
                     break;
                 }
@@ -204,27 +322,10 @@
             switch (msg.what) {
                 case DcSwitchAsyncChannel.REQ_CONNECT: {
                     RequestInfo apnRequest = (RequestInfo)msg.obj;
-                    if (DBG) {
-                        log("AttachedState: REQ_CONNECT, apnRequest=" + apnRequest);
-                    }
+                    apnRequest.log("DcSwitchStateMachine.AttachedState: REQ_CONNECT");
+                    if (DBG) log("AttachedState: REQ_CONNECT, apnRequest=" + apnRequest);
 
                     DctController.getInstance().executeRequest(apnRequest);
-                    mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_CONNECT,
-                            PhoneConstants.APN_REQUEST_STARTED);
-
-                    retVal = HANDLED;
-                    break;
-                }
-                case DcSwitchAsyncChannel.REQ_DISCONNECT: {
-                    RequestInfo apnRequest = (RequestInfo)msg.obj;
-                    if (DBG) {
-                        log("AttachedState: REQ_DISCONNECT apnRequest=" + apnRequest);
-                    }
-
-                    DctController.getInstance().releaseRequest(apnRequest);
-                    mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_CONNECT,
-                            PhoneConstants.APN_REQUEST_STARTED);
-
                     retVal = HANDLED;
                     break;
                 }
@@ -234,9 +335,6 @@
                         log("AttachedState: REQ_DISCONNECT_ALL" );
                     }
                     DctController.getInstance().releaseAllRequests(mId);
-                    mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_DISCONNECT_ALL,
-                            PhoneConstants.APN_REQUEST_STARTED);
-
                     transitionTo(mDetachingState);
                     retVal = HANDLED;
                     break;
@@ -264,12 +362,14 @@
     }
 
     private class DetachingState extends State {
+        private int mCurrentDisallowedSequence = 0;
+
         @Override
         public void enter() {
             if (DBG) log("DetachingState: enter");
             PhoneBase pb = (PhoneBase)((PhoneProxy)mPhone).getActivePhone();
-            pb.mCi.setDataAllowed(false, obtainMessage(
-                    DcSwitchAsyncChannel.EVENT_DATA_DETACHED));
+            pb.mCi.setDataAllowed(false, obtainMessage(EVENT_DATA_DISALLOWED,
+                    ++mCurrentDisallowedSequence, 0));
         }
 
         @Override
@@ -277,6 +377,17 @@
             boolean retVal;
 
             switch (msg.what) {
+                case DcSwitchAsyncChannel.REQ_CONNECT: {
+                    RequestInfo apnRequest = (RequestInfo)msg.obj;
+                    apnRequest.log("DcSwitchStateMachine.DetachingState: REQ_CONNECT");
+                    if (DBG) log("DetachingState: REQ_CONNECT, apnRequest=" + apnRequest);
+
+                    // can't process this now - wait until we return to idle
+                    deferMessage(msg);
+                    retVal = HANDLED;
+                    break;
+                }
+
                 case DcSwitchAsyncChannel.EVENT_DATA_DETACHED: {
                     if (DBG) {
                         log("DetachingState: EVENT_DATA_DETACHED");
@@ -285,13 +396,25 @@
                     retVal = HANDLED;
                     break;
                 }
-
+                case EVENT_DATA_DISALLOWED: {
+                    AsyncResult ar = (AsyncResult)msg.obj;
+                    if (mCurrentDisallowedSequence != msg.arg1) {
+                        loge("EVENT_DATA_DISALLOWED ignored arg1=" + msg.arg1 + ", seq=" +
+                                mCurrentDisallowedSequence);
+                    } else if (ar.exception != null) {
+                        // go back to attached as we still think we are.  Notifications
+                        // from the ServiceStateTracker will kick us out of attached when
+                        // appropriate.
+                        loge("EVENT_DATA_DISALLOWED failed, " + ar.exception);
+                        transitionTo(mAttachedState);
+                    }
+                    retVal = HANDLED;
+                    break;
+                }
                 case DcSwitchAsyncChannel.REQ_DISCONNECT_ALL: {
                     if (DBG) {
                         log("DetachingState: REQ_DISCONNECT_ALL, already detaching" );
                     }
-                    mAc.replyToMessage(msg, DcSwitchAsyncChannel.RSP_DISCONNECT_ALL,
-                            PhoneConstants.APN_REQUEST_STARTED);
                     retVal = HANDLED;
                     break;
                 }
@@ -350,6 +473,11 @@
                             DcSwitchAsyncChannel.RSP_IS_IDLE_OR_DETACHING_STATE, val ? 1 : 0);
                     break;
                 }
+                case DcSwitchAsyncChannel.EVENT_EMERGENCY_CALL_STARTED: {
+                    mPreEmergencyState = getCurrentState();
+                    transitionTo(mEmergencyState);
+                    break;
+                }
                 default:
                     if (DBG) {
                         log("DefaultState: shouldn't happen but ignore msg.what=0x" +
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
index 27e3ed8..0a453b0 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTracker.java
@@ -40,9 +40,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.Looper;
 import android.os.Message;
-import android.os.Messenger;
 import android.os.RegistrantList;
 import android.os.ServiceManager;
 import android.os.SystemClock;
@@ -58,7 +56,7 @@
 import android.telephony.gsm.GsmCellLocation;
 import android.text.TextUtils;
 import android.util.EventLog;
-import android.util.SparseArray;
+import android.util.LocalLog;
 import android.view.WindowManager;
 import android.telephony.Rlog;
 
@@ -85,8 +83,6 @@
 import java.util.Objects;
 import java.lang.StringBuilder;
 
-import android.provider.Settings;
-
 import com.android.internal.telephony.ServiceStateTracker;
 /**
  * {@hide}
@@ -257,18 +253,20 @@
     }
 
     @Override
-    public void incApnRefCount(String name) {
+    public void incApnRefCount(String name, LocalLog log) {
         ApnContext apnContext = mApnContexts.get(name);
+        log.log("DcTracker.incApnRefCount on " + name + " found " + apnContext);
         if (apnContext != null) {
-            apnContext.incRefCount();
+            apnContext.incRefCount(log);
         }
     }
 
     @Override
-    public void decApnRefCount(String name) {
+    public void decApnRefCount(String name, LocalLog log) {
         ApnContext apnContext = mApnContexts.get(name);
+        log.log("DcTracker.decApnRefCount on " + name + " found " + apnContext);
         if (apnContext != null) {
-            apnContext.decRefCount();
+            apnContext.decRefCount(log);
         }
     }
 
@@ -380,6 +378,14 @@
         boolean dataAllowed = isEmergencyApn || isDataAllowed();
         boolean possible = dataAllowed && apnTypePossible;
 
+        if ((apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DEFAULT)
+                    || apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IA))
+                && (mPhone.getServiceState().getRilDataRadioTechnology()
+                == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN)) {
+            log("Default data call activation not possible in iwlan.");
+            possible = false;
+        }
+
         if (VDBG) {
             log(String.format("isDataPossible(%s): possible=%b isDataAllowed=%b " +
                     "apnTypePossible=%b apnContextisEnabled=%b apnContextState()=%s",
@@ -646,7 +652,17 @@
     }
 
     private boolean isDataAllowed(ApnContext apnContext) {
-        return apnContext.isReady() && isDataAllowed();
+        //If RAT is iwlan then dont allow default/IA PDP at all.
+        //Rest of APN types can be evaluated for remaining conditions.
+        if ((apnContext.getApnType().equals(PhoneConstants.APN_TYPE_DEFAULT)
+                    || apnContext.getApnType().equals(PhoneConstants.APN_TYPE_IA))
+                && (mPhone.getServiceState().getRilDataRadioTechnology()
+                == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN)) {
+            log("Default data call activation not allowed in iwlan.");
+            return false;
+        } else {
+            return apnContext.isReady() && isDataAllowed();
+        }
     }
 
     //****** Called from ServiceStateTracker
@@ -679,7 +695,7 @@
             notifyOffApnsOfAvailability(Phone.REASON_DATA_ATTACHED);
         }
         if (mAutoAttachOnCreationConfig) {
-            mAutoAttachOnCreation = true;
+            mAutoAttachOnCreation.set(true);
         }
         setupDataOnConnectableApns(Phone.REASON_DATA_ATTACHED);
     }
@@ -693,11 +709,16 @@
 
         boolean attachedState = mAttached.get();
         boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
+        int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
+        if (radioTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN) {
+            desiredPowerState = true;
+        }
+
         IccRecords r = mIccRecords.get();
         boolean recordsLoaded = false;
         if (r != null) {
             recordsLoaded = r.getRecordsLoaded();
-            if (DBG) log("isDataAllowed getRecordsLoaded=" + recordsLoaded);
+            if (DBG && !recordsLoaded) log("isDataAllowed getRecordsLoaded=" + recordsLoaded);
         }
 
         //FIXME always attach
@@ -721,7 +742,7 @@
             state = mPhone.getCallTracker().getState();
         }
         boolean allowed =
-                    (attachedState || mAutoAttachOnCreation) &&
+                    (attachedState || mAutoAttachOnCreation.get()) &&
                     recordsLoaded &&
                     (state == PhoneConstants.State.IDLE ||
                      mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) &&
@@ -733,7 +754,7 @@
                     desiredPowerState;
         if (!allowed && DBG) {
             String reason = "";
-            if (!(attachedState || mAutoAttachOnCreation)) {
+            if (!(attachedState || mAutoAttachOnCreation.get())) {
                 reason += " - Attached= " + attachedState;
             }
             if (!recordsLoaded) reason += " - SIM not loaded";
@@ -770,17 +791,19 @@
 
     private void setupDataOnConnectableApns(String reason, RetryFailures retryFailures) {
         if (DBG) log("setupDataOnConnectableApns: " + reason);
-        ArrayList<ApnSetting> waitingApns = null;
 
         for (ApnContext apnContext : mPrioritySortedApnContexts) {
+            ArrayList<ApnSetting> waitingApns = null;
+
             if (DBG) log("setupDataOnConnectableApns: apnContext " + apnContext);
-            if (apnContext.getState() == DctConstants.State.FAILED) {
+            if (apnContext.getState() == DctConstants.State.FAILED
+                    || apnContext.getState() == DctConstants.State.RETRYING) {
                 if (retryFailures == RetryFailures.ALWAYS) {
-                    apnContext.setState(DctConstants.State.IDLE);
+                    apnContext.releaseDataConnection(reason);
                 } else if (apnContext.isConcurrentVoiceAndDataAllowed() == false &&
                          mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {
                     // RetryFailures.ONLY_ON_CHANGE - check if voice concurrency has changed
-                    apnContext.setState(DctConstants.State.IDLE);
+                    apnContext.releaseDataConnection(reason);
                 } else {
                     // RetryFailures.ONLY_ON_CHANGE - check if the apns have changed
                     int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
@@ -789,7 +812,7 @@
                         waitingApns = buildWaitingApns(apnContext.getApnType(), radioTech);
                         if (originalApns.size() != waitingApns.size() ||
                                 originalApns.containsAll(waitingApns) == false) {
-                            apnContext.setState(DctConstants.State.IDLE);
+                            apnContext.releaseDataConnection(reason);
                         }
                     }
                 }
@@ -812,6 +835,7 @@
                     " due to " + apnContext.getReason() + " apnContext=" + apnContext);
             log("trySetupData with mIsPsRestricted=" + mIsPsRestricted);
         }
+        apnContext.requestLog("trySetupData due to " + apnContext.getReason());
 
         if (mPhone.getSimulatedRadioControl() != null) {
             // Assume data is connected on the simulator
@@ -835,7 +859,9 @@
                 (isDataAllowed(apnContext) &&
                 getAnyDataEnabled(checkUserDataEnabled) && !isEmergency()))) {
             if (apnContext.getState() == DctConstants.State.FAILED) {
-                if (DBG) log("trySetupData: make a FAILED ApnContext IDLE so its reusable");
+                String str ="trySetupData: make a FAILED ApnContext IDLE so its reusable";
+                if (DBG) log(str);
+                apnContext.requestLog(str);
                 apnContext.setState(DctConstants.State.IDLE);
             }
             int radioTech = mPhone.getServiceState().getRilDataRadioTechnology();
@@ -847,7 +873,9 @@
                 if (waitingApns.isEmpty()) {
                     notifyNoData(DcFailCause.MISSING_UNKNOWN_APN, apnContext);
                     notifyOffApnsOfAvailability(apnContext.getReason());
-                    if (DBG) log("trySetupData: X No APN found retValue=false");
+                    String str = "trySetupData: X No APN found retValue=false";
+                    if (DBG) log(str);
+                    apnContext.requestLog(str);
                     return false;
                 } else {
                     apnContext.setWaitingApns(waitingApns);
@@ -873,7 +901,16 @@
                 mPhone.notifyDataConnectionFailed(apnContext.getReason(), apnContext.getApnType());
             }
             notifyOffApnsOfAvailability(apnContext.getReason());
-            if (DBG) log ("trySetupData: X apnContext not 'ready' retValue=false");
+            String str = "trySetupData: X apnContext not 'ready' retValue=false";
+            apnContext.requestLog(str);
+            if (DBG) {
+                log(str);
+                if (!apnContext.isConnectable()) log("apnContext.isConnectable = false");
+                if (!isDataAllowed(apnContext)) log("isDataAllowed = false");
+                if (!getAnyDataEnabled(checkUserDataEnabled)) {
+                    log("getAnyDataEnabled(" + checkUserDataEnabled + ") = false");
+                }
+            }
             return false;
         }
     }
@@ -969,10 +1006,10 @@
         }
 
         DcAsyncChannel dcac = apnContext.getDcAc();
-        if (DBG) {
-            log("cleanUpConnection: E tearDown=" + tearDown + " reason=" + apnContext.getReason() +
-                    " apnContext=" + apnContext);
-        }
+        String str = "cleanUpConnection: tearDown=" + tearDown + " reason=" +
+                apnContext.getReason();
+        if (DBG) log(str + " apnContext=" + apnContext);
+        apnContext.requestLog(str);
         if (tearDown) {
             if (apnContext.isDisconnected()) {
                 // The request is tearDown and but ApnContext is not connected.
@@ -980,10 +1017,9 @@
                 apnContext.setState(DctConstants.State.IDLE);
                 if (!apnContext.isReady()) {
                     if (dcac != null) {
-                        if (DBG) {
-                            log("cleanUpConnection: teardown, disconnected, !ready apnContext="
-                                    + apnContext);
-                        }
+                        str = "cleanUpConnection: teardown, disconnectd, !ready";
+                        if (DBG) log(str + " apnContext=" + apnContext);
+                        apnContext.requestLog(str);
                         dcac.tearDown(apnContext, "", null);
                     }
                     apnContext.setDataConnectionAc(null);
@@ -1007,10 +1043,9 @@
                                 disconnectAll = true;
                             }
                         }
-                        if (DBG) {
-                            log("cleanUpConnection: tearing down" + (disconnectAll ? " all" :"")
-                                    + "apnContext=" + apnContext);
-                        }
+                        str = "cleanUpConnection: tearing down" + (disconnectAll ? " all" : "");
+                        if (DBG) log(str + "apnContext=" + apnContext);
+                        apnContext.requestLog(str);
                         Message msg = obtainMessage(DctConstants.EVENT_DISCONNECT_DONE, apnContext);
                         if (disconnectAll) {
                             apnContext.getDcAc().tearDownAll(apnContext.getReason(), msg);
@@ -1025,6 +1060,7 @@
                     // apn is connected but no reference to dcac.
                     // Should not be happen, but reset the state in case.
                     apnContext.setState(DctConstants.State.IDLE);
+                    apnContext.requestLog("cleanUpConnection: connected, bug no DCAC");
                     mPhone.notifyDataConnection(apnContext.getReason(),
                                                 apnContext.getApnType());
                 }
@@ -1042,10 +1078,9 @@
         if (dcac != null) {
             cancelReconnectAlarm(apnContext);
         }
-        if (DBG) {
-            log("cleanUpConnection: X tearDown=" + tearDown + " reason=" + apnContext.getReason() +
-                    " apnContext=" + apnContext + " dcac=" + apnContext.getDcAc());
-        }
+        str = "cleanUpConnection: X tearDown=" + tearDown + " reason=" + apnContext.getReason();
+        if (DBG) log(str + " apnContext=" + apnContext + " dcac=" + apnContext.getDcAc());
+        apnContext.requestLog(str);
     }
 
     /**
@@ -1093,53 +1128,6 @@
         return result;
     }
 
-    private boolean imsiMatches(String imsiDB, String imsiSIM) {
-        // Note: imsiDB value has digit number or 'x' character for seperating USIM information
-        // for MVNO operator. And then digit number is matched at same order and 'x' character
-        // could replace by any digit number.
-        // ex) if imsiDB inserted '310260x10xxxxxx' for GG Operator,
-        //     that means first 6 digits, 8th and 9th digit
-        //     should be set in USIM for GG Operator.
-        int len = imsiDB.length();
-        int idxCompare = 0;
-
-        if (len <= 0) return false;
-        if (len > imsiSIM.length()) return false;
-
-        for (int idx=0; idx<len; idx++) {
-            char c = imsiDB.charAt(idx);
-            if ((c == 'x') || (c == 'X') || (c == imsiSIM.charAt(idx))) {
-                continue;
-            } else {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    @Override
-    protected boolean mvnoMatches(IccRecords r, String mvnoType, String mvnoMatchData) {
-        if (mvnoType.equalsIgnoreCase("spn")) {
-            if ((r.getServiceProviderName() != null) &&
-                    r.getServiceProviderName().equalsIgnoreCase(mvnoMatchData)) {
-                return true;
-            }
-        } else if (mvnoType.equalsIgnoreCase("imsi")) {
-            String imsiSIM = r.getIMSI();
-            if ((imsiSIM != null) && imsiMatches(mvnoMatchData, imsiSIM)) {
-                return true;
-            }
-        } else if (mvnoType.equalsIgnoreCase("gid")) {
-            String gid1 = r.getGid1();
-            int mvno_match_data_length = mvnoMatchData.length();
-            if ((gid1 != null) && (gid1.length() >= mvno_match_data_length) &&
-                    gid1.substring(0, mvno_match_data_length).equalsIgnoreCase(mvnoMatchData)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     @Override
     protected boolean isPermanentFail(DcFailCause dcFailCause) {
         return (dcFailCause.isPermanentFail() &&
@@ -1175,6 +1163,7 @@
                 cursor.getInt(cursor.getColumnIndexOrThrow(
                         Telephony.Carriers.CARRIER_ENABLED)) == 1,
                 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.BEARER)),
+                cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.BEARER_BITMASK)),
                 cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.PROFILE_ID)),
                 cursor.getInt(cursor.getColumnIndexOrThrow(
                         Telephony.Carriers.MODEM_COGNITIVE)) == 1,
@@ -1201,7 +1190,7 @@
                 }
 
                 if (apn.hasMvnoParams()) {
-                    if (r != null && mvnoMatches(r, apn.mvnoType, apn.mvnoMatchData)) {
+                    if (r != null && ApnSetting.mvnoMatches(r, apn.mvnoType, apn.mvnoMatchData)) {
                         mvnoApns.add(apn);
                     }
                 } else {
@@ -1248,6 +1237,7 @@
 
     private boolean setupData(ApnContext apnContext, int radioTech) {
         if (DBG) log("setupData: apnContext=" + apnContext);
+        apnContext.requestLog("setupData");
         ApnSetting apnSetting;
         DcAsyncChannel dcac = null;
 
@@ -1323,8 +1313,8 @@
         Message msg = obtainMessage();
         msg.what = DctConstants.EVENT_DATA_SETUP_COMPLETE;
         msg.obj = apnContext;
-        dcac.bringUp(apnContext, getInitialMaxRetry(), profileId, radioTech, mAutoAttachOnCreation,
-                msg);
+        dcac.bringUp(apnContext, getInitialMaxRetry(), profileId, radioTech,
+                mAutoAttachOnCreation.get(), msg);
 
         if (DBG) log("setupData: initing!");
         return true;
@@ -1544,11 +1534,12 @@
     private void applyNewState(ApnContext apnContext, boolean enabled, boolean met) {
         boolean cleanup = false;
         boolean trySetup = false;
-        if (DBG) {
-            log("applyNewState(" + apnContext.getApnType() + ", " + enabled +
-                    "(" + apnContext.isEnabled() + "), " + met + "(" +
-                    apnContext.getDependencyMet() +"))");
-        }
+        String str ="applyNewState(" + apnContext.getApnType() + ", " + enabled +
+                "(" + apnContext.isEnabled() + "), " + met + "(" +
+                apnContext.getDependencyMet() +"))";
+        if (DBG) log(str);
+        apnContext.requestLog(str);
+
         if (apnContext.isReady()) {
             cleanup = true;
             if (enabled && met) {
@@ -1560,6 +1551,7 @@
                     case DISCONNECTING:
                         // We're "READY" and active so just return
                         if (DBG) log("applyNewState: 'ready' so return");
+                        apnContext.requestLog("applyNewState state=" + state + ", so return");
                         return;
                     case IDLE:
                         // fall through: this is unexpected but if it happens cleanup and try setup
@@ -1607,7 +1599,10 @@
         apnContext.setEnabled(enabled);
         apnContext.setDependencyMet(met);
         if (cleanup) cleanUpConnection(true, apnContext);
-        if (trySetup) trySetupData(apnContext);
+        if (trySetup) {
+            apnContext.resetErrorCodeRetries();
+            trySetupData(apnContext);
+        }
     }
 
     private DcAsyncChannel checkForCompatibleConnectedApnContext(ApnContext apnContext) {
@@ -1625,7 +1620,6 @@
         ApnContext potentialApnCtx = null;
         for (ApnContext curApnCtx : mApnContexts.values()) {
             DcAsyncChannel curDcac = curApnCtx.getDcAc();
-            log("curDcac: " + curDcac);
             if (curDcac != null) {
                 ApnSetting apnSetting = curApnCtx.getApnSetting();
                 log("apnSetting: " + apnSetting);
@@ -2245,13 +2239,14 @@
         String operator = (r != null) ? r.getOperatorNumeric() : "";
         if (operator != null) {
             String selection = "numeric = '" + operator + "'";
+            String orderBy = "_id";
             // query only enabled apn.
             // carrier_enabled : 1 means enabled apn, 0 disabled apn.
             // selection += " and carrier_enabled = 1";
             if (DBG) log("createAllApnList: selection=" + selection);
 
             Cursor cursor = mPhone.getContext().getContentResolver().query(
-                    Telephony.Carriers.CONTENT_URI, null, selection, null, null);
+                    Telephony.Carriers.CONTENT_URI, null, selection, null, orderBy);
 
             if (cursor != null) {
                 if (cursor.getCount() > 0) {
@@ -2350,7 +2345,7 @@
                 xorEquals(first.proxy, second.proxy) &&
                 xorEquals(first.port, second.port) &&
                 first.carrierEnabled == second.carrierEnabled &&
-                first.bearer == second.bearer &&
+                first.bearerBitmask == second.bearerBitmask &&
                 first.profileId == second.profileId &&
                 Objects.equals(first.mvnoType, second.mvnoType) &&
                 Objects.equals(first.mvnoMatchData, second.mvnoMatchData) &&
@@ -2367,10 +2362,12 @@
     }
 
     private ApnSetting mergeApns(ApnSetting dest, ApnSetting src) {
+        int id = dest.id;
         ArrayList<String> resultTypes = new ArrayList<String>();
         resultTypes.addAll(Arrays.asList(dest.types));
         for (String srcType : src.types) {
             if (resultTypes.contains(srcType) == false) resultTypes.add(srcType);
+            if (srcType.equals(PhoneConstants.APN_TYPE_DEFAULT)) id = src.id;
         }
         String mmsc = (TextUtils.isEmpty(dest.mmsc) ? src.mmsc : dest.mmsc);
         String mmsProxy = (TextUtils.isEmpty(dest.mmsProxy) ? src.mmsProxy : dest.mmsProxy);
@@ -2380,11 +2377,13 @@
         String protocol = src.protocol.equals("IPV4V6") ? src.protocol : dest.protocol;
         String roamingProtocol = src.roamingProtocol.equals("IPV4V6") ? src.roamingProtocol :
                 dest.roamingProtocol;
+        int bearerBitmask = (dest.bearerBitmask == 0 || src.bearerBitmask == 0) ?
+                0 : (dest.bearerBitmask | src.bearerBitmask);
 
-        return new ApnSetting(dest.id, dest.numeric, dest.carrier, dest.apn,
+        return new ApnSetting(id, dest.numeric, dest.carrier, dest.apn,
                 proxy, port, mmsc, mmsProxy, mmsPort, dest.user, dest.password,
                 dest.authType, resultTypes.toArray(new String[0]), protocol,
-                roamingProtocol, dest.carrierEnabled, dest.bearer, dest.profileId,
+                roamingProtocol, dest.carrierEnabled, 0, bearerBitmask, dest.profileId,
                 (dest.modemCognitive || src.modemCognitive), dest.maxConns, dest.waitTime,
                 dest.maxConnsTime, dest.mtu, dest.mvnoType, dest.mvnoMatchData);
     }
@@ -2454,6 +2453,9 @@
             if (DBG) log("buildWaitingApns: usePreferred NotFoundException set to true");
             usePreferred = true;
         }
+        if (usePreferred) {
+            mPreferredApn = getPreferredApn();
+        }
         if (DBG) {
             log("buildWaitingApns: usePreferred=" + usePreferred
                     + " canSetPreferApn=" + mCanSetPreferApn
@@ -2469,7 +2471,7 @@
                         + mPreferredApn.numeric + ":" + mPreferredApn);
             }
             if (mPreferredApn.numeric.equals(operator)) {
-                if (mPreferredApn.bearer == 0 || mPreferredApn.bearer == radioTech) {
+                if (ServiceState.bitmaskHasTech(mPreferredApn.bearerBitmask, radioTech)) {
                     apnList.add(mPreferredApn);
                     if (DBG) log("buildWaitingApns: X added preferred apnList=" + apnList);
                     return apnList;
@@ -2487,26 +2489,23 @@
         if (mAllApnSettings != null) {
             if (DBG) log("buildWaitingApns: mAllApnSettings=" + mAllApnSettings);
             for (ApnSetting apn : mAllApnSettings) {
-                if (DBG) log("buildWaitingApns: apn=" + apn);
                 if (apn.canHandleType(requestedApnType)) {
-                    if (apn.bearer == 0 || apn.bearer == radioTech) {
-                        if (DBG) log("buildWaitingApns: adding apn=" + apn.toString());
+                    if (ServiceState.bitmaskHasTech(apn.bearerBitmask, radioTech)) {
+                        if (DBG) log("buildWaitingApns: adding apn=" + apn);
                         apnList.add(apn);
                     } else {
                         if (DBG) {
-                            log("buildWaitingApns: bearer:" + apn.bearer + " != "
-                                    + "radioTech:" + radioTech);
+                            log("buildWaitingApns: bearerBitmask:" + apn.bearerBitmask + " does " +
+                                    "not include radioTech:" + radioTech);
                         }
                     }
-                } else {
-                if (DBG) {
+                } else if (DBG) {
                     log("buildWaitingApns: couldn't handle requesedApnType="
                             + requestedApnType);
                 }
             }
-            }
         } else {
-            loge("mAllApnSettings is empty!");
+            loge("mAllApnSettings is null!");
         }
         if (DBG) log("buildWaitingApns: X apnList=" + apnList);
         return apnList;
@@ -2543,8 +2542,8 @@
     }
 
     private ApnSetting getPreferredApn() {
-        if (mAllApnSettings.isEmpty()) {
-            log("getPreferredApn: X not found mAllApnSettings.isEmpty");
+        if (mAllApnSettings == null || mAllApnSettings.isEmpty()) {
+            log("getPreferredApn: mAllApnSettings is " + ((mAllApnSettings == null)?"null":"empty"));
             return null;
         }
 
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTrackerBase.java b/src/java/com/android/internal/telephony/dataconnection/DcTrackerBase.java
index a5a0e64..5c0226e 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTrackerBase.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTrackerBase.java
@@ -42,11 +42,13 @@
 import android.preference.PreferenceManager;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
+import android.telephony.ServiceState;
 import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.EventLog;
+import android.util.LocalLog;
 import android.telephony.Rlog;
 
 import com.android.internal.R;
@@ -69,6 +71,7 @@
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.PriorityQueue;
@@ -79,7 +82,7 @@
 public abstract class DcTrackerBase extends Handler {
     protected static final boolean DBG = true;
     protected static final boolean VDBG = false; // STOPSHIP if true
-    protected static final boolean VDBG_STALL = true; // STOPSHIP if true
+    protected static final boolean VDBG_STALL = false; // STOPSHIP if true
     protected static final boolean RADIO_TESTS = false;
 
     static boolean mIsCleanupRequired = false;
@@ -233,7 +236,7 @@
 
     // When false we will not auto attach and manually attaching is required.
     protected boolean mAutoAttachOnCreationConfig = false;
-    protected boolean mAutoAttachOnCreation = false;
+    protected AtomicBoolean mAutoAttachOnCreation = new AtomicBoolean(false);
 
     // State of screen
     // (TODO: Reconsider tying directly to screen, maybe this is
@@ -615,7 +618,7 @@
         }
 
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mPhone.getContext());
-        mAutoAttachOnCreation = sp.getBoolean(PhoneBase.DATA_DISABLED_ON_BOOT_KEY, false);
+        mAutoAttachOnCreation.set(sp.getBoolean(PhoneBase.DATA_DISABLED_ON_BOOT_KEY, false));
 
         mSubscriptionManager = SubscriptionManager.from(mPhone.getContext());
         mSubscriptionManager
@@ -660,13 +663,9 @@
         mPhone.notifyDataActivity();
     }
 
-    public void incApnRefCount(String name) {
+    abstract public void incApnRefCount(String name, LocalLog log);
 
-    }
-
-    public void decApnRefCount(String name) {
-
-    }
+    abstract public void decApnRefCount(String name, LocalLog log);
 
     public boolean isApnSupported(String name) {
         return false;
@@ -693,21 +692,18 @@
             log("fetchDunApn: net.tethering.noprovisioning=true ret: null");
             return null;
         }
-        int bearer = -1;
+        int bearer = mPhone.getServiceState().getRilDataRadioTechnology();
         ApnSetting retDunSetting = null;
         String apnData = Settings.Global.getString(mResolver, Settings.Global.TETHER_DUN_APN);
         List<ApnSetting> dunSettings = ApnSetting.arrayFromString(apnData);
         IccRecords r = mIccRecords.get();
         for (ApnSetting dunSetting : dunSettings) {
             String operator = (r != null) ? r.getOperatorNumeric() : "";
-            if (dunSetting.bearer != 0) {
-                if (bearer == -1) bearer = mPhone.getServiceState().getRilDataRadioTechnology();
-                if (dunSetting.bearer != bearer) continue;
-            }
+            if (!ServiceState.bitmaskHasTech(dunSetting.bearerBitmask, bearer)) continue;
             if (dunSetting.numeric.equals(operator)) {
                 if (dunSetting.hasMvnoParams()) {
-                    if (r != null &&
-                            mvnoMatches(r, dunSetting.mvnoType, dunSetting.mvnoMatchData)) {
+                    if (r != null && ApnSetting.mvnoMatches(r, dunSetting.mvnoType,
+                            dunSetting.mvnoMatchData)) {
                         if (VDBG) {
                             log("fetchDunApn: global TETHER_DUN_APN dunSetting=" + dunSetting);
                         }
@@ -725,15 +721,13 @@
         for (String apn : apnArrayData) {
             ApnSetting dunSetting = ApnSetting.fromString(apn);
             if (dunSetting != null) {
-                if (dunSetting.bearer != 0) {
-                    if (bearer == -1) bearer = mPhone.getServiceState().getRilDataRadioTechnology();
-                    if (dunSetting.bearer != bearer) continue;
-                }
+                if (!ServiceState.bitmaskHasTech(dunSetting.bearerBitmask, bearer)) continue;
                 if (dunSetting.hasMvnoParams()) {
-                    if (r != null &&
-                            mvnoMatches(r, dunSetting.mvnoType, dunSetting.mvnoMatchData)) {
-                        if (VDBG) log("fetchDunApn: config_tether_apndata mvno dunSetting="
-                                + dunSetting);
+                    if (r != null && ApnSetting.mvnoMatches(r, dunSetting.mvnoType,
+                            dunSetting.mvnoMatchData)) {
+                        if (VDBG) {
+                            log("fetchDunApn: config_tether_apndata mvno dunSetting=" + dunSetting);
+                        }
                         return dunSetting;
                     }
                 } else {
@@ -894,7 +888,6 @@
     public abstract void setDataAllowed(boolean enable, Message response);
     public abstract String[] getPcscfAddress(String apnType);
     public abstract void setImsRegistrationState(boolean registered);
-    protected abstract boolean mvnoMatches(IccRecords r, String mvno_type, String mvno_match_data);
     protected abstract boolean isPermanentFail(DcFailCause dcFailCause);
 
     @Override
@@ -1310,58 +1303,7 @@
         sendMessage(msg);
     }
 
-    protected void onEnableApn(int apnId, int enabled) {
-        if (DBG) {
-            log("EVENT_APN_ENABLE_REQUEST apnId=" + apnId + ", apnType=" + apnIdToType(apnId) +
-                    ", enabled=" + enabled + ", dataEnabled = " + mDataEnabled[apnId] +
-                    ", enabledCount = " + mEnabledCount + ", isApnTypeActive = " +
-                    isApnTypeActive(apnIdToType(apnId)));
-        }
-        if (enabled == DctConstants.ENABLED) {
-            synchronized (this) {
-                if (!mDataEnabled[apnId]) {
-                    mDataEnabled[apnId] = true;
-                    mEnabledCount++;
-                }
-            }
-            String type = apnIdToType(apnId);
-            if (!isApnTypeActive(type)) {
-                mRequestedApnType = type;
-                onEnableNewApn();
-            } else {
-                notifyApnIdUpToCurrent(Phone.REASON_APN_SWITCHED, apnId);
-            }
-        } else {
-            // disable
-            boolean didDisable = false;
-            synchronized (this) {
-                if (mDataEnabled[apnId]) {
-                    mDataEnabled[apnId] = false;
-                    mEnabledCount--;
-                    didDisable = true;
-                }
-            }
-            if (didDisable) {
-                if ((mEnabledCount == 0) || (apnId == DctConstants.APN_DUN_ID)) {
-                    mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT;
-                    onCleanUpConnection(true, apnId, Phone.REASON_DATA_DISABLED);
-                }
-
-                // send the disconnect msg manually, since the normal route wont send
-                // it (it's not enabled)
-                notifyApnIdDisconnected(Phone.REASON_DATA_DISABLED, apnId);
-                if (mDataEnabled[DctConstants.APN_DEFAULT_ID] == true
-                        && !isApnTypeActive(PhoneConstants.APN_TYPE_DEFAULT)) {
-                    // TODO - this is an ugly way to restore the default conn - should be done
-                    // by a real contention manager and policy that disconnects the lower pri
-                    // stuff as enable requests come in and pops them back on as we disable back
-                    // down to the lower pri stuff
-                    mRequestedApnType = PhoneConstants.APN_TYPE_DEFAULT;
-                    onEnableNewApn();
-                }
-            }
-        }
-    }
+    abstract void onEnableApn(int apnId, int enabled);
 
     /**
      * Called when we switch APNs.
@@ -1973,6 +1915,10 @@
         sendMessage(msg);
     }
 
+    public boolean getAutoAttachOnCreation() {
+        return mAutoAttachOnCreation.get();
+    }
+
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("DcTrackerBase:");
         pw.println(" RADIO_TESTS=" + RADIO_TESTS);
@@ -2002,7 +1948,7 @@
         pw.println(" mIsWifiConnected=" + mIsWifiConnected);
         pw.println(" mReconnectIntent=" + mReconnectIntent);
         pw.println(" mCidActive=" + mCidActive);
-        pw.println(" mAutoAttachOnCreation=" + mAutoAttachOnCreation);
+        pw.println(" mAutoAttachOnCreation=" + mAutoAttachOnCreation.get());
         pw.println(" mIsScreenOn=" + mIsScreenOn);
         pw.println(" mUniqueIdGenerator=" + mUniqueIdGenerator);
         pw.flush();
diff --git a/src/java/com/android/internal/telephony/dataconnection/DctController.java b/src/java/com/android/internal/telephony/dataconnection/DctController.java
index 4fce2ec..fdeb77e 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DctController.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DctController.java
@@ -22,6 +22,7 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkFactory;
 import android.net.NetworkRequest;
+import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -30,6 +31,7 @@
 import android.telephony.Rlog;
 import android.telephony.SubscriptionManager;
 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
+import android.util.LocalLog;
 import android.util.SparseArray;
 
 import com.android.internal.telephony.Phone;
@@ -39,9 +41,11 @@
 import com.android.internal.telephony.SubscriptionController;
 import com.android.internal.telephony.dataconnection.DcSwitchAsyncChannel.RequestInfo;
 import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.IndentingPrintWriter;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.ArrayDeque;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map.Entry;
@@ -55,9 +59,13 @@
     private static final int EVENT_EXECUTE_ALL_REQUESTS = 102;
     private static final int EVENT_RELEASE_REQUEST = 103;
     private static final int EVENT_RELEASE_ALL_REQUESTS = 104;
+    private static final int EVENT_RETRY_ATTACH = 105;
+    private static final int EVENT_SETTINGS_CHANGED = 106;
+    private static final int EVENT_SUBSCRIPTIONS_CHANGED = 107;
 
     private static final int EVENT_DATA_ATTACHED = 500;
     private static final int EVENT_DATA_DETACHED = 600;
+    private static final int EVENT_EMERGENCY_CALL_TOGGLED = 700;
 
     private static DctController sDctController;
 
@@ -83,7 +91,7 @@
             new OnSubscriptionsChangedListener() {
         @Override
         public void onSubscriptionsChanged() {
-            onSubInfoReady();
+            DctController.this.obtainMessage(EVENT_SUBSCRIPTIONS_CHANGED).sendToTarget();
         }
     };
 
@@ -91,7 +99,7 @@
         @Override
         public void onChange(boolean selfChange) {
             logd("Settings change");
-            onSettingsChange();
+            DctController.this.obtainMessage(EVENT_SETTINGS_CHANGED).sendToTarget();
         }
     };
 
@@ -122,6 +130,8 @@
                    EVENT_DATA_ATTACHED + index, null);
         phoneBase.getServiceStateTracker().registerForDataConnectionDetached(mRspHandler,
                    EVENT_DATA_DETACHED + index, null);
+        phoneBase.registerForEmergencyCallToggle(mRspHandler,
+                EVENT_EMERGENCY_CALL_TOGGLED + index, null);
 
         ConnectivityManager cm = (ConnectivityManager)mPhones[index].getContext()
             .getSystemService(Context.CONNECTIVITY_SERVICE);
@@ -160,7 +170,14 @@
     private Handler mRspHandler = new Handler() {
         @Override
         public void handleMessage(Message msg){
-            if (msg.what >= EVENT_DATA_DETACHED) {
+            if (msg.what >= EVENT_EMERGENCY_CALL_TOGGLED) {
+                logd("EVENT_PHONE" + (msg.what - EVENT_EMERGENCY_CALL_TOGGLED + 1)
+                        + "_EMERGENCY_CALL_END.");
+                AsyncResult ar = (AsyncResult) msg.obj;
+                Integer toggle = (Integer) ar.result;
+                mDcSwitchAsyncChannel[msg.what - EVENT_EMERGENCY_CALL_TOGGLED].
+                        notifyEmergencyCallToggled(toggle.intValue());
+            } else if (msg.what >= EVENT_DATA_DETACHED) {
                 logd("EVENT_PHONE" + (msg.what - EVENT_DATA_DETACHED + 1)
                         + "_DATA_DETACH.");
                 mDcSwitchAsyncChannel[msg.what - EVENT_DATA_DETACHED].notifyDataDetached();
@@ -276,16 +293,26 @@
             case EVENT_RELEASE_ALL_REQUESTS:
                 onReleaseAllRequests(msg.arg1);
                 break;
+            case EVENT_RETRY_ATTACH:
+                onRetryAttach(msg.arg1);
+                break;
+            case EVENT_SETTINGS_CHANGED:
+                onSettingsChanged();
+                break;
+            case EVENT_SUBSCRIPTIONS_CHANGED:
+                onSubInfoReady();
+                break;
             default:
                 loge("Un-handled message [" + msg.what + "]");
         }
     }
 
-    private int requestNetwork(NetworkRequest request, int priority) {
+    private int requestNetwork(NetworkRequest request, int priority, LocalLog l, int phoneId) {
         logd("requestNetwork request=" + request
                 + ", priority=" + priority);
+        l.log("Dctc.requestNetwork, priority=" + priority);
 
-        RequestInfo requestInfo = new RequestInfo(request, priority);
+        RequestInfo requestInfo = new RequestInfo(request, priority, l, phoneId);
         mRequestInfos.put(request.requestId, requestInfo);
         processRequests();
 
@@ -295,6 +322,7 @@
     private int releaseNetwork(NetworkRequest request) {
         RequestInfo requestInfo = mRequestInfos.get(request.requestId);
         logd("releaseNetwork request=" + request + ", requestInfo=" + requestInfo);
+        if (requestInfo != null) requestInfo.log("DctController.releaseNetwork");
 
         mRequestInfos.remove(request.requestId);
         releaseRequest(requestInfo);
@@ -327,6 +355,11 @@
         sendMessage(obtainMessage(EVENT_RELEASE_ALL_REQUESTS, phoneId, 0));
     }
 
+    public void retryAttach(int phoneId) {
+        logd("retryAttach, phone:" + phoneId);
+        sendMessage(obtainMessage(EVENT_RETRY_ATTACH, phoneId, 0));
+    }
+
     private void onProcessRequest() {
         //process all requests
         //1. Check all requests and find subscription of the top priority
@@ -353,24 +386,25 @@
             Iterator<Integer> iterator = mRequestInfos.keySet().iterator();
             while (iterator.hasNext()) {
                 RequestInfo requestInfo = mRequestInfos.get(iterator.next());
-                if (getRequestPhoneId(requestInfo.request) == phoneId && !requestInfo.executed) {
-                    mDcSwitchAsyncChannel[phoneId].connectSync(requestInfo);
+                if (requestInfo.phoneId == phoneId && !requestInfo.executed) {
+                    mDcSwitchAsyncChannel[phoneId].connect(requestInfo);
                 }
             }
         } else {
-            mDcSwitchAsyncChannel[activePhoneId].disconnectAllSync();
+            mDcSwitchAsyncChannel[activePhoneId].disconnectAll();
         }
     }
 
     private void onExecuteRequest(RequestInfo requestInfo) {
-        logd("onExecuteRequest request=" + requestInfo);
-        if (!requestInfo.executed) {
+        if (!requestInfo.executed && mRequestInfos.containsKey(requestInfo.request.requestId)) {
+            logd("onExecuteRequest request=" + requestInfo);
+            requestInfo.log("DctController.onExecuteRequest - executed=" + requestInfo.executed);
             requestInfo.executed = true;
             String apn = apnForNetworkRequest(requestInfo.request);
-            int phoneId = getRequestPhoneId(requestInfo.request);
+            int phoneId = requestInfo.phoneId;
             PhoneBase phoneBase = (PhoneBase)mPhones[phoneId].getActivePhone();
             DcTrackerBase dcTracker = phoneBase.mDcTracker;
-            dcTracker.incApnRefCount(apn);
+            dcTracker.incApnRefCount(apn, requestInfo.getLog());
         }
     }
 
@@ -379,7 +413,7 @@
         Iterator<Integer> iterator = mRequestInfos.keySet().iterator();
         while (iterator.hasNext()) {
             RequestInfo requestInfo = mRequestInfos.get(iterator.next());
-            if (getRequestPhoneId(requestInfo.request) == phoneId) {
+            if (requestInfo.phoneId == phoneId) {
                 onExecuteRequest(requestInfo);
             }
         }
@@ -387,13 +421,16 @@
 
     private void onReleaseRequest(RequestInfo requestInfo) {
         logd("onReleaseRequest request=" + requestInfo);
-        if (requestInfo != null && requestInfo.executed) {
-            String apn = apnForNetworkRequest(requestInfo.request);
-            int phoneId = getRequestPhoneId(requestInfo.request);
-            PhoneBase phoneBase = (PhoneBase)mPhones[phoneId].getActivePhone();
-            DcTrackerBase dcTracker = phoneBase.mDcTracker;
-            dcTracker.decApnRefCount(apn);
-            requestInfo.executed = false;
+        if (requestInfo != null) {
+            requestInfo.log("DctController.onReleaseRequest");
+            if (requestInfo.executed) {
+                String apn = apnForNetworkRequest(requestInfo.request);
+                int phoneId = requestInfo.phoneId;
+                PhoneBase phoneBase = (PhoneBase)mPhones[phoneId].getActivePhone();
+                DcTrackerBase dcTracker = phoneBase.mDcTracker;
+                dcTracker.decApnRefCount(apn, requestInfo.getLog());
+                requestInfo.executed = false;
+            }
         }
     }
 
@@ -402,13 +439,22 @@
         Iterator<Integer> iterator = mRequestInfos.keySet().iterator();
         while (iterator.hasNext()) {
             RequestInfo requestInfo = mRequestInfos.get(iterator.next());
-            if (getRequestPhoneId(requestInfo.request) == phoneId) {
+            if (requestInfo.phoneId == phoneId) {
                 onReleaseRequest(requestInfo);
             }
         }
     }
 
-    private void onSettingsChange() {
+    private void onRetryAttach(int phoneId) {
+        final int topPriPhone = getTopPriorityRequestPhoneId();
+        logd("onRetryAttach phoneId=" + phoneId + " topPri phone = " + topPriPhone);
+
+        if (phoneId != -1 && phoneId == topPriPhone) {
+            mDcSwitchAsyncChannel[phoneId].retryConnect();
+        }
+    }
+
+    private void onSettingsChanged() {
         //Sub Selection
         long dataSubId = mSubController.getDefaultDataSubId();
 
@@ -437,10 +483,11 @@
                         String apn = apnForNetworkRequest(requestInfo.request);
                         logd("[setDataSubId] activePhoneId:" + activePhoneId + ", subId =" +
                                 dataSubId);
+                        requestInfo.log("DctController.onSettingsChange releasing request");
                         PhoneBase phoneBase =
                                 (PhoneBase)mPhones[activePhoneId].getActivePhone();
                         DcTrackerBase dcTracker = phoneBase.mDcTracker;
-                        dcTracker.decApnRefCount(apn);
+                        dcTracker.decApnRefCount(apn, requestInfo.getLog());
                         requestInfo.executed = false;
                     }
                 }
@@ -467,7 +514,7 @@
             while (iterator.hasNext()) {
                 RequestInfo requestInfo = mRequestInfos.get(iterator.next());
                 logd("selectExecPhone requestInfo = " + requestInfo);
-                if (getRequestPhoneId(requestInfo.request) == i &&
+                if (requestInfo.phoneId == i &&
                         priority < requestInfo.priority) {
                     priority = requestInfo.priority;
                     retRequestInfo = requestInfo;
@@ -477,6 +524,11 @@
 
         if (retRequestInfo != null) {
             phoneId = getRequestPhoneId(retRequestInfo.request);
+        } else {
+            int defaultDds = mSubController.getDefaultDataSubId();
+            phoneId = mSubController.getPhoneId(defaultDds);
+            logd("getTopPriorityRequestPhoneId: RequestInfo list is empty, " +
+                    "use Dds sub phone id");
         }
 
         logd("getTopPriorityRequestPhoneId = " + phoneId
@@ -562,10 +614,14 @@
         }
         if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_EIMS)) {
             if (name != null) error = true;
-            name = null;
-            loge("EIMS APN type not yet supported");
+            name = PhoneConstants.APN_TYPE_EMERGENCY;
+            type = ConnectivityManager.TYPE_MOBILE_EMERGENCY;
         }
         if (error) {
+            // TODO: If this error condition is removed, the framework's handling of
+            // NET_CAPABILITY_NOT_RESTRICTED will need to be updated so requests for
+            // say FOTA and INTERNET are marked as restricted.  This is not how
+            // NetworkCapabilities.maybeMarkCapabilitiesRestricted currently works.
             loge("Multiple apn types specified in request - result is unspecified!");
         }
         if (type == -1 || name == null) {
@@ -605,6 +661,21 @@
         private final SparseArray<NetworkRequest> mPendingReq = new SparseArray<NetworkRequest>();
         private Phone mPhone;
 
+        private class RequestLogger {
+            public NetworkRequest request;
+            public LocalLog log;
+
+            public RequestLogger(NetworkRequest r, LocalLog log) {
+                request = r;
+                this.log = log;
+            }
+        }
+
+        private static final int MAX_REQUESTS_LOGGED = 20;
+        private static final int MAX_LOG_LINES_PER_REQUEST = 50;
+
+        private ArrayDeque<RequestLogger> mRequestLogs = new ArrayDeque<RequestLogger>();
+
         public TelephonyNetworkFactory(Looper l, Context c, String TAG, Phone phone,
                 NetworkCapabilities nc) {
             super(l, c, TAG, nc);
@@ -612,53 +683,78 @@
             log("NetworkCapabilities: " + nc);
         }
 
+        public LocalLog requestLog(int requestId, String l) {
+            synchronized(mRequestLogs) {
+                for (RequestLogger r : mRequestLogs) {
+                    if (r.request.requestId == requestId) {
+                        r.log.log(l);
+                        return r.log;
+                    }
+                }
+            }
+            return null;
+        }
+
+        private LocalLog addLogger(NetworkRequest request) {
+            synchronized(mRequestLogs) {
+                for (RequestLogger r : mRequestLogs) {
+                    if (r.request.requestId == request.requestId) {
+                        return r.log;
+                    }
+                }
+                LocalLog l = new LocalLog(MAX_LOG_LINES_PER_REQUEST);
+                RequestLogger logger = new RequestLogger(request, l);
+                while (mRequestLogs.size() >= MAX_REQUESTS_LOGGED) {
+                    mRequestLogs.removeFirst();
+                }
+                mRequestLogs.addLast(logger);
+                return l;
+            }
+        }
+
         @Override
         protected void needNetworkFor(NetworkRequest networkRequest, int score) {
             // figure out the apn type and enable it
             log("Cellular needs Network for " + networkRequest);
 
-            if (!SubscriptionManager.isUsableSubIdValue(mPhone.getSubId())) {
-                log("Sub Info has not been ready, pending request.");
+            final LocalLog l = addLogger(networkRequest);
+
+            if (!SubscriptionManager.isUsableSubIdValue(mPhone.getSubId()) ||
+                    getRequestPhoneId(networkRequest) != mPhone.getPhoneId()) {
+                final String str = "Request not useable, pending request.";
+                log(str);
+                l.log(str);
                 mPendingReq.put(networkRequest.requestId, networkRequest);
                 return;
             }
 
-            if (getRequestPhoneId(networkRequest) == mPhone.getPhoneId()) {
-                DcTrackerBase dcTracker =((PhoneBase)mPhone).mDcTracker;
-                String apn = apnForNetworkRequest(networkRequest);
-                if (dcTracker.isApnSupported(apn)) {
-                    requestNetwork(networkRequest, dcTracker.getApnPriority(apn));
-                } else {
-                    log("Unsupported APN");
-                }
+            DcTrackerBase dcTracker =((PhoneBase)mPhone).mDcTracker;
+            String apn = apnForNetworkRequest(networkRequest);
+            if (dcTracker.isApnSupported(apn)) {
+                requestNetwork(networkRequest, dcTracker.getApnPriority(apn), l,
+                        mPhone.getPhoneId());
             } else {
-                log("Request not send, put to pending");
-                mPendingReq.put(networkRequest.requestId, networkRequest);
+                final String str = "Unsupported APN";
+                log(str);
+                l.log(str);
             }
         }
 
         @Override
         protected void releaseNetworkFor(NetworkRequest networkRequest) {
-            log("Cellular releasing Network for " + networkRequest);
+            String str = "Cellular releasing Network for ";
+            log(str + networkRequest);
+            final LocalLog l = requestLog(networkRequest.requestId, str);
 
-            if (!SubscriptionManager.isUsableSubIdValue(mPhone.getSubId())) {
-                log("Sub Info has not been ready, remove request.");
+            if (mPendingReq.get(networkRequest.requestId) != null) {
+                str = "Sub Info has not been ready, remove request.";
+                log(str);
+                if (l != null) l.log(str);
                 mPendingReq.remove(networkRequest.requestId);
                 return;
             }
 
-            if (getRequestPhoneId(networkRequest) == mPhone.getPhoneId()) {
-                DcTrackerBase dcTracker =((PhoneBase)mPhone).mDcTracker;
-                String apn = apnForNetworkRequest(networkRequest);
-                if (dcTracker.isApnSupported(apn)) {
-                    releaseNetwork(networkRequest);
-                } else {
-                    log("Unsupported APN");
-                }
-
-            } else {
-                log("Request not release");
-            }
+            releaseNetwork(networkRequest);
         }
 
         @Override
@@ -678,6 +774,33 @@
                 needNetworkFor(request, 0);
             }
         }
+
+        @Override
+        public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+            super.dump(fd, writer, args);
+            final IndentingPrintWriter pw = new IndentingPrintWriter(writer, "  ");
+            pw.increaseIndent();
+            pw.println("Pending Requests:");
+            pw.increaseIndent();
+            for (int i = 0; i < mPendingReq.size(); i++) {
+                NetworkRequest request = mPendingReq.valueAt(i);
+                pw.println(request);
+            }
+            pw.decreaseIndent();
+
+            pw.println("Request History:");
+            pw.increaseIndent();
+            synchronized(mRequestLogs) {
+                for (RequestLogger r : mRequestLogs) {
+                    pw.println(r.request);
+                    pw.increaseIndent();
+                    r.log.dump(fd, pw, args);
+                    pw.decreaseIndent();
+                }
+            }
+            pw.decreaseIndent();
+            pw.decreaseIndent();
+        }
     }
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
@@ -704,7 +827,7 @@
         pw.flush();
         pw.println("TelephonyNetworkFactories:");
         for (NetworkFactory tnf : mNetworkFactory) {
-            pw.println("  " + tnf);
+            tnf.dump(fd, pw, args);
         }
         pw.flush();
         pw.println("++++++++++++++++++++++++++++++++");
diff --git a/src/java/com/android/internal/telephony/gsm/GSMPhone.java b/src/java/com/android/internal/telephony/gsm/GSMPhone.java
index 70636ce..b861095 100755
--- a/src/java/com/android/internal/telephony/gsm/GSMPhone.java
+++ b/src/java/com/android/internal/telephony/gsm/GSMPhone.java
@@ -22,11 +22,11 @@
 import android.database.SQLException;
 import android.net.Uri;
 import android.os.AsyncResult;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
 import android.os.Registrant;
 import android.os.RegistrantList;
-import android.os.SystemProperties;
 import android.preference.PreferenceManager;
 import android.provider.Telephony;
 import android.telecom.VideoProfile;
@@ -36,13 +36,13 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 
-import com.android.ims.ImsManager;
 import com.android.internal.telephony.CallTracker;
 
 import android.text.TextUtils;
 import android.telephony.Rlog;
 import android.util.Log;
 
+import com.android.ims.ImsManager;
 import static com.android.internal.telephony.CommandsInterface.CF_ACTION_DISABLE;
 import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE;
 import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ERASURE;
@@ -63,14 +63,12 @@
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.IccPhoneBookInterfaceManager;
 import com.android.internal.telephony.MmiCode;
-import com.android.internal.telephony.OperatorInfo;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneBase;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.PhoneNotifier;
 import com.android.internal.telephony.PhoneProxy;
 import com.android.internal.telephony.PhoneSubInfo;
-import com.android.internal.telephony.TelephonyProperties;
 import com.android.internal.telephony.UUSInfo;
 import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.test.SimulatedRadioControl;
@@ -797,39 +795,47 @@
     @Override
     public Connection
     dial(String dialString, int videoState) throws CallStateException {
-        return dial(dialString, null, videoState);
+        return dial(dialString, null, videoState, null);
     }
 
     @Override
     public Connection
-    dial (String dialString, UUSInfo uusInfo, int videoState) throws CallStateException {
+    dial (String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
+            throws CallStateException {
+        boolean isEmergency = PhoneNumberUtils.isEmergencyNumber(dialString);
         ImsPhone imsPhone = mImsPhone;
 
-        boolean imsUseEnabled =
-                ImsManager.isVolteEnabledByPlatform(mContext) &&
-                ImsManager.isEnhanced4gLteModeSettingEnabledByUser(mContext) &&
-                ImsManager.isNonTtyOrTtyOnVolteEnabled(mContext);
-        if (!imsUseEnabled) {
-            Rlog.w(LOG_TAG, "IMS is disabled: forced to CS");
-        }
+        boolean imsUseEnabled = isImsUseEnabled()
+                 && imsPhone != null
+                 && (imsPhone.isVolteEnabled() || imsPhone.isVowifiEnabled())
+                 && (imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE);
+
+        boolean useImsForEmergency = ImsManager.isVolteEnabledByPlatform(mContext)
+                && imsPhone != null
+                && isEmergency
+                &&  mContext.getResources().getBoolean(
+                        com.android.internal.R.bool.useImsAlwaysForEmergencyCall)
+                && ImsManager.isNonTtyOrTtyOnVolteEnabled(mContext)
+                && (imsPhone.getServiceState().getState() != ServiceState.STATE_POWER_OFF);
 
         if (LOCAL_DEBUG) {
-            Rlog.d(LOG_TAG, "imsUseEnabled=" + imsUseEnabled + ", imsPhone=" + imsPhone
+            Rlog.d(LOG_TAG, "imsUseEnabled=" + imsUseEnabled
+                    + ", useImsForEmergency=" + useImsForEmergency
+                    + ", imsPhone=" + imsPhone
                     + ", imsPhone.isVolteEnabled()="
                     + ((imsPhone != null) ? imsPhone.isVolteEnabled() : "N/A")
+                    + ", imsPhone.isVowifiEnabled()="
+                    + ((imsPhone != null) ? imsPhone.isVowifiEnabled() : "N/A")
                     + ", imsPhone.getServiceState().getState()="
                     + ((imsPhone != null) ? imsPhone.getServiceState().getState() : "N/A"));
         }
 
-        if (imsUseEnabled && imsPhone != null && imsPhone.isVolteEnabled()
-                && ((imsPhone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE
-                && !PhoneNumberUtils.isEmergencyNumber(dialString))
-                || (PhoneNumberUtils.isEmergencyNumber(dialString)
-                && mContext.getResources().getBoolean(
-                        com.android.internal.R.bool.useImsAlwaysForEmergencyCall))) ) {
+        ImsPhone.checkWfcWifiOnlyModeBeforeDial(mImsPhone, mContext);
+
+        if (imsUseEnabled || useImsForEmergency) {
             try {
                 if (LOCAL_DEBUG) Rlog.d(LOG_TAG, "Trying IMS PS call");
-                return imsPhone.dial(dialString, videoState);
+                return imsPhone.dial(dialString, uusInfo, videoState, intentExtras);
             } catch (CallStateException e) {
                 if (LOCAL_DEBUG) Rlog.d(LOG_TAG, "IMS PS call exception " + e +
                         "imsUseEnabled =" + imsUseEnabled + ", imsPhone =" + imsPhone);
@@ -841,13 +847,17 @@
             }
         }
 
+        if (mSST != null && mSST.mSS.getState() == ServiceState.STATE_OUT_OF_SERVICE
+                && mSST.mSS.getDataRegState() != ServiceState.STATE_IN_SERVICE && !isEmergency) {
+            throw new CallStateException("cannot dial in current state");
+        }
         if (LOCAL_DEBUG) Rlog.d(LOG_TAG, "Trying (non-IMS) CS call");
-        return dialInternal(dialString, null, VideoProfile.VideoState.AUDIO_ONLY);
+        return dialInternal(dialString, null, VideoProfile.STATE_AUDIO_ONLY, intentExtras);
     }
 
     @Override
     protected Connection
-    dialInternal (String dialString, UUSInfo uusInfo, int videoState)
+    dialInternal (String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
             throws CallStateException {
 
         // Need to make sure dialString gets parsed properly
@@ -866,9 +876,9 @@
                                "dialing w/ mmi '" + mmi + "'...");
 
         if (mmi == null) {
-            return mCT.dial(newDialString, uusInfo);
+            return mCT.dial(newDialString, uusInfo, intentExtras);
         } else if (mmi.isTemporaryModeCLIR()) {
-            return mCT.dial(mmi.mDialingNumber, mmi.getCLIRMode(), uusInfo);
+            return mCT.dial(mmi.mDialingNumber, mmi.getCLIRMode(), uusInfo, intentExtras);
         } else {
             mPendingMMIs.add(mmi);
             mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
@@ -1065,6 +1075,12 @@
     }
 
     @Override
+    public String getGroupIdLevel2() {
+        IccRecords r = mIccRecords.get();
+        return (r != null) ? r.getGid2() : null;
+    }
+
+    @Override
     public String getLine1Number() {
         IccRecords r = mIccRecords.get();
         return (r != null) ? r.getMsisdnNumber() : null;
@@ -1435,11 +1451,14 @@
                 mCi.getIMEI(obtainMessage(EVENT_GET_IMEI_DONE));
                 mCi.getIMEISV(obtainMessage(EVENT_GET_IMEISV_DONE));
                 mCi.getRadioCapability(obtainMessage(EVENT_GET_RADIO_CAPABILITY));
+                startLceAfterRadioIsAvailable();
             }
             break;
 
             case EVENT_RADIO_ON:
-                // do-nothing
+                // If this is on APM off, SIM may already be loaded. Send setPreferredNetworkType
+                // request to RIL to preserve user setting across APM toggling
+                setPreferredNetworkTypeIfSimLoaded();
                 break;
 
             case EVENT_REGISTERED_TO_NETWORK:
diff --git a/src/java/com/android/internal/telephony/gsm/GsmCallTracker.java b/src/java/com/android/internal/telephony/gsm/GsmCallTracker.java
index 64b4155..7de8217 100755
--- a/src/java/com/android/internal/telephony/gsm/GsmCallTracker.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmCallTracker.java
@@ -17,6 +17,7 @@
 package com.android.internal.telephony.gsm;
 
 import android.os.AsyncResult;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
 import android.os.Registrant;
@@ -37,6 +38,7 @@
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.DriverCall;
 import com.android.internal.telephony.EventLogTags;
+import com.android.internal.telephony.LastCallFailCause;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneBase;
 import com.android.internal.telephony.PhoneConstants;
@@ -169,7 +171,8 @@
      * clirMode is one of the CLIR_ constants
      */
     synchronized Connection
-    dial (String dialString, int clirMode, UUSInfo uusInfo) throws CallStateException {
+    dial (String dialString, int clirMode, UUSInfo uusInfo, Bundle intentExtras)
+            throws CallStateException {
         // note that this triggers call state changed notif
         clearDisconnected();
 
@@ -189,6 +192,14 @@
             // and we need to make sure the foreground call is clear
             // for the newly dialed connection
             switchWaitingOrHoldingAndActive();
+            // This is a hack to delay DIAL so that it is sent out to RIL only after
+            // EVENT_SWITCH_RESULT is received. We've seen failures when adding a new call to
+            // multi-way conference calls due to DIAL being sent out before SWITCH is processed
+            try {
+                Thread.sleep(500);
+            } catch (InterruptedException e) {
+                // do nothing
+            }
 
             // Fake local state so that
             // a) foregroundCall is empty for the newly dialed connection
@@ -239,13 +250,13 @@
     }
 
     Connection
-    dial(String dialString, UUSInfo uusInfo) throws CallStateException {
-        return dial(dialString, CommandsInterface.CLIR_DEFAULT, uusInfo);
+    dial(String dialString, UUSInfo uusInfo, Bundle intentExtras) throws CallStateException {
+        return dial(dialString, CommandsInterface.CLIR_DEFAULT, uusInfo, intentExtras);
     }
 
     Connection
-    dial(String dialString, int clirMode) throws CallStateException {
-        return dial(dialString, clirMode, null);
+    dial(String dialString, int clirMode, Bundle intentExtras) throws CallStateException {
+        return dial(dialString, clirMode, null, intentExtras);
     }
 
     void
@@ -907,6 +918,7 @@
 
             case EVENT_GET_LAST_CALL_FAIL_CAUSE:
                 int causeCode;
+                String vendorCause = null;
                 ar = (AsyncResult)msg.obj;
 
                 operationComplete();
@@ -918,7 +930,9 @@
                     Rlog.i(LOG_TAG,
                             "Exception during getLastCallFailCause, assuming normal disconnect");
                 } else {
-                    causeCode = ((int[])ar.result)[0];
+                    LastCallFailCause failCause = (LastCallFailCause)ar.result;
+                    causeCode = failCause.causeCode;
+                    vendorCause = failCause.vendorCause;
                 }
                 // Log the causeCode if its not normal
                 if (causeCode == CallFailCause.NO_CIRCUIT_AVAIL ||
@@ -939,7 +953,7 @@
                 ) {
                     GsmConnection conn = mDroppedDuringPoll.get(i);
 
-                    conn.onRemoteDisconnect(causeCode);
+                    conn.onRemoteDisconnect(causeCode, vendorCause);
                 }
 
                 updatePhoneState();
diff --git a/src/java/com/android/internal/telephony/gsm/GsmConnection.java b/src/java/com/android/internal/telephony/gsm/GsmConnection.java
index 3b722d3..e343c6e 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmConnection.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmConnection.java
@@ -64,6 +64,7 @@
     PostDialState mPostDialState = PostDialState.NOT_STARTED;
     UUSInfo mUusInfo;
     int mPreciseCause = 0;
+    String mVendorCause;
 
     Connection mOrigConnection;
 
@@ -293,6 +294,7 @@
     onHangupLocal() {
         mCause = DisconnectCause.LOCAL;
         mPreciseCause = 0;
+        mVendorCause = null;
     }
 
     /**
@@ -375,8 +377,9 @@
     }
 
     /*package*/ void
-    onRemoteDisconnect(int causeCode) {
+    onRemoteDisconnect(int causeCode, String vendorCause) {
         this.mPreciseCause = causeCode;
+        this.mVendorCause = vendorCause;
         onDisconnect(disconnectCauseFromCode(causeCode));
     }
 
@@ -756,6 +759,11 @@
     }
 
     @Override
+    public String getVendorDisconnectCause() {
+        return mVendorCause;
+    }
+
+    @Override
     public void migrateFrom(Connection c) {
         if (c == null) return;
 
diff --git a/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java b/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
index 1a5ac32..8bd1ec7 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmMmiCode.java
@@ -306,7 +306,7 @@
                             isServiceClassVoiceorNone(ssData.serviceClass));
 
                     Rlog.d(LOG_TAG, "setVoiceCallForwardingFlag cffEnabled: " + cffEnabled);
-                    if (mPhone.mIccRecords != null) {
+                    if (mIccRecords != null) {
                         mIccRecords.setVoiceCallForwardingFlag(1, cffEnabled, null);
                         Rlog.d(LOG_TAG, "setVoiceCallForwardingFlag done from SS Info.");
                     } else {
diff --git a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index b18fa4a..4fbc924 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -157,7 +157,7 @@
             HashMap map = getSmsTrackerMap(destAddr, scAddr, destPort, data, pdu);
             SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
                     null /*messageUri*/, false /*isExpectMore*/, null /*fullMessageText*/,
-                    false /*isText*/);
+                    false /*isText*/, true /*persistMessage*/);
 
             String carrierPackage = getCarrierAppPackageName();
             if (carrierPackage != null) {
@@ -176,13 +176,15 @@
     /** {@inheritDoc} */
     @Override
     protected void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent,
-            PendingIntent deliveryIntent, Uri messageUri, String callingPkg) {
+            PendingIntent deliveryIntent, Uri messageUri, String callingPkg,
+            boolean persistMessage) {
         SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
                 scAddr, destAddr, text, (deliveryIntent != null));
         if (pdu != null) {
             HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu);
             SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
-                    messageUri, false /*isExpectMore*/, text /*fullMessageText*/, true /*isText*/);
+                    messageUri, false /*isExpectMore*/, text /*fullMessageText*/, true /*isText*/,
+                    persistMessage);
 
             String carrierPackage = getCarrierAppPackageName();
             if (carrierPackage != null) {
@@ -226,7 +228,8 @@
                     message, pdu);
             return getSmsTracker(map, sentIntent,
                     deliveryIntent, getFormat(), unsentPartCount, anyPartFailed, messageUri,
-                    smsHeader, !lastPart, fullMessageText, true /*isText*/);
+                    smsHeader, !lastPart, fullMessageText, true /*isText*/,
+                    false /*persistMessage*/);
         } else {
             Rlog.e(TAG, "GsmSMSDispatcher.sendNewSubmitPdu(): getSubmitPdu() returned null");
             return null;
diff --git a/src/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/src/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
index 1541c49..133e628 100755
--- a/src/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -31,7 +31,10 @@
 import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
+import android.os.PersistableBundle;
 import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -45,7 +48,6 @@
 import android.telephony.CellInfoLte;
 import android.telephony.CellInfoWcdma;
 import android.telephony.CellLocation;
-import android.telephony.RadioAccessFamily;
 import android.telephony.Rlog;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
@@ -58,11 +60,10 @@
 import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.EventLogTags;
+import com.android.internal.telephony.ICarrierConfigLoader;
 import com.android.internal.telephony.MccTable;
-import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.ProxyController;
 import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneFactory;
 import com.android.internal.telephony.RILConstants;
 import com.android.internal.telephony.RestrictedState;
 import com.android.internal.telephony.ServiceStateTracker;
@@ -70,10 +71,10 @@
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.TelephonyProperties;
 import com.android.internal.telephony.dataconnection.DcTrackerBase;
+import com.android.internal.telephony.imsphone.ImsPhone;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
 import com.android.internal.telephony.uicc.IccRecords;
 import com.android.internal.telephony.uicc.SIMRecords;
-import com.android.internal.telephony.uicc.UiccCard;
 import com.android.internal.telephony.uicc.UiccCardApplication;
 import com.android.internal.telephony.uicc.UiccController;
 
@@ -183,21 +184,6 @@
                 mAlarmSwitch = false;
                 DcTrackerBase dcTracker = mPhone.mDcTracker;
                 powerOffRadioSafely(dcTracker);
-            } else if (intent.getAction().equals(
-                    TelephonyIntents.ACTION_SET_RADIO_CAPABILITY_DONE)) {
-                if (DBG) {
-                    log("Received Intent ACTION_SET_RADIO_CAPABILITY_DONE");
-                }
-                ArrayList<RadioAccessFamily> newPhoneRcList =
-                        intent.getParcelableArrayListExtra(
-                        TelephonyIntents.EXTRA_RADIO_ACCESS_FAMILY);
-                if (newPhoneRcList == null || newPhoneRcList.size() == 0) {
-                    if (DBG) {
-                        log("EXTRA_RADIO_ACCESS_FAMILY not present.");
-                    }
-                } else {
-                    onSetPhoneRCDone(newPhoneRcList);
-                }
             }
         }
     };
@@ -260,7 +246,6 @@
         filter = new IntentFilter();
         Context context = phone.getContext();
         filter.addAction(ACTION_RADIO_OFF);
-        filter.addAction(TelephonyIntents.ACTION_SET_RADIO_CAPABILITY_DONE);
         context.registerReceiver(mIntentReceiver, filter);
     }
 
@@ -503,6 +488,11 @@
                 setPowerStateToDesired();
                 break;
 
+            case EVENT_IMS_CAPABILITY_CHANGED:
+                if (DBG) log("EVENT_IMS_CAPABILITY_CHANGED");
+                updateSpnDisplay();
+                break;
+
             default:
                 super.handleMessage(msg);
             break;
@@ -626,35 +616,59 @@
         }
 
         // The value of spn/showSpn are same in different scenarios.
-        //    EXTRA_SHOW_SPN = depending on IccRecords rule
+        //    EXTRA_SHOW_SPN = depending on IccRecords rule and radio/IMS state
         //    EXTRA_SPN = spn
+        //    EXTRA_DATA_SPN = dataSpn
         String spn = (iccRecords != null) ? iccRecords.getServiceProviderName() : "";
+        String dataSpn = spn;
         boolean showSpn = !TextUtils.isEmpty(spn)
                 && ((rule & SIMRecords.SPN_RULE_SHOW_SPN)
                         == SIMRecords.SPN_RULE_SHOW_SPN);
 
-        // airplane mode or spn equals plmn, do not show spn
-        if (mSS.getVoiceRegState() == ServiceState.STATE_POWER_OFF
+        if (!TextUtils.isEmpty(spn)
+                && mPhone.getImsPhone() != null
+                && ((ImsPhone) mPhone.getImsPhone()).isVowifiEnabled()) {
+            // In Wi-Fi Calling mode show SPN+WiFi
+            String formatVoice = mPhone.getContext().getText(
+                    com.android.internal.R.string.wfcSpnFormat).toString();
+            String formatData = mPhone.getContext().getText(
+                    com.android.internal.R.string.wfcDataSpnFormat).toString();
+            String originalSpn = spn.trim();
+            spn = String.format(formatVoice, originalSpn);
+            dataSpn = String.format(formatData, originalSpn);
+            showSpn = true;
+            showPlmn = false;
+        } else if (mSS.getVoiceRegState() == ServiceState.STATE_POWER_OFF
                 || (showPlmn && TextUtils.equals(spn, plmn))) {
+            // airplane mode or spn equals plmn, do not show spn
             spn = null;
             showSpn = false;
         }
 
+        int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+        int[] subIds = SubscriptionManager.getSubId(mPhone.getPhoneId());
+        if (subIds != null && subIds.length > 0) {
+            subId = subIds[0];
+        }
+
         // Update SPN_STRINGS_UPDATED_ACTION IFF any value changes
-        if (showPlmn != mCurShowPlmn
+        if (mSubId != subId ||
+                showPlmn != mCurShowPlmn
                 || showSpn != mCurShowSpn
                 || !TextUtils.equals(spn, mCurSpn)
+                || !TextUtils.equals(dataSpn, mCurDataSpn)
                 || !TextUtils.equals(plmn, mCurPlmn)) {
             if (DBG) {
                 log(String.format("updateSpnDisplay: changed" +
                         " sending intent rule=" + rule +
-                        " showPlmn='%b' plmn='%s' showSpn='%b' spn='%s'",
-                        showPlmn, plmn, showSpn, spn));
+                        " showPlmn='%b' plmn='%s' showSpn='%b' spn='%s' dataSpn='%s' subId='%d'",
+                        showPlmn, plmn, showSpn, spn, dataSpn, subId));
             }
             Intent intent = new Intent(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
             intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
             intent.putExtra(TelephonyIntents.EXTRA_SHOW_SPN, showSpn);
             intent.putExtra(TelephonyIntents.EXTRA_SPN, spn);
+            intent.putExtra(TelephonyIntents.EXTRA_DATA_SPN, dataSpn);
             intent.putExtra(TelephonyIntents.EXTRA_SHOW_PLMN, showPlmn);
             intent.putExtra(TelephonyIntents.EXTRA_PLMN, plmn);
             SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
@@ -666,9 +680,11 @@
             }
         }
 
+        mSubId = subId;
         mCurShowSpn = showSpn;
         mCurShowPlmn = showPlmn;
         mCurSpn = spn;
+        mCurDataSpn = dataSpn;
         mCurPlmn = plmn;
     }
 
@@ -696,12 +712,6 @@
                 return;
             }
 
-            if (!mCi.getRadioState().isOn()) {
-                // Radio has crashed or turned off
-                cancelPollState();
-                return;
-            }
-
             if (err != CommandException.Error.OP_NOT_ALLOWED_BEFORE_REG_NW) {
                 loge("RIL implementation has returned an error where it must succeed" +
                         ar.exception);
@@ -842,39 +852,69 @@
         mPollingContext[0]--;
 
         if (mPollingContext[0] == 0) {
-            /**
-             * Since the roaming state of gsm service (from +CREG) and
-             * data service (from +CGREG) could be different, the new SS
-             * is set to roaming when either is true.
-             *
-             * There are exceptions for the above rule.
-             * The new SS is not set as roaming while gsm service reports
-             * roaming but indeed it is same operator.
-             * And the operator is considered non roaming.
-             *
-             * The test for the operators is to handle special roaming
-             * agreements and MVNO's.
-             */
-            boolean roaming = (mGsmRoaming || mDataRoaming);
-            if (mGsmRoaming && !isOperatorConsideredRoaming(mNewSS) &&
-                (isSameNamedOperators(mNewSS) || isOperatorConsideredNonRoaming(mNewSS))) {
-                roaming = false;
-            }
-
-            if (mPhone.isMccMncMarkedAsNonRoaming(mNewSS.getOperatorNumeric())) {
-                roaming = false;
-            } else if (mPhone.isMccMncMarkedAsRoaming(mNewSS.getOperatorNumeric())) {
-                roaming = true;
-            }
-
-            mNewSS.setVoiceRoaming(roaming);
-            mNewSS.setDataRoaming(roaming);
+            updateRoamingState();
             mNewSS.setEmergencyOnly(mEmergencyOnly);
             pollStateDone();
         }
     }
 
     /**
+     * Query the carrier configuration to determine if there any network overrides
+     * for roaming or not roaming for the current service state.
+     */
+    protected void updateRoamingState() {
+        /**
+         * Since the roaming state of gsm service (from +CREG) and
+         * data service (from +CGREG) could be different, the new SS
+         * is set to roaming when either is true.
+         *
+         * There are exceptions for the above rule.
+         * The new SS is not set as roaming while gsm service reports
+         * roaming but indeed it is same operator.
+         * And the operator is considered non roaming.
+         *
+         * The test for the operators is to handle special roaming
+         * agreements and MVNO's.
+         */
+        boolean roaming = (mGsmRoaming || mDataRoaming);
+        if (mGsmRoaming && !isOperatorConsideredRoaming(mNewSS) &&
+                (isSameNamedOperators(mNewSS) || isOperatorConsideredNonRoaming(mNewSS))) {
+            roaming = false;
+        }
+
+        // Save the roaming state before carrier config possibly overrides it.
+        mNewSS.setDataRoamingFromRegistration(roaming);
+
+        ICarrierConfigLoader configLoader =
+            (ICarrierConfigLoader) ServiceManager.getService(Context.CARRIER_CONFIG_SERVICE);
+        if (configLoader != null) {
+            try {
+                PersistableBundle b = configLoader.getConfigForSubId(mPhone.getSubId());
+
+                if (alwaysOnHomeNetwork(b)) {
+                    log("updateRoamingState: carrier config override always on home network");
+                    roaming = false;
+                } else if (isNonRoamingInGsmNetwork(b, mNewSS.getOperatorNumeric())) {
+                    log("updateRoamingState: carrier config override set non roaming:"
+                            + mNewSS.getOperatorNumeric());
+                    roaming = false;
+                } else if (isRoamingInGsmNetwork(b, mNewSS.getOperatorNumeric())) {
+                    log("updateRoamingState: carrier config override set roaming:"
+                            + mNewSS.getOperatorNumeric());
+                    roaming = true;
+                }
+            } catch (RemoteException e) {
+                loge("updateRoamingState: unable to access carrier config service");
+            }
+        } else {
+            log("updateRoamingState: no carrier config service available");
+        }
+
+        mNewSS.setVoiceRoaming(roaming);
+        mNewSS.setDataRoaming(roaming);
+    }
+
+    /**
      * Set both voice and data roaming type,
      * judging from the ISO country of SIM VS network.
      */
@@ -950,8 +990,10 @@
                 setSignalStrengthDefaultValues();
                 mGotCountryCode = false;
                 mNitzUpdatedTime = false;
-                pollStateDone();
-            break;
+                if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+                        != mSS.getRilDataRadioTechnology()) {
+                    pollStateDone();
+                }
 
             default:
                 // Issue all poll-related commands at once
@@ -982,6 +1024,13 @@
     }
 
     private void pollStateDone() {
+        if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean(PROP_FORCE_ROAMING, false)) {
+            mNewSS.setVoiceRoaming(true);
+            mNewSS.setDataRoaming(true);
+        }
+        useDataRegStateForDataOnlyDevices();
+        resetServiceStateInIwlanMode();
+
         if (DBG) {
             log("Poll ServiceState done: " +
                 " oldSS=[" + mSS + "] newSS=[" + mNewSS + "]" +
@@ -991,13 +1040,6 @@
                 " mNewReasonDataDenied=" + mNewReasonDataDenied);
         }
 
-        if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean(PROP_FORCE_ROAMING, false)) {
-            mNewSS.setVoiceRoaming(true);
-            mNewSS.setDataRoaming(true);
-        }
-
-        useDataRegStateForDataOnlyDevices();
-
         boolean hasRegistered =
             mSS.getVoiceRegState() != ServiceState.STATE_IN_SERVICE
             && mNewSS.getVoiceRegState() == ServiceState.STATE_IN_SERVICE;
@@ -1037,7 +1079,6 @@
         boolean hasDataRoamingOff = mSS.getDataRoaming() && !mNewSS.getDataRoaming();
 
         boolean hasLocationChanged = !mNewCellLoc.equals(mCellLoc);
-
         TelephonyManager tm =
                 (TelephonyManager) mPhone.getContext().getSystemService(Context.TELEPHONY_SERVICE);
 
@@ -1092,6 +1133,11 @@
 
         if (hasRilDataRadioTechnologyChanged) {
             tm.setDataNetworkTypeForPhone(mPhone.getPhoneId(), mSS.getRilVoiceRadioTechnology());
+
+            if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+                        == mSS.getRilDataRadioTechnology()) {
+                log("pollStateDone: IWLAN enabled");
+            }
         }
 
         if (hasRegistered) {
@@ -1250,7 +1296,13 @@
 
         if (hasDataRegStateChanged || hasRilDataRadioTechnologyChanged) {
             notifyDataRegStateRilRadioTechnologyChanged();
-            mPhone.notifyDataConnection(null);
+
+            if (ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
+                        == mSS.getRilDataRadioTechnology()) {
+                mPhone.notifyDataConnection(Phone.REASON_IWLAN_AVAILABLE);
+            } else {
+                mPhone.notifyDataConnection(null);
+            }
         }
 
         if (hasVoiceRoamingOn) {
@@ -1922,7 +1974,6 @@
      * @param notifyType is one state of PS/CS_*_ENABLE/DISABLE
      */
     private void setNotification(int notifyType) {
-
         if (DBG) log("setNotification: create notification " + notifyType);
 
         // Needed because sprout RIL sends these when they shouldn't?
@@ -1935,13 +1986,6 @@
 
         Context context = mPhone.getContext();
 
-        mNotification = new Notification();
-        mNotification.when = System.currentTimeMillis();
-        mNotification.flags = Notification.FLAG_AUTO_CANCEL;
-        mNotification.icon = com.android.internal.R.drawable.stat_sys_warning;
-        Intent intent = new Intent();
-        mNotification.contentIntent = PendingIntent
-        .getActivity(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
 
         CharSequence details = "";
         CharSequence title = context.getText(com.android.internal.R.string.RestrictedChangedTitle);
@@ -1974,11 +2018,16 @@
         }
 
         if (DBG) log("setNotification: put notification " + title + " / " +details);
-        mNotification.tickerText = title;
-        mNotification.color = context.getResources().getColor(
-                com.android.internal.R.color.system_notification_accent_color);
-        mNotification.setLatestEventInfo(context, title, details,
-                mNotification.contentIntent);
+        mNotification = new Notification.Builder(context)
+                .setWhen(System.currentTimeMillis())
+                .setAutoCancel(true)
+                .setSmallIcon(com.android.internal.R.drawable.stat_sys_warning)
+                .setTicker(title)
+                .setColor(context.getResources().getColor(
+                        com.android.internal.R.color.system_notification_accent_color))
+                .setContentTitle(title)
+                .setContentText(details)
+                .build();
 
         NotificationManager notificationManager = (NotificationManager)
             context.getSystemService(Context.NOTIFICATION_SERVICE);
@@ -2068,6 +2117,7 @@
         pw.println(" mNotification=" + mNotification);
         pw.println(" mWakeLock=" + mWakeLock);
         pw.println(" mCurSpn=" + mCurSpn);
+        pw.println(" mCurDataSpn=" + mCurDataSpn);
         pw.println(" mCurShowSpn=" + mCurShowSpn);
         pw.println(" mCurPlmn=" + mCurPlmn);
         pw.println(" mCurShowPlmn=" + mCurShowPlmn);
@@ -2146,53 +2196,7 @@
         mImsRegistrationOnOff = registered;
     }
 
-    public void onSetPhoneRCDone(ArrayList<RadioAccessFamily> phoneRcs) {
-        int INVALID = -1;
-        int size = 0;
-        boolean needToChangeNetworkMode = false;
-        RadioAccessFamily phoneRaf = null;
-        int myPhoneId = mPhone.getPhoneId();
-        int newCapability = 0;
-        int networkMode = INVALID;
-
-        if (phoneRcs == null) return;
-        size = phoneRcs.size();
-        for (int i = 0; i < size; i++) {
-            phoneRaf = phoneRcs.get(i);
-            if (myPhoneId == phoneRaf.getPhoneId()) {
-                needToChangeNetworkMode = true;
-                newCapability = phoneRaf.getRadioAccessFamily();
-                break;
-            }
-        }
-
-        if (needToChangeNetworkMode) {
-            if ((newCapability & RadioAccessFamily.RAF_LTE)
-                    == RadioAccessFamily.RAF_LTE) {
-                networkMode = RILConstants.NETWORK_MODE_LTE_GSM_WCDMA;
-            } else if ((newCapability & RadioAccessFamily.RAF_UMTS)
-                    == RadioAccessFamily.RAF_UMTS) {
-                networkMode = RILConstants.NETWORK_MODE_WCDMA_PREF;
-            } else if ((newCapability & RadioAccessFamily.RAF_GSM)
-                    == RadioAccessFamily.RAF_GSM) {
-                networkMode = RILConstants.NETWORK_MODE_GSM_ONLY;
-            } else {
-                networkMode = INVALID;
-                log("Error: capability is not define");
-            }
-
-            if (DBG) log("myPhoneId=" + myPhoneId + " newCapability=" + newCapability
-                    + " networkMode=" + networkMode);
-
-            if (networkMode != INVALID) {
-                //FIXME : update preferred network mode
-                //TelephonyManager.putIntAtIndex(mPhone.getContext().getContentResolver(),
-                //        Settings.Global.PREFERRED_NETWORK_MODE, myPhoneId, networkMode);
-                //networkMode = PhoneFactory.calculatePreferredNetworkType(mPhone.getContext());
-                //FIXME : update preferred network mode
-
-                mCi.setPreferredNetworkType(networkMode, null);
-            }
-        }
-     }
+    public void onImsCapabilityChanged() {
+        sendMessage(obtainMessage(EVENT_IMS_CAPABILITY_CHANGED));
+    }
 }
diff --git a/src/java/com/android/internal/telephony/gsm/SmsMessage.java b/src/java/com/android/internal/telephony/gsm/SmsMessage.java
index a1f029f..0d9bed4 100644
--- a/src/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/src/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -777,7 +777,8 @@
     }
 
     /**
-     * Calculate the number of septets needed to encode the message.
+     * Calculates the number of SMS's required to encode the message body and
+     * the number of characters remaining until the next message.
      *
      * @param msgBody the message to encode
      * @param use7bitOnly ignore (but still count) illegal characters if true
@@ -795,31 +796,7 @@
         }
         TextEncodingDetails ted = GsmAlphabet.countGsmSeptets(newMsgBody, use7bitOnly);
         if (ted == null) {
-            ted = new TextEncodingDetails();
-            int octets = newMsgBody.length() * 2;
-            ted.codeUnitCount = newMsgBody.length();
-            if (octets > MAX_USER_DATA_BYTES) {
-                // If EMS is not supported, break down EMS into single segment SMS
-                // and add page info " x/y".
-                // In the case of UCS2 encoding type, we need 8 bytes for this
-                // but we only have 6 bytes from UDH, so truncate the limit for
-                // each segment by 2 bytes (1 char).
-                int max_user_data_bytes_with_header = MAX_USER_DATA_BYTES_WITH_HEADER;
-                if (!android.telephony.SmsMessage.hasEmsSupport()) {
-                    // make sure total number of segments is less than 10
-                    if (octets <= 9 * (max_user_data_bytes_with_header - 2))
-                        max_user_data_bytes_with_header -= 2;
-                }
-
-                ted.msgCount = (octets + (max_user_data_bytes_with_header - 1)) /
-                        max_user_data_bytes_with_header;
-                ted.codeUnitsRemaining = ((ted.msgCount *
-                        max_user_data_bytes_with_header) - octets) / 2;
-            } else {
-                ted.msgCount = 1;
-                ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES - octets)/2;
-            }
-            ted.codeUnitSize = ENCODING_16BIT;
+            return SmsMessageBase.calcUnicodeEncodingDetails(newMsgBody);
         }
         return ted;
     }
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
index efc53c4..48d0d37 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhone.java
@@ -16,10 +16,16 @@
 
 package com.android.internal.telephony.imsphone;
 
+import android.app.Activity;
 import android.app.ActivityManagerNative;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.os.AsyncResult;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
 import android.os.PowerManager;
@@ -37,9 +43,11 @@
 
 import com.android.ims.ImsCallForwardInfo;
 import com.android.ims.ImsCallProfile;
+import com.android.ims.ImsConfig;
 import com.android.ims.ImsEcbm;
 import com.android.ims.ImsEcbmStateListener;
 import com.android.ims.ImsException;
+import com.android.ims.ImsManager;
 import com.android.ims.ImsReasonInfo;
 import com.android.ims.ImsSsInfo;
 import com.android.ims.ImsUtInterface;
@@ -77,8 +85,10 @@
 import com.android.internal.telephony.PhoneBase;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.PhoneNotifier;
+import com.android.internal.telephony.ServiceStateTracker;
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.telephony.TelephonyProperties;
+import com.android.internal.telephony.UUSInfo;
 import com.android.internal.telephony.cdma.CDMAPhone;
 import com.android.internal.telephony.gsm.GSMPhone;
 import com.android.internal.telephony.uicc.IccRecords;
@@ -98,9 +108,15 @@
     protected static final int EVENT_GET_CALL_BARRING_DONE          = EVENT_LAST + 2;
     protected static final int EVENT_SET_CALL_WAITING_DONE          = EVENT_LAST + 3;
     protected static final int EVENT_GET_CALL_WAITING_DONE          = EVENT_LAST + 4;
+    protected static final int EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED  = EVENT_LAST + 5;
 
     public static final String CS_FALLBACK = "cs_fallback";
 
+    public static final String EXTRA_KEY_ALERT_TITLE = "alertTitle";
+    public static final String EXTRA_KEY_ALERT_MESSAGE = "alertMessage";
+    public static final String EXTRA_KEY_ALERT_SHOW = "alertShow";
+    public static final String EXTRA_KEY_NOTIFICATION_MESSAGE = "notificationMessage";
+
     static final int RESTART_ECM_TIMER = 0; // restart Ecm timer
     static final int CANCEL_ECM_TIMER = 1; // cancel Ecm timer
 
@@ -128,6 +144,7 @@
     private final RegistrantList mSilentRedialRegistrants = new RegistrantList();
 
     private boolean mImsRegistered = false;
+
     // A runnable which is used to automatically exit from Ecm after a period of time.
     private Runnable mExitEcmRunnable = new Runnable() {
         @Override
@@ -171,12 +188,34 @@
         PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
         mWakeLock.setReferenceCounted(false);
+
+        if (mDefaultPhone.getServiceStateTracker() != null) {
+            mDefaultPhone.getServiceStateTracker()
+                    .registerForDataRegStateOrRatChanged(this,
+                    EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED, null);
+        }
+        updateDataServiceState();
     }
 
     public void updateParentPhone(PhoneBase parentPhone) {
         // synchronization is managed at the PhoneBase scope (which calls this function)
+        if (mDefaultPhone != null && mDefaultPhone.getServiceStateTracker() != null) {
+            mDefaultPhone.getServiceStateTracker().
+                    unregisterForDataRegStateOrRatChanged(this);
+        }
         mDefaultPhone = parentPhone;
         mPhoneId = mDefaultPhone.getPhoneId();
+        if (mDefaultPhone.getServiceStateTracker() != null) {
+            mDefaultPhone.getServiceStateTracker()
+                    .registerForDataRegStateOrRatChanged(this,
+                    EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED, null);
+        }
+        updateDataServiceState();
+
+        // When the parent phone is updated, we need to notify listeners of the cached video
+        // capability.
+        Rlog.d(LOG_TAG, "updateParentPhone - Notify video capability changed " + mIsVideoCapable);
+        notifyForVideoCapabilityChanged(mIsVideoCapable);
     }
 
     @Override
@@ -188,6 +227,10 @@
         mCT.dispose();
 
         //Force all referenced classes to unregister their former registered events
+        if (mDefaultPhone != null && mDefaultPhone.getServiceStateTracker() != null) {
+            mDefaultPhone.getServiceStateTracker().
+                    unregisterForDataRegStateOrRatChanged(this);
+        }
     }
 
     @Override
@@ -207,6 +250,7 @@
 
     /* package */ void setServiceState(int state) {
         mSS.setState(state);
+        updateDataServiceState();
     }
 
     @Override
@@ -463,14 +507,42 @@
         mDefaultPhone.notifyNewRingingConnectionP(c);
     }
 
+    public static void checkWfcWifiOnlyModeBeforeDial(ImsPhone imsPhone, Context context)
+            throws CallStateException {
+        if (imsPhone == null ||
+                !imsPhone.isVowifiEnabled()) {
+            boolean wfcWiFiOnly = (ImsManager.isWfcEnabledByPlatform(context) &&
+                    ImsManager.isWfcEnabledByUser(context) &&
+                    (ImsManager.getWfcMode(context) ==
+                            ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY));
+            if (wfcWiFiOnly) {
+                throw new CallStateException(
+                        CallStateException.ERROR_DISCONNECTED,
+                        "WFC Wi-Fi Only Mode: IMS not registered");
+            }
+        }
+    }
+
+    public void notifyForVideoCapabilityChanged(boolean isVideoCapable) {
+        mIsVideoCapable = isVideoCapable;
+        mDefaultPhone.notifyForVideoCapabilityChanged(isVideoCapable);
+    }
 
     @Override
     public Connection
     dial(String dialString, int videoState) throws CallStateException {
-        return dialInternal(dialString, videoState);
+        return dialInternal(dialString, videoState, null);
     }
 
-    protected Connection dialInternal(String dialString, int videoState)
+    @Override
+    public Connection
+    dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
+            throws CallStateException {
+        // ignore UUSInfo
+        return dialInternal (dialString, videoState, intentExtras);
+    }
+
+    protected Connection dialInternal(String dialString, int videoState, Bundle intentExtras)
             throws CallStateException {
         // Need to make sure dialString gets parsed properly
         String newDialString = PhoneNumberUtils.stripSeparators(dialString);
@@ -481,7 +553,7 @@
         }
 
         if (mDefaultPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
-            return mCT.dial(dialString, videoState);
+            return mCT.dial(dialString, videoState, intentExtras);
         }
 
         // Only look at the Network portion for mmi
@@ -492,9 +564,9 @@
                 "dialing w/ mmi '" + mmi + "'...");
 
         if (mmi == null) {
-            return mCT.dial(dialString, videoState);
+            return mCT.dial(dialString, videoState, intentExtras);
         } else if (mmi.isTemporaryModeCLIR()) {
-            return mCT.dial(mmi.getDialingNumber(), mmi.getCLIRMode(), videoState);
+            return mCT.dial(mmi.getDialingNumber(), mmi.getCLIRMode(), videoState, intentExtras);
         } else if (!mmi.isSupportedOverImsPhone()) {
             // If the mmi is not supported by IMS service,
             // try to initiate dialing with default phone
@@ -668,8 +740,18 @@
             String dialingNumber,
             int timerSeconds,
             Message onComplete) {
+        setCallForwardingOption(commandInterfaceCFAction, commandInterfaceCFReason, dialingNumber,
+                CommandsInterface.SERVICE_CLASS_VOICE, timerSeconds, onComplete);
+    }
+
+    public void setCallForwardingOption(int commandInterfaceCFAction,
+            int commandInterfaceCFReason,
+            String dialingNumber,
+            int serviceClass,
+            int timerSeconds,
+            Message onComplete) {
         if (DBG) Rlog.d(LOG_TAG, "setCallForwardingOption action=" + commandInterfaceCFAction
-                + ", reason=" + commandInterfaceCFReason);
+                + ", reason=" + commandInterfaceCFReason + " serviceClass=" + serviceClass);
         if ((isValidCommandInterfaceCFAction(commandInterfaceCFAction)) &&
                 (isValidCommandInterfaceCFReason(commandInterfaceCFReason))) {
             Message resp;
@@ -684,6 +766,7 @@
                 ut.updateCallForward(getActionFromCFAction(commandInterfaceCFAction),
                         getConditionFromCFReason(commandInterfaceCFReason),
                         dialingNumber,
+                        serviceClass,
                         timerSeconds,
                         onComplete);
              } catch (ImsException e) {
@@ -710,13 +793,17 @@
 
     @Override
     public void setCallWaiting(boolean enable, Message onComplete) {
+        setCallWaiting(enable, CommandsInterface.SERVICE_CLASS_VOICE, onComplete);
+    }
+
+    public void setCallWaiting(boolean enable, int serviceClass, Message onComplete) {
         if (DBG) Rlog.d(LOG_TAG, "setCallWaiting enable=" + enable);
         Message resp;
         resp = obtainMessage(EVENT_SET_CALL_WAITING_DONE, onComplete);
 
         try {
             ImsUtInterface ut = mCT.getUtInterface();
-            ut.updateCallWaiting(enable, resp);
+            ut.updateCallWaiting(enable, serviceClass, resp);
         } catch (ImsException e) {
             sendErrorResponse(onComplete, e);
         }
@@ -833,6 +920,8 @@
             case ImsReasonInfo.CODE_UT_CB_PASSWORD_MISMATCH:
                 error = CommandException.Error.PASSWORD_INCORRECT;
                 break;
+            case ImsReasonInfo.CODE_UT_SERVICE_UNAVAILABLE:
+                error = CommandException.Error.RADIO_NOT_AVAILABLE;
             default:
                 break;
         }
@@ -1039,22 +1128,24 @@
     sendResponse(Message onComplete, Object result, Throwable e) {
         if (onComplete != null) {
             CommandException ex = null;
-            ImsException imsEx = null;
             if (e != null) {
-                if (e instanceof ImsException) {
-                    imsEx = (ImsException) e;
-                    AsyncResult.forMessage(onComplete, result, imsEx);
-                } else {
-                    ex = getCommandException(e);
-                    AsyncResult.forMessage(onComplete, result, ex);
-                }
-            } else {
-                AsyncResult.forMessage(onComplete, result, null);
+                ex = getCommandException(e);
             }
+            AsyncResult.forMessage(onComplete, result, ex);
             onComplete.sendToTarget();
         }
     }
 
+    private void updateDataServiceState() {
+        if (mSS != null && mDefaultPhone.getServiceStateTracker() != null
+                && mDefaultPhone.getServiceStateTracker().mSS != null) {
+            ServiceState ss = mDefaultPhone.getServiceStateTracker().mSS;
+            mSS.setDataRegState(ss.getDataRegState());
+            mSS.setRilDataRadioTechnology(ss.getRilDataRadioTechnology());
+            Rlog.d(LOG_TAG, "updateDataServiceState: defSs = " + ss + " imsSs = " + mSS);
+        }
+    }
+
     @Override
     public void handleMessage (Message msg) {
         AsyncResult ar = (AsyncResult) msg.obj;
@@ -1097,6 +1188,11 @@
                 sendResponse((Message) ar.userObj, null, ar.exception);
                 break;
 
+             case EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED:
+                 if (DBG) Rlog.d(LOG_TAG, "EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED");
+                 updateDataServiceState();
+                 break;
+
              default:
                  super.handleMessage(msg);
                  break;
@@ -1234,12 +1330,20 @@
         mEcmExitRespRegistrant.clear();
     }
 
+    public void onFeatureCapabilityChanged() {
+        mDefaultPhone.getServiceStateTracker().onImsCapabilityChanged();
+    }
+
     public boolean isVolteEnabled() {
         return mCT.isVolteEnabled();
     }
 
-    public boolean isVtEnabled() {
-        return mCT.isVtEnabled();
+    public boolean isVowifiEnabled() {
+        return mCT.isVowifiEnabled();
+    }
+
+    public boolean isVideoCallEnabled() {
+        return mCT.isVideoCallEnabled();
     }
 
     public Phone getDefaultPhone() {
@@ -1257,4 +1361,120 @@
     public void callEndCleanupHandOverCallIfAny() {
         mCT.callEndCleanupHandOverCallIfAny();
     }
+
+    private BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            // Add notification only if alert was not shown by WfcSettings
+            if (getResultCode() == Activity.RESULT_OK) {
+                // Default result code (as passed to sendOrderedBroadcast)
+                // means that intent was not received by WfcSettings.
+
+                CharSequence title = intent.getCharSequenceExtra(EXTRA_KEY_ALERT_TITLE);
+                CharSequence messageAlert = intent.getCharSequenceExtra(EXTRA_KEY_ALERT_MESSAGE);
+                CharSequence messageNotification = intent.getCharSequenceExtra(EXTRA_KEY_NOTIFICATION_MESSAGE);
+
+                Intent resultIntent = new Intent(Intent.ACTION_MAIN);
+                resultIntent.setClassName("com.android.settings",
+                        "com.android.settings.Settings$WifiCallingSettingsActivity");
+                resultIntent.putExtra(EXTRA_KEY_ALERT_SHOW, true);
+                resultIntent.putExtra(EXTRA_KEY_ALERT_TITLE, title);
+                resultIntent.putExtra(EXTRA_KEY_ALERT_MESSAGE, messageAlert);
+                PendingIntent resultPendingIntent =
+                        PendingIntent.getActivity(
+                                mContext,
+                                0,
+                                resultIntent,
+                                PendingIntent.FLAG_UPDATE_CURRENT
+                        );
+
+                final Notification notification =
+                        new Notification.Builder(mContext)
+                                .setSmallIcon(android.R.drawable.stat_sys_warning)
+                                .setContentTitle(title)
+                                .setContentText(messageNotification)
+                                .setAutoCancel(true)
+                                .setContentIntent(resultPendingIntent)
+                                .setStyle(new Notification.BigTextStyle().bigText(messageNotification))
+                                .build();
+                final String notificationTag = "wifi_calling";
+                final int notificationId = 1;
+
+                NotificationManager notificationManager =
+                        (NotificationManager) mContext.getSystemService(
+                                Context.NOTIFICATION_SERVICE);
+                notificationManager.notify(notificationTag, notificationId,
+                        notification);
+            }
+        }
+    };
+
+    /**
+     * Show notification in case of some error codes.
+     */
+    public void processDisconnectReason(ImsReasonInfo imsReasonInfo) {
+        if (imsReasonInfo.mCode == imsReasonInfo.CODE_REGISTRATION_ERROR
+                && imsReasonInfo.mExtraMessage != null) {
+
+            final String[] wfcOperatorErrorCodes =
+                    mContext.getResources().getStringArray(
+                            com.android.internal.R.array.wfcOperatorErrorCodes);
+            final String[] wfcOperatorErrorAlertMessages =
+                    mContext.getResources().getStringArray(
+                            com.android.internal.R.array.wfcOperatorErrorAlertMessages);
+            final String[] wfcOperatorErrorNotificationMessages =
+                    mContext.getResources().getStringArray(
+                            com.android.internal.R.array.wfcOperatorErrorNotificationMessages);
+
+            for (int i = 0; i < wfcOperatorErrorCodes.length; i++) {
+                // Match error code.
+                if (!imsReasonInfo.mExtraMessage.startsWith(
+                        wfcOperatorErrorCodes[i])) {
+                    continue;
+                }
+                // If there is no delimiter at the end of error code string
+                // then we need to verify that we are not matching partial code.
+                // EXAMPLE: "REG9" must not match "REG99".
+                // NOTE: Error code must not be empty.
+                int codeStringLength = wfcOperatorErrorCodes[i].length();
+                char lastChar = wfcOperatorErrorCodes[i].charAt(codeStringLength-1);
+                if (Character.isLetterOrDigit(lastChar)) {
+                    if (imsReasonInfo.mExtraMessage.length() > codeStringLength) {
+                        char nextChar = imsReasonInfo.mExtraMessage.charAt(codeStringLength);
+                        if (Character.isLetterOrDigit(nextChar)) {
+                            continue;
+                        }
+                    }
+                }
+
+                final CharSequence title = mContext.getText(
+                        com.android.internal.R.string.wfcRegErrorTitle);
+
+                CharSequence messageAlert = imsReasonInfo.mExtraMessage;
+                CharSequence messageNotification = imsReasonInfo.mExtraMessage;
+                if (!wfcOperatorErrorAlertMessages[i].isEmpty()) {
+                    messageAlert = wfcOperatorErrorAlertMessages[i];
+                }
+                if (!wfcOperatorErrorNotificationMessages[i].isEmpty()) {
+                    messageNotification = wfcOperatorErrorNotificationMessages[i];
+                }
+
+                // UX requirement is to disable WFC in case of "permanent" registration failures.
+                ImsManager.setWfcSetting(mContext, false);
+
+                // If WfcSettings are active then alert will be shown
+                // otherwise notification will be added.
+                Intent intent = new Intent(ImsManager.ACTION_IMS_REGISTRATION_ERROR);
+                intent.putExtra(EXTRA_KEY_ALERT_TITLE, title);
+                intent.putExtra(EXTRA_KEY_ALERT_MESSAGE, messageAlert);
+                intent.putExtra(EXTRA_KEY_NOTIFICATION_MESSAGE, messageNotification);
+                mContext.sendOrderedBroadcast(intent, null, mResultReceiver,
+                        null, Activity.RESULT_OK, null, null);
+
+                // We can only match a single error code
+                // so should break the loop after a successful match.
+                break;
+            }
+        }
+    }
 }
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
index 22eb63d..8ad7472 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneBase.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.net.LinkProperties;
 import android.os.AsyncResult;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
 import android.os.Registrant;
@@ -66,13 +67,6 @@
     }
 
     @Override
-    public Connection dial(String dialString, UUSInfo uusInfo, int videoState)
-            throws CallStateException {
-        // ignore UUSInfo
-        return dial(dialString, videoState);
-    }
-
-    @Override
     public void migrateFrom(PhoneBase from) {
         super.migrateFrom(from);
         migrate(mRingbackRegistrants, ((ImsPhoneBase)from).mRingbackRegistrants);
@@ -341,6 +335,11 @@
     }
 
     @Override
+    public String getGroupIdLevel2() {
+        return null;
+    }
+
+    @Override
     public String getIccSerialNumber() {
         return null;
     }
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
index 5098bbb..c6f118a 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCall.java
@@ -16,8 +16,10 @@
 
 package com.android.internal.telephony.imsphone;
 
+import android.telecom.ConferenceParticipant;
 import android.telephony.Rlog;
 import android.telephony.DisconnectCause;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.Call;
@@ -34,22 +36,40 @@
  * {@hide}
  */
 public class ImsPhoneCall extends Call {
-    /*************************** Instance Variables **************************/
-
     private static final String LOG_TAG = "ImsPhoneCall";
 
+    // This flag is meant to be used as a debugging tool to quickly see all logs
+    // regardless of the actual log level set on this component.
+    private static final boolean FORCE_DEBUG = false; /* STOPSHIP if true */
+    private static final boolean DBG = FORCE_DEBUG || Rlog.isLoggable(LOG_TAG, Log.DEBUG);
+    private static final boolean VDBG = FORCE_DEBUG || Rlog.isLoggable(LOG_TAG, Log.VERBOSE);
+
+    /*************************** Instance Variables **************************/
+    public static final String CONTEXT_UNKNOWN = "UK";
+    public static final String CONTEXT_RINGING = "RG";
+    public static final String CONTEXT_FOREGROUND = "FG";
+    public static final String CONTEXT_BACKGROUND = "BG";
+    public static final String CONTEXT_HANDOVER = "HO";
+
     /*package*/ ImsPhoneCallTracker mOwner;
 
     private boolean mRingbackTonePlayed = false;
 
+    // Determines what type of ImsPhoneCall this is.  ImsPhoneCallTracker uses instances of
+    // ImsPhoneCall to for fg, bg, etc calls.  This is used as a convenience for logging so that it
+    // can be made clear whether a call being logged is the foreground, background, etc.
+    private final String mCallContext;
+
     /****************************** Constructors *****************************/
     /*package*/
     ImsPhoneCall() {
+        mCallContext = CONTEXT_UNKNOWN;
     }
 
     /*package*/
-    ImsPhoneCall(ImsPhoneCallTracker owner) {
+    ImsPhoneCall(ImsPhoneCallTracker owner, String context) {
         mOwner = owner;
+        mCallContext = context;
     }
 
     public void dispose() {
@@ -101,21 +121,53 @@
     }
 
     @Override
-    public String
-    toString() {
-        return mState.toString();
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("[ImsPhoneCall ");
+        sb.append(mCallContext);
+        sb.append(" state: ");
+        sb.append(mState.toString());
+        sb.append(" ");
+        if (mConnections.size() > 1) {
+            sb.append(" ERROR_MULTIPLE ");
+        }
+        for (Connection conn : mConnections) {
+            sb.append(conn);
+            sb.append(" ");
+        }
+
+        sb.append("]");
+        return sb.toString();
+    }
+
+    @Override
+    public List<ConferenceParticipant> getConferenceParticipants() {
+         ImsCall call = getImsCall();
+         if (call == null) {
+             return null;
+         }
+         return call.getConferenceParticipants();
     }
 
     //***** Called from ImsPhoneConnection
 
     /*package*/ void
     attach(Connection conn) {
+        if (VDBG) {
+            Rlog.v(LOG_TAG, "attach : " + mCallContext + " conn = " + conn);
+        }
         clearDisconnected();
         mConnections.add(conn);
+
+        mOwner.logState();
     }
 
     /*package*/ void
     attach(Connection conn, State state) {
+        if (VDBG) {
+            Rlog.v(LOG_TAG, "attach : " + mCallContext + " state = " +
+                    state.toString());
+        }
         this.attach(conn);
         mState = state;
     }
@@ -153,8 +205,13 @@
 
     /*package*/ void
     detach(ImsPhoneConnection conn) {
+        if (VDBG) {
+            Rlog.v(LOG_TAG, "detach : " + mCallContext + " conn = " + conn);
+        }
         mConnections.remove(conn);
         clearDisconnected();
+
+        mOwner.logState();
     }
 
     /**
@@ -227,13 +284,15 @@
             long conferenceConnectTime = imsPhoneConnection.getConferenceConnectTime();
             if (conferenceConnectTime > 0) {
                 imsPhoneConnection.setConnectTime(conferenceConnectTime);
+            } else {
+                if (DBG) {
+                    Rlog.d(LOG_TAG, "merge: conference connect time is 0");
+                }
             }
         }
-
-        ImsPhoneConnection[] cc = that.mConnections.toArray(
-                new ImsPhoneConnection[that.mConnections.size()]);
-        for (ImsPhoneConnection c : cc) {
-            c.update(null, state);
+        if (DBG) {
+            Rlog.d(LOG_TAG, "merge(" + mCallContext + "): " + that + "state = "
+                    + state);
         }
     }
 
@@ -302,12 +361,16 @@
     }
 
     void switchWith(ImsPhoneCall that) {
+        if (VDBG) {
+            Rlog.v(LOG_TAG, "switchWith : switchCall = " + this + " withCall = " + that);
+        }
         synchronized (ImsPhoneCall.class) {
             ImsPhoneCall tmp = new ImsPhoneCall();
             tmp.takeOver(this);
             this.takeOver(that);
             that.takeOver(tmp);
         }
+        mOwner.logState();
     }
 
     private void takeOver(ImsPhoneCall that) {
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index d148713..1515e9c 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -28,6 +28,7 @@
 import android.content.IntentFilter;
 import android.content.SharedPreferences;
 import android.os.AsyncResult;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
 import android.os.Registrant;
@@ -74,8 +75,13 @@
 
     private static final boolean DBG = true;
 
-    private boolean mIsVolteEnabled = false;
-    private boolean mIsVtEnabled = false;
+    // When true, dumps the state of ImsPhoneCallTracker after changes to foreground and background
+    // calls.  This is helpful for debugging.
+    private static final boolean VERBOSE_STATE_LOGGING = false; /* stopship if true */
+
+    //Indices map to ImsConfig.FeatureConstants
+    private boolean[] mImsFeatureEnabled = {false, false, false, false};
+    private final String[] mImsFeatureStrings = {"VoLTE", "ViLTE", "VoWiFi", "ViWiFi"};
 
     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
@@ -105,13 +111,7 @@
                             ImsPhoneCallTracker.this, mRingingCall);
                     addConnection(conn);
 
-                    IImsVideoCallProvider imsVideoCallProvider =
-                            imsCall.getCallSession().getVideoCallProvider();
-                    if (imsVideoCallProvider != null) {
-                        ImsVideoCallProviderWrapper imsVideoCallProviderWrapper =
-                                new ImsVideoCallProviderWrapper(imsVideoCallProvider);
-                        conn.setVideoProvider(imsVideoCallProviderWrapper);
-                    }
+                    setVideoCallProvider(conn, imsCall);
 
                     if ((mForegroundCall.getState() != ImsPhoneCall.State.IDLE) ||
                             (mBackgroundCall.getState() != ImsPhoneCall.State.IDLE)) {
@@ -147,10 +147,10 @@
     private RegistrantList mVoiceCallEndedRegistrants = new RegistrantList();
     private RegistrantList mVoiceCallStartedRegistrants = new RegistrantList();
 
-    ImsPhoneCall mRingingCall = new ImsPhoneCall(this);
-    ImsPhoneCall mForegroundCall = new ImsPhoneCall(this);
-    ImsPhoneCall mBackgroundCall = new ImsPhoneCall(this);
-    ImsPhoneCall mHandoverCall = new ImsPhoneCall(this);
+    final ImsPhoneCall mRingingCall = new ImsPhoneCall(this, ImsPhoneCall.CONTEXT_RINGING);
+    final ImsPhoneCall mForegroundCall = new ImsPhoneCall(this, ImsPhoneCall.CONTEXT_FOREGROUND);
+    final ImsPhoneCall mBackgroundCall = new ImsPhoneCall(this, ImsPhoneCall.CONTEXT_BACKGROUND);
+    final ImsPhoneCall mHandoverCall = new ImsPhoneCall(this, ImsPhoneCall.CONTEXT_HANDOVER);
 
     private ImsPhoneConnection mPendingMO;
     private int mClirMode = CommandsInterface.CLIR_DEFAULT;
@@ -174,7 +174,7 @@
     private boolean mIsInEmergencyCall = false;
 
     private int pendingCallClirMode;
-    private int pendingCallVideoState;
+    private int mPendingCallVideoState;
     private boolean pendingCallInEcm = false;
     private boolean mSwitchingFgAndBgCalls = false;
     private ImsCall mCallExpectedToResume = null;
@@ -275,17 +275,18 @@
     }
 
     Connection
-    dial(String dialString, int videoState) throws CallStateException {
+    dial(String dialString, int videoState, Bundle intentExtras) throws CallStateException {
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mPhone.getContext());
         int oirMode = sp.getInt(PhoneBase.CLIR_KEY, CommandsInterface.CLIR_DEFAULT);
-        return dial(dialString, oirMode, videoState);
+        return dial(dialString, oirMode, videoState, intentExtras);
     }
 
     /**
      * oirMode is one of the CLIR_ constants
      */
     synchronized Connection
-    dial(String dialString, int clirMode, int videoState) throws CallStateException {
+    dial(String dialString, int clirMode, int videoState, Bundle intentExtras)
+            throws CallStateException {
         boolean isPhoneInEcmMode = SystemProperties.getBoolean(
                 TelephonyProperties.PROPERTY_INECM_MODE, false);
         boolean isEmergencyNumber = PhoneNumberUtils.isEmergencyNumber(dialString);
@@ -319,6 +320,8 @@
             }
             // foreground call is empty for the newly dialed connection
             holdBeforeDial = true;
+            // Cache the video state for pending MO call.
+            mPendingCallVideoState = videoState;
             switchWaitingOrHoldingAndActive();
         }
 
@@ -360,7 +363,7 @@
                 }
                 mPhone.setOnEcbModeExitResponse(this, EVENT_EXIT_ECM_RESPONSE_CDMA, null);
                 pendingCallClirMode = clirMode;
-                pendingCallVideoState = videoState;
+                mPendingCallVideoState = videoState;
                 pendingCallInEcm = true;
             }
         }
@@ -414,13 +417,7 @@
                     callees, mImsCallListener);
             conn.setImsCall(imsCall);
 
-            IImsVideoCallProvider imsVideoCallProvider =
-                    imsCall.getCallSession().getVideoCallProvider();
-            if (imsVideoCallProvider != null) {
-                ImsVideoCallProviderWrapper imsVideoCallProviderWrapper =
-                        new ImsVideoCallProviderWrapper(imsVideoCallProvider);
-                conn.setVideoProvider(imsVideoCallProviderWrapper);
-            }
+            setVideoCallProvider(conn, imsCall);
         } catch (ImsException e) {
             loge("dialInternal : " + e);
             conn.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED);
@@ -447,6 +444,8 @@
         if ((mRingingCall.getState() == ImsPhoneCall.State.WAITING)
                 && mForegroundCall.getState().isAlive()) {
             setMute(false);
+            // Cache video state for pending MT call.
+            mPendingCallVideoState = videoState;
             switchWaitingOrHoldingAndActive();
         } else if (mRingingCall.getState().isRinging()) {
             if (DBG) log("acceptCall: incoming...");
@@ -478,6 +477,17 @@
         }
     }
 
+
+    private void switchAfterConferenceSuccess() {
+        if (DBG) log("switchAfterConferenceSuccess fg =" + mForegroundCall.getState() +
+                ", bg = " + mBackgroundCall.getState());
+
+        if (mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING) {
+            log("switchAfterConferenceSuccess");
+            mForegroundCall.switchWith(mBackgroundCall);
+        }
+    }
+
     void
     switchWaitingOrHoldingAndActive() throws CallStateException {
         if (DBG) log("switchWaitingOrHoldingAndActive");
@@ -502,6 +512,11 @@
             // be resumed.
             try {
                 imsCall.hold();
+
+                // If there is no background call to resume, then don't expect there to be a switch.
+                if (mCallExpectedToResume == null) {
+                    mSwitchingFgAndBgCalls = false;
+                }
             } catch (ImsException e) {
                 mForegroundCall.switchWith(mBackgroundCall);
                 throw new CallStateException(e.getMessage());
@@ -529,8 +544,21 @@
 
         // Keep track of the connect time of the earliest call so that it can be set on the
         // {@code ImsConference} when it is created.
-        long conferenceConnectTime = Math.min(mForegroundCall.getEarliestConnectTime(),
-                mBackgroundCall.getEarliestConnectTime());
+        long foregroundConnectTime = mForegroundCall.getEarliestConnectTime();
+        long backgroundConnectTime = mBackgroundCall.getEarliestConnectTime();
+        long conferenceConnectTime;
+        if (foregroundConnectTime > 0 && backgroundConnectTime > 0) {
+            conferenceConnectTime = Math.min(mForegroundCall.getEarliestConnectTime(),
+                    mBackgroundCall.getEarliestConnectTime());
+            log("conference - using connect time = " + conferenceConnectTime);
+        } else if (foregroundConnectTime > 0) {
+            log("conference - bg call connect time is 0; using fg = " + foregroundConnectTime);
+            conferenceConnectTime = foregroundConnectTime;
+        } else {
+            log("conference - fg call connect time is 0; using bg = " + backgroundConnectTime);
+            conferenceConnectTime = backgroundConnectTime;
+        }
+
         ImsPhoneConnection foregroundConnection = mForegroundCall.getFirstConnection();
         if (foregroundConnection != null) {
             foregroundConnection.setConferenceConnectTime(conferenceConnectTime);
@@ -811,7 +839,10 @@
             } else if (mRingingCall.getState() == ImsPhoneCall.State.WAITING) {
                 //accept waiting call after holding background call
                 ImsCall imsCall = mRingingCall.getImsCall();
-                if (imsCall != null) imsCall.accept(ImsCallProfile.CALL_TYPE_VOICE);
+                if (imsCall != null) {
+                    imsCall.accept(
+                        ImsCallProfile.getCallTypeFromVideoState(mPendingCallVideoState));
+                }
             } else {
                 //Just resume background call.
                 //To distinguish resuming call with swapping calls
@@ -862,7 +893,7 @@
 
     }
 
-    private synchronized ImsPhoneConnection findConnection(ImsCall imsCall) {
+    private synchronized ImsPhoneConnection findConnection(final ImsCall imsCall) {
         for (ImsPhoneConnection conn : mConnections) {
             if (conn.getImsCall() == imsCall) {
                 return conn;
@@ -881,6 +912,19 @@
 
     private void processCallStateChange(ImsCall imsCall, ImsPhoneCall.State state, int cause) {
         if (DBG) log("processCallStateChange " + imsCall + " state=" + state + " cause=" + cause);
+        // This method is called on onCallUpdate() where there is not necessarily a call state
+        // change. In these situations, we'll ignore the state related updates and only process
+        // the change in media capabilities (as expected).  The default is to not ignore state
+        // changes so we do not change existing behavior.
+        processCallStateChange(imsCall, state, cause, false /* do not ignore state update */);
+    }
+
+    private void processCallStateChange(ImsCall imsCall, ImsPhoneCall.State state, int cause,
+            boolean ignoreState) {
+        if (DBG) {
+            log("processCallStateChange state=" + state + " cause=" + cause
+                    + " ignoreState=" + ignoreState);
+        }
 
         if (imsCall == null) return;
 
@@ -892,8 +936,17 @@
             return;
         }
 
-        changed = conn.update(imsCall, state);
+        // processCallStateChange is triggered for onCallUpdated as well.
+        // onCallUpdated should not modify the state of the call
+        // It should modify only other capabilities of call through updateMediaCapabilities
+        // State updates will be triggered through individual callbacks
+        // i.e. onCallHeld, onCallResume, etc and conn.update will be responsible for the update
+        if (ignoreState) {
+            conn.updateMediaCapabilities(imsCall);
+            return;
+        }
 
+        changed = conn.update(imsCall, state);
         if (state == ImsPhoneCall.State.DISCONNECTED) {
             changed = conn.onDisconnect(cause) || changed;
             //detach the disconnected connections
@@ -991,6 +1044,19 @@
                     DisconnectCause.NOT_DISCONNECTED);
         }
 
+        @Override
+        public void onCallUpdated(ImsCall imsCall) {
+            if (DBG) log("onCallUpdated");
+            if (imsCall == null) {
+                return;
+            }
+            ImsPhoneConnection conn = findConnection(imsCall);
+            if (conn != null) {
+                processCallStateChange(imsCall, conn.getCall().mState,
+                        DisconnectCause.NOT_DISCONNECTED, true /*ignore state update*/);
+            }
+        }
+
         /**
          * onCallStartFailed will be invoked when:
          * case 1) Dialing fails
@@ -1048,7 +1114,13 @@
 
         @Override
         public void onCallHeld(ImsCall imsCall) {
-            if (DBG) log("onCallHeld");
+            if (DBG) {
+                if (mForegroundCall.getImsCall() == imsCall) {
+                    log("onCallHeld (fg) " + imsCall);
+                } else if (mBackgroundCall.getImsCall() == imsCall) {
+                    log("onCallHeld (bg) " + imsCall);
+                }
+            }
 
             synchronized (mSyncHold) {
                 ImsPhoneCall.State oldState = mBackgroundCall.getState();
@@ -1115,6 +1187,10 @@
             // is not the one we expected, we likely had a resume failure and we need to swap the
             // FG and BG calls back.
             if (mSwitchingFgAndBgCalls && imsCall != mCallExpectedToResume) {
+                if (DBG) {
+                    log("onCallResumed : switching " + mForegroundCall + " with "
+                            + mBackgroundCall);
+                }
                 mForegroundCall.switchWith(mBackgroundCall);
                 mSwitchingFgAndBgCalls = false;
                 mCallExpectedToResume = null;
@@ -1125,10 +1201,13 @@
 
         @Override
         public void onCallResumeFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) {
-            // TODO : What should be done?
             // If we are in the midst of swapping the FG and BG calls and we got a resume fail, we
             // need to swap back the FG and BG calls.
             if (mSwitchingFgAndBgCalls && imsCall == mCallExpectedToResume) {
+                if (DBG) {
+                    log("onCallResumeFailed : switching " + mForegroundCall + " with "
+                            + mBackgroundCall);
+                }
                 mForegroundCall.switchWith(mBackgroundCall);
                 mCallExpectedToResume = null;
                 mSwitchingFgAndBgCalls = false;
@@ -1160,27 +1239,75 @@
         }
 
         @Override
-        public void onCallMerged(ImsCall call, boolean swapCalls) {
+        public void onCallMerged(final ImsCall call, final ImsCall peerCall, boolean swapCalls) {
             if (DBG) log("onCallMerged");
 
-            mForegroundCall.merge(mBackgroundCall, mForegroundCall.getState());
+            ImsPhoneCall foregroundImsPhoneCall = findConnection(call).getCall();
+            ImsPhoneConnection peerConnection = findConnection(peerCall);
+            ImsPhoneCall peerImsPhoneCall = peerConnection == null ? null
+                    : peerConnection.getCall();
+
             if (swapCalls) {
-                try {
-                    switchWaitingOrHoldingAndActive();
-                } catch (CallStateException e) {
-                    if (Phone.DEBUG_PHONE) {
-                        loge("Failed swap fg and bg calls on merge exception=" + e);
+                switchAfterConferenceSuccess();
+            }
+            foregroundImsPhoneCall.merge(peerImsPhoneCall, ImsPhoneCall.State.ACTIVE);
+
+            // TODO Temporary code. Remove the try-catch block from the runnable once thread
+            // synchronization is fixed.
+            Runnable r = new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        final ImsPhoneConnection conn = findConnection(call);
+                        log("onCallMerged: ImsPhoneConnection=" + conn);
+                        log("onCallMerged: CurrentVideoProvider=" + conn.getVideoProvider());
+                        setVideoCallProvider(conn, call);
+                        log("onCallMerged: CurrentVideoProvider=" + conn.getVideoProvider());
+                    } catch (Exception e) {
+                        loge("onCallMerged: exception " + e);
                     }
                 }
+            };
+
+            ImsPhoneCallTracker.this.post(r);
+
+            // After merge complete, update foreground as Active
+            // and background call as Held, if background call exists
+            processCallStateChange(mForegroundCall.getImsCall(), ImsPhoneCall.State.ACTIVE,
+                    DisconnectCause.NOT_DISCONNECTED);
+            if (peerConnection != null) {
+                processCallStateChange(mBackgroundCall.getImsCall(), ImsPhoneCall.State.HOLDING,
+                    DisconnectCause.NOT_DISCONNECTED);
             }
-            updatePhoneState();
-            mPhone.notifyPreciseCallStateChanged();
+
+            // Check if the merge was requested by an existing conference call. In that
+            // case, no further action is required.
+            if (!call.isMergeRequestedByConf()) {
+                log("onCallMerged :: calling onMultipartyStateChanged()");
+                onMultipartyStateChanged(call, true);
+            } else {
+                log("onCallMerged :: Merge requested by existing conference.");
+                // Reset the flag.
+                call.resetIsMergeRequestedByConf(false);
+            }
+            logState();
         }
 
         @Override
         public void onCallMergeFailed(ImsCall call, ImsReasonInfo reasonInfo) {
             if (DBG) log("onCallMergeFailed reasonInfo=" + reasonInfo);
+
+            // TODO: the call to notifySuppServiceFailed throws up the "merge failed" dialog
+            // We should move this into the InCallService so that it is handled appropriately
+            // based on the user facing UI.
             mPhone.notifySuppServiceFailed(Phone.SuppService.CONFERENCE);
+
+            // Start plumbing this even through Telecom so other components can take
+            // appropriate action.
+            ImsPhoneConnection conn = findConnection(call);
+            if (conn != null) {
+                conn.onConferenceMergeFailed();
+            }
         }
 
         /**
@@ -1204,6 +1331,42 @@
         public void onCallSessionTtyModeReceived(ImsCall call, int mode) {
             mPhone.onTtyModeReceived(mode);
         }
+
+        @Override
+        public void onCallHandover(ImsCall imsCall, int srcAccessTech, int targetAccessTech,
+            ImsReasonInfo reasonInfo) {
+            if (DBG) {
+                log("onCallHandover ::  srcAccessTech=" + srcAccessTech + ", targetAccessTech=" +
+                    targetAccessTech + ", reasonInfo=" + reasonInfo);
+            }
+        }
+
+        @Override
+        public void onCallHandoverFailed(ImsCall imsCall, int srcAccessTech, int targetAccessTech,
+            ImsReasonInfo reasonInfo) {
+            if (DBG) {
+                log("onCallHandoverFailed :: srcAccessTech=" + srcAccessTech +
+                    ", targetAccessTech=" + targetAccessTech + ", reasonInfo=" + reasonInfo);
+            }
+        }
+
+        /**
+         * Handles a change to the multiparty state for an {@code ImsCall}.  Notifies the associated
+         * {@link ImsPhoneConnection} of the change.
+         *
+         * @param imsCall The IMS call.
+         * @param isMultiParty {@code true} if the call became multiparty, {@code false}
+         *      otherwise.
+         */
+        @Override
+        public void onMultipartyStateChanged(ImsCall imsCall, boolean isMultiParty) {
+            if (DBG) log("onMultipartyStateChanged to " + (isMultiParty ? "Y" : "N"));
+
+            ImsPhoneConnection conn = findConnection(imsCall);
+            if (conn != null) {
+                conn.updateMultipartyState(isMultiParty);
+            }
+        }
     };
 
     /**
@@ -1282,10 +1445,16 @@
         }
 
         @Override
-        public void onImsDisconnected() {
-            if (DBG) log("onImsDisconnected");
+        public void onImsDisconnected(ImsReasonInfo imsReasonInfo) {
+            if (DBG) log("onImsDisconnected imsReasonInfo=" + imsReasonInfo);
             mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
             mPhone.setImsRegistered(false);
+            mPhone.processDisconnectReason(imsReasonInfo);
+        }
+
+        @Override
+        public void onImsProgressing() {
+            if (DBG) log("onImsProgressing");
         }
 
         @Override
@@ -1304,25 +1473,41 @@
         public void onFeatureCapabilityChanged(int serviceClass,
                 int[] enabledFeatures, int[] disabledFeatures) {
             if (serviceClass == ImsServiceClass.MMTEL) {
-                if (enabledFeatures[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE] ==
-                        ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE) {
-                    mIsVolteEnabled = true;
+                boolean tmpIsVideoCallEnabled = isVideoCallEnabled();
+                // Check enabledFeatures to determine capabilities. We ignore disabledFeatures.
+                for (int  i = ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE;
+                        i <= ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_WIFI; i++) {
+                    if (enabledFeatures[i] == i) {
+                        // If the feature is set to its own integer value it is enabled.
+                        if (DBG) log("onFeatureCapabilityChanged(" + i + ", " + mImsFeatureStrings[i] + "): value=true");
+                        mImsFeatureEnabled[i] = true;
+                    } else if (enabledFeatures[i]
+                            == ImsConfig.FeatureConstants.FEATURE_TYPE_UNKNOWN) {
+                        // FEATURE_TYPE_UNKNOWN indicates that a feature is disabled.
+                        if (DBG) log("onFeatureCapabilityChanged(" + i + ", " + mImsFeatureStrings[i] + "): value=false");
+                        mImsFeatureEnabled[i] = false;
+                    } else {
+                        // Feature has unknown state; it is not its own value or -1.
+                        if (DBG) {
+                            loge("onFeatureCapabilityChanged(" + i + ", " +mImsFeatureStrings[i] + "): unexpectedValue="
+                                + enabledFeatures[i]);
+                        }
+                    }
                 }
-                if (enabledFeatures[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE] ==
-                        ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE) {
-                    mIsVtEnabled = true;
+                if (tmpIsVideoCallEnabled != isVideoCallEnabled()) {
+                    mPhone.notifyForVideoCapabilityChanged(isVideoCallEnabled());
                 }
-                if (disabledFeatures[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE] ==
-                        ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE) {
-                    mIsVolteEnabled = false;
+
+                // TODO: Use the ImsCallSession or ImsCallProfile to tell the initial Wifi state and
+                // {@link ImsCallSession.Listener#callSessionHandover} to listen for changes to
+                // wifi capability caused by a handover.
+                if (DBG) log("onFeatureCapabilityChanged: isVowifiEnabled=" + isVowifiEnabled());
+                for (ImsPhoneConnection connection : mConnections) {
+                    connection.updateWifiState();
                 }
-                if (disabledFeatures[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE] ==
-                        ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE) {
-                    mIsVtEnabled = false;
-                }
+
+                mPhone.onFeatureCapabilityChanged();
             }
-            if (DBG) log("onFeatureCapabilityChanged, mIsVolteEnabled = " +  mIsVolteEnabled
-                    + " mIsVtEnabled = " + mIsVtEnabled);
         }
     };
 
@@ -1407,13 +1592,14 @@
                 }
                 break;
             case EVENT_DIAL_PENDINGMO:
-                dialInternal(mPendingMO, mClirMode, VideoProfile.VideoState.AUDIO_ONLY);
+                dialInternal(mPendingMO, mClirMode, mPendingCallVideoState);
                 break;
 
             case EVENT_EXIT_ECM_RESPONSE_CDMA:
                 // no matter the result, we still do the same here
                 if (pendingCallInEcm) {
-                    dialInternal(mPendingMO, pendingCallClirMode, pendingCallVideoState);
+                    dialInternal(mPendingMO, pendingCallClirMode,
+                            mPendingCallVideoState);
                     pendingCallInEcm = false;
                 }
                 mPhone.unsetOnEcbModeExitResponse(this);
@@ -1430,6 +1616,33 @@
         Rlog.e(LOG_TAG, "[ImsPhoneCallTracker] " + msg);
     }
 
+    /**
+     * Logs the current state of the ImsPhoneCallTracker.  Useful for debugging issues with
+     * call tracking.
+     */
+    /* package */
+    void logState() {
+        if (!VERBOSE_STATE_LOGGING) {
+            return;
+        }
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("Current IMS PhoneCall State:\n");
+        sb.append(" Foreground: ");
+        sb.append(mForegroundCall);
+        sb.append("\n");
+        sb.append(" Background: ");
+        sb.append(mBackgroundCall);
+        sb.append("\n");
+        sb.append(" Ringing: ");
+        sb.append(mRingingCall);
+        sb.append("\n");
+        sb.append(" Handover: ");
+        sb.append(mHandoverCall);
+        sb.append("\n");
+        Rlog.v(LOG_TAG, sb.toString());
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("ImsPhoneCallTracker extends:");
@@ -1466,14 +1679,31 @@
     }
 
     public boolean isVolteEnabled() {
-        return mIsVolteEnabled;
+        return mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE];
     }
 
-    public boolean isVtEnabled() {
-        return mIsVtEnabled;
+    public boolean isVowifiEnabled() {
+        return mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI];
     }
+
+    public boolean isVideoCallEnabled() {
+        return (mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE]
+                || mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_WIFI]);
+    }
+
     @Override
     public PhoneConstants.State getState() {
         return mState;
     }
+
+    private void setVideoCallProvider(ImsPhoneConnection conn, ImsCall imsCall)
+            throws RemoteException {
+        IImsVideoCallProvider imsVideoCallProvider =
+                imsCall.getCallSession().getVideoCallProvider();
+        if (imsVideoCallProvider != null) {
+            ImsVideoCallProviderWrapper imsVideoCallProviderWrapper =
+                    new ImsVideoCallProviderWrapper(imsVideoCallProvider);
+            conn.setVideoProvider(imsVideoCallProviderWrapper);
+        }
+    }
 }
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
index cd0920b..7172993 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCommandInterface.java
@@ -608,4 +608,20 @@
     @Override
     public void getRadioCapability(Message response) {
     }
+
+    @Override
+    public void startLceService(int reportIntervalMs, boolean pullMode, Message result) {
+    }
+
+    @Override
+    public void stopLceService(Message result) {
+    }
+
+    @Override
+    public void pullLceData(Message result) {
+    }
+
+    @Override
+    public void getModemActivityInfo(Message result) {
+    }
 }
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
index 2c93f5a..bd2dd40 100755
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneConnection.java
@@ -29,6 +29,7 @@
 import android.telephony.DisconnectCause;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.Rlog;
+import android.text.TextUtils;
 
 import com.android.ims.ImsException;
 import com.android.ims.ImsStreamMediaProfile;
@@ -116,7 +117,8 @@
 
     /** This is probably an MT call */
     /*package*/
-    ImsPhoneConnection(Context context, ImsCall imsCall, ImsPhoneCallTracker ct, ImsPhoneCall parent) {
+    ImsPhoneConnection(Context context, ImsCall imsCall, ImsPhoneCallTracker ct,
+            ImsPhoneCall parent) {
         createWakeLock(context);
         acquireWakeLock();
 
@@ -131,7 +133,6 @@
                     imsCall.getCallProfile().getCallExtraInt(ImsCallProfile.EXTRA_OIR));
             mCnapNamePresentation = ImsCallProfile.OIRToPresentation(
                     imsCall.getCallProfile().getCallExtraInt(ImsCallProfile.EXTRA_CNAP));
-
             updateMediaCapabilities(imsCall);
         } else {
             mNumberPresentation = PhoneConstants.PRESENTATION_UNKNOWN;
@@ -144,13 +145,16 @@
 
         //mIndex = index;
 
+        updateWifiState();
+
         mParent = parent;
         mParent.attach(this, ImsPhoneCall.State.INCOMING);
     }
 
     /** This is an MO call, created when dialing */
     /*package*/
-    ImsPhoneConnection(Context context, String dialString, ImsPhoneCallTracker ct, ImsPhoneCall parent) {
+    ImsPhoneConnection(Context context, String dialString, ImsPhoneCallTracker ct,
+            ImsPhoneCall parent) {
         createWakeLock(context);
         acquireWakeLock();
 
@@ -221,6 +225,11 @@
         mCause = cause;
     }
 
+    @Override
+    public String getVendorDisconnectCause() {
+      return null;
+    }
+
     public ImsPhoneCallTracker getOwner () {
         return mOwner;
     }
@@ -467,7 +476,8 @@
             // arg1 is the character that was/is being processed
             notifyMessage.arg1 = c;
 
-            //Rlog.v(LOG_TAG, "##### processNextPostDialChar: send msg to postDialHandler, arg1=" + c);
+            //Rlog.v(LOG_TAG,
+            //      "##### processNextPostDialChar: send msg to postDialHandler, arg1=" + c);
             notifyMessage.sendToTarget();
         }
     }
@@ -535,6 +545,21 @@
         return mImsCall != null && mImsCall.isMultiparty();
     }
 
+    /**
+     * Where {@link #isMultiparty()} is {@code true}, determines if this {@link ImsCall} is the
+     * origin of the conference call (i.e. {@code #isConferenceHost()} is {@code true}), or if this
+     * {@link ImsCall} is a member of a conference hosted on another device.
+     *
+     * @return {@code true} if this call is the origin of the conference call it is a member of,
+     *      {@code false} otherwise.
+     */
+    public boolean isConferenceHost() {
+        if (mImsCall == null) {
+            return false;
+        }
+        return mImsCall.isConferenceHost();
+    }
+
     /*package*/ ImsCall getImsCall() {
         return mImsCall;
     }
@@ -553,6 +578,15 @@
      */
     /*package*/ boolean update(ImsCall imsCall, ImsPhoneCall.State state) {
         if (state == ImsPhoneCall.State.ACTIVE) {
+            // If the state of the call is active, but there is a pending request to the RIL to hold
+            // the call, we will skip this update.  This is really a signalling delay or failure
+            // from the RIL, but we will prevent it from going through as we will end up erroneously
+            // making this call active when really it should be on hold.
+            if (imsCall.isPendingHold()) {
+                Rlog.w(LOG_TAG, "update : state is ACTIVE, but call is pending hold, skipping");
+                return false;
+            }
+
             if (mParent.getState().isRinging() || mParent.getState().isDialing()) {
                 onConnectedInOrOut();
             }
@@ -572,7 +606,10 @@
 
         boolean updateParent = mParent.update(this, imsCall, state);
         boolean updateMediaCapabilities = updateMediaCapabilities(imsCall);
-        return updateParent || updateMediaCapabilities;
+        boolean updateWifiState = updateWifiState();
+        boolean updateAddressDisplay = updateAddressDisplay(imsCall);
+
+        return updateParent || updateMediaCapabilities || updateWifiState || updateAddressDisplay;
     }
 
     @Override
@@ -619,13 +656,63 @@
     }
 
     /**
+     * Check for a change in the address display related fields for the {@link ImsCall}, and
+     * update the {@link ImsPhoneConnection} with this information.
+     *
+     * @param imsCall The call to check for changes in address display fields.
+     * @return Whether the address display fields have been changed.
+     */
+    private boolean updateAddressDisplay(ImsCall imsCall) {
+        if (imsCall == null) {
+            return false;
+        }
+
+        boolean changed = false;
+        ImsCallProfile callProfile = imsCall.getCallProfile();
+        if (callProfile != null) {
+            String address = callProfile.getCallExtra(ImsCallProfile.EXTRA_OI);
+            String name = callProfile.getCallExtra(ImsCallProfile.EXTRA_CNA);
+            int nump = ImsCallProfile.OIRToPresentation(
+                    callProfile.getCallExtraInt(ImsCallProfile.EXTRA_OIR));
+            int namep = ImsCallProfile.OIRToPresentation(
+                    callProfile.getCallExtraInt(ImsCallProfile.EXTRA_CNAP));
+            if (Phone.DEBUG_PHONE) {
+                Rlog.d(LOG_TAG, "address = " +  address + " name = " + name +
+                        " nump = " + nump + " namep = " + namep);
+            }
+            if(equalsHandlesNulls(mAddress, address)) {
+                mAddress = address;
+                changed = true;
+            }
+            if (TextUtils.isEmpty(name)) {
+                if (!TextUtils.isEmpty(mCnapName)) {
+                    mCnapName = "";
+                    changed = true;
+                }
+            } else if (!name.equals(mCnapName)) {
+                mCnapName = name;
+                changed = true;
+            }
+            if (mNumberPresentation != nump) {
+                mNumberPresentation = nump;
+                changed = true;
+            }
+            if (mCnapNamePresentation != namep) {
+                mCnapNamePresentation = namep;
+                changed = true;
+            }
+        }
+        return changed;
+    }
+
+    /**
      * Check for a change in the video capabilities and audio quality for the {@link ImsCall}, and
      * update the {@link ImsPhoneConnection} with this information.
      *
      * @param imsCall The call to check for changes in media capabilities.
      * @return Whether the media capabilities have been changed.
      */
-    private boolean updateMediaCapabilities(ImsCall imsCall) {
+    public boolean updateMediaCapabilities(ImsCall imsCall) {
         if (imsCall == null) {
             return false;
         }
@@ -641,10 +728,11 @@
             ImsCallProfile remoteCallProfile = imsCall.getRemoteCallProfile();
 
             if (negotiatedCallProfile != null) {
-                int callType = negotiatedCallProfile.mCallType;
+                int oldVideoState = getVideoState();
+                int newVideoState = ImsCallProfile
+                        .getVideoStateFromImsCallProfile(negotiatedCallProfile);
 
-                int newVideoState = ImsCallProfile.getVideoStateFromCallType(callType);
-                if (getVideoState() != newVideoState) {
+                if (oldVideoState != newVideoState) {
                     setVideoState(newVideoState);
                     changed = true;
                 }
@@ -660,6 +748,16 @@
                 }
             }
 
+            if (remoteCallProfile != null) {
+                    boolean newRemoteVideoCapable = remoteCallProfile.mCallType
+                            == ImsCallProfile.CALL_TYPE_VT;
+
+                    if (isRemoteVideoCapable() != newRemoteVideoCapable) {
+                        setRemoteVideoCapable(newRemoteVideoCapable);
+                        changed = true;
+                    }
+            }
+
             int newAudioQuality =
                     getAudioQualityFromCallProfile(localCallProfile, remoteCallProfile);
             if (getAudioQuality() != newAudioQuality) {
@@ -674,6 +772,21 @@
     }
 
     /**
+     * Check for a change in the wifi state of the ImsPhoneCallTracker and update the
+     * {@link ImsPhoneConnection} with this information.
+     *
+     * @return Whether the ImsPhoneCallTracker's usage of wifi has been changed.
+     */
+    public boolean updateWifiState() {
+        Rlog.d(LOG_TAG, "updateWifiState: " + mOwner.isVowifiEnabled());
+        if (isWifi() != mOwner.isVowifiEnabled()) {
+            setWifi(mOwner.isVowifiEnabled());
+            return true;
+        }
+        return false;
+    }
+
+    /**
      * Determines the {@link ImsPhoneConnection} audio quality based on the local and remote
      * {@link ImsCallProfile}. If indicate a HQ audio call if the local stream profile
      * indicates AMR_WB or EVRC_WB and there is no remote restrict cause.
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
index fbdd572..ac7f76e 100644
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneMmiCode.java
@@ -706,10 +706,10 @@
                 throw new CallStateException(ImsPhone.CS_FALLBACK);
             } else if (isServiceCodeCallForwarding(mSc)) {
                 Rlog.d(LOG_TAG, "is CF");
-                // service group is not supported
 
                 String dialingNumber = mSia;
                 int reason = scToCallForwardReason(mSc);
+                int serviceClass = siToServiceClass(mSib);
                 int time = siToTime(mSic);
 
                 if (isInterrogate()) {
@@ -750,7 +750,7 @@
 
                     Rlog.d(LOG_TAG, "is CF setCallForward");
                     mPhone.setCallForwardingOption(cfAction, reason,
-                            dialingNumber, time, obtainMessage(
+                            dialingNumber, serviceClass, time, obtainMessage(
                                     EVENT_SET_CFF_COMPLETE,
                                     isSettingUnconditional,
                                     isEnableDesired, this));
@@ -890,9 +890,10 @@
                 }
             } else if (mSc != null && mSc.equals(SC_WAIT)) {
                 // sia = basic service group
-                // service group is not supported
+                int serviceClass = siToServiceClass(mSib);
+
                 if (isActivate() || isDeactivate()) {
-                    mPhone.setCallWaiting(isActivate(),
+                    mPhone.setCallWaiting(isActivate(), serviceClass,
                             obtainMessage(EVENT_SET_COMPLETE, this));
                 } else if (isInterrogate()) {
                     mPhone.getCallWaiting(obtainMessage(EVENT_QUERY_COMPLETE, this));
diff --git a/src/java/com/android/internal/telephony/sip/SipCommandInterface.java b/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
index 889cc8f..8fd036d 100644
--- a/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
+++ b/src/java/com/android/internal/telephony/sip/SipCommandInterface.java
@@ -610,4 +610,21 @@
     @Override
     public void requestShutdown(Message result) {
     }
+
+    @Override
+    public void startLceService(int reportIntervalMs, boolean pullMode, Message result) {
+    }
+
+    @Override
+    public void stopLceService(Message result) {
+    }
+
+    @Override
+    public void pullLceData(Message result) {
+    }
+
+    @Override
+    public void getModemActivityInfo(Message result) {
+    }
+
 }
diff --git a/src/java/com/android/internal/telephony/sip/SipConnectionBase.java b/src/java/com/android/internal/telephony/sip/SipConnectionBase.java
index 8d8a554..c182981 100644
--- a/src/java/com/android/internal/telephony/sip/SipConnectionBase.java
+++ b/src/java/com/android/internal/telephony/sip/SipConnectionBase.java
@@ -141,6 +141,11 @@
     }
 
     @Override
+    public String getVendorDisconnectCause() {
+      return null;
+    }
+
+    @Override
     public PostDialState getPostDialState() {
         if (VDBG) log("getPostDialState: ret=" + mPostDialState);
         return mPostDialState;
diff --git a/src/java/com/android/internal/telephony/sip/SipPhone.java b/src/java/com/android/internal/telephony/sip/SipPhone.java
index 6edee55..beb02c6 100644
--- a/src/java/com/android/internal/telephony/sip/SipPhone.java
+++ b/src/java/com/android/internal/telephony/sip/SipPhone.java
@@ -26,6 +26,7 @@
 import android.net.sip.SipProfile;
 import android.net.sip.SipSession;
 import android.os.AsyncResult;
+import android.os.Bundle;
 import android.os.Message;
 import android.telephony.DisconnectCause;
 import android.telephony.PhoneNumberUtils;
@@ -213,7 +214,7 @@
 
     @Override
     public void switchHoldingAndActive() throws CallStateException {
-        if (DBG) log("dialInternal: switch fg and bg");
+        if (DBG) log("switchHoldingAndActive: switch fg and bg");
         synchronized (SipPhone.class) {
             mForegroundCall.switchWith(mBackgroundCall);
             if (mBackgroundCall.getState().isAlive()) mBackgroundCall.hold();
diff --git a/src/java/com/android/internal/telephony/sip/SipPhoneBase.java b/src/java/com/android/internal/telephony/sip/SipPhoneBase.java
index c8a6d93..bcd520f 100755
--- a/src/java/com/android/internal/telephony/sip/SipPhoneBase.java
+++ b/src/java/com/android/internal/telephony/sip/SipPhoneBase.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.net.LinkProperties;
 import android.os.AsyncResult;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
 import android.os.Registrant;
@@ -70,7 +71,7 @@
     public abstract Call getRingingCall();
 
     @Override
-    public Connection dial(String dialString, UUSInfo uusInfo, int videoState)
+    public Connection dial(String dialString, UUSInfo uusInfo, int videoState, Bundle intentExtras)
             throws CallStateException {
         // ignore UUSInfo
         return dial(dialString, videoState);
@@ -304,6 +305,11 @@
     }
 
     @Override
+    public String getGroupIdLevel2() {
+        return null;
+    }
+
+    @Override
     public String getIccSerialNumber() {
         return null;
     }
@@ -502,6 +508,16 @@
         return null;
     }
 
+    /**
+     * Determines if video calling is enabled.  Always {@code false} for SIP.
+     *
+     * @return {@code false} since SIP does not support video calling.
+     */
+    @Override
+    public boolean isVideoEnabled() {
+        return false;
+    }
+
     void updatePhoneState() {
         PhoneConstants.State oldState = mState;
 
diff --git a/src/java/com/android/internal/telephony/test/SimulatedCommands.java b/src/java/com/android/internal/telephony/test/SimulatedCommands.java
index 71fd66e..e0c2b79 100644
--- a/src/java/com/android/internal/telephony/test/SimulatedCommands.java
+++ b/src/java/com/android/internal/telephony/test/SimulatedCommands.java
@@ -1767,4 +1767,24 @@
     public void requestShutdown(Message result) {
         setRadioState(RadioState.RADIO_UNAVAILABLE);
     }
+
+    @Override
+    public void startLceService(int report_interval_ms, boolean pullMode, Message result) {
+        unimplemented(result);
+    }
+
+    @Override
+    public void stopLceService(Message result) {
+        unimplemented(result);
+    }
+
+    @Override
+    public void pullLceData(Message result) {
+        unimplemented(result);
+    }
+
+    @Override
+    public void getModemActivityInfo(Message result) {
+        unimplemented(result);
+    }
 }
diff --git a/src/java/com/android/internal/telephony/uicc/AdnRecordLoader.java b/src/java/com/android/internal/telephony/uicc/AdnRecordLoader.java
index 8df8216..5e3dcb4 100644
--- a/src/java/com/android/internal/telephony/uicc/AdnRecordLoader.java
+++ b/src/java/com/android/internal/telephony/uicc/AdnRecordLoader.java
@@ -24,6 +24,8 @@
 import android.os.Message;
 import android.telephony.Rlog;
 
+import com.android.internal.telephony.uicc.IccConstants;
+
 public class AdnRecordLoader extends Handler {
     final static String LOG_TAG = "AdnRecordLoader";
     final static boolean VDBG = false;
@@ -64,6 +66,14 @@
         mFh = fh;
     }
 
+    private String getEFPath(int efid) {
+        if (efid == IccConstants.EF_ADN) {
+            return IccConstants.MF_SIM + IccConstants.DF_TELECOM;
+        }
+
+        return null;
+    }
+
     /**
      * Resulting AdnRecord is placed in response.obj.result
      * or response.obj.exception is set
@@ -76,10 +86,9 @@
         mRecordNumber = recordNumber;
         mUserResponse = response;
 
-        mFh.loadEFLinearFixed(
-                    ef, recordNumber,
-                    obtainMessage(EVENT_ADN_LOAD_DONE));
-
+       mFh.loadEFLinearFixed(
+               ef, getEFPath(ef), recordNumber,
+               obtainMessage(EVENT_ADN_LOAD_DONE));
     }
 
 
@@ -94,10 +103,13 @@
         mExtensionEF = extensionEF;
         mUserResponse = response;
 
+        /* If we are loading from EF_ADN, specifically
+         * specify the path as well, since, on some cards,
+         * the fileid is not unique.
+         */
         mFh.loadEFLinearFixedAll(
-                    ef,
-                    obtainMessage(EVENT_ADN_LOAD_ALL_DONE));
-
+                ef, getEFPath(ef),
+                obtainMessage(EVENT_ADN_LOAD_ALL_DONE));
     }
 
     /**
@@ -120,10 +132,10 @@
         mRecordNumber = recordNumber;
         mUserResponse = response;
         mPin2 = pin2;
-
-        mFh.getEFLinearRecordSize( ef,
-            obtainMessage(EVENT_EF_LINEAR_RECORD_SIZE_DONE, adn));
-    }
+ 
+        mFh.getEFLinearRecordSize( ef, getEFPath(ef),
+                obtainMessage(EVENT_EF_LINEAR_RECORD_SIZE_DONE, adn));
+     }
 
     //***** Overridden from Handler
 
@@ -163,7 +175,8 @@
                                 ar.exception);
                     }
 
-                    mFh.updateEFLinearFixed(mEf, mRecordNumber,
+
+                    mFh.updateEFLinearFixed(mEf, getEFPath(mEf), mRecordNumber,
                             data, mPin2, obtainMessage(EVENT_UPDATE_RECORD_DONE));
 
                     mPendingExtLoads = 1;
diff --git a/src/java/com/android/internal/telephony/uicc/IccCardProxy.java b/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
index f18640e..ce8a671 100644
--- a/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
+++ b/src/java/com/android/internal/telephony/uicc/IccCardProxy.java
@@ -362,7 +362,6 @@
                 mIccRecords = newRecords;
                 registerUiccCardEvents();
             }
-
             updateExternalState();
         }
     }
@@ -382,7 +381,15 @@
     }
 
     private void updateExternalState() {
-        if (mUiccCard == null || mUiccCard.getCardState() == CardState.CARDSTATE_ABSENT) {
+
+        // mUiccCard could be null at bootup, before valid card states have
+        // been received from UiccController.
+        if (mUiccCard == null) {
+            setExternalState(State.NOT_READY);
+            return;
+        }
+
+        if (mUiccCard.getCardState() == CardState.CARDSTATE_ABSENT) {
             if (mRadioOn) {
                 setExternalState(State.ABSENT);
             } else {
diff --git a/src/java/com/android/internal/telephony/uicc/IccConstants.java b/src/java/com/android/internal/telephony/uicc/IccConstants.java
index 8a9fc6b..061b28f 100644
--- a/src/java/com/android/internal/telephony/uicc/IccConstants.java
+++ b/src/java/com/android/internal/telephony/uicc/IccConstants.java
@@ -24,10 +24,12 @@
     static final int EF_ADN = 0x6F3A;
     static final int EF_FDN = 0x6F3B;
     static final int EF_GID1 = 0x6F3E;
+    static final int EF_GID2 = 0x6F3F;
     static final int EF_SDN = 0x6F49;
     static final int EF_EXT1 = 0x6F4A;
     static final int EF_EXT2 = 0x6F4B;
     static final int EF_EXT3 = 0x6F4C;
+    static final int EF_EXT5 = 0x6F4E;
     static final int EF_EXT6 = 0x6fc8;   // Ext record for EF[MBDN]
     static final int EF_MWIS = 0x6FCA;
     static final int EF_MBDN = 0x6fc7;
diff --git a/src/java/com/android/internal/telephony/uicc/IccFileHandler.java b/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
index a460819..881b4c3 100644
--- a/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
+++ b/src/java/com/android/internal/telephony/uicc/IccFileHandler.java
@@ -102,6 +102,7 @@
         int mEfid;
         int mRecordNum, mRecordSize, mCountRecords;
         boolean mLoadAll;
+        String mPath;
 
         Message mOnLoaded;
 
@@ -112,6 +113,23 @@
             mRecordNum = recordNum;
             mOnLoaded = onLoaded;
             mLoadAll = false;
+            mPath = null;
+        }
+
+        LoadLinearFixedContext(int efid, int recordNum, String path, Message onLoaded) {
+            mEfid = efid;
+            mRecordNum = recordNum;
+            mOnLoaded = onLoaded;
+            mLoadAll = false;
+            mPath = path;
+        }
+
+        LoadLinearFixedContext(int efid, String path, Message onLoaded) {
+            mEfid = efid;
+            mRecordNum = 1;
+            mLoadAll = true;
+            mOnLoaded = onLoaded;
+            mPath = path;
         }
 
         LoadLinearFixedContext(int efid, Message onLoaded) {
@@ -119,6 +137,7 @@
             mRecordNum = 1;
             mLoadAll = true;
             mOnLoaded = onLoaded;
+            mPath = null;
         }
     }
 
@@ -140,6 +159,27 @@
      * Load a record from a SIM Linear Fixed EF
      *
      * @param fileid EF id
+     * @param path Path of the EF on the card
+     * @param recordNum 1-based (not 0-based) record number
+     * @param onLoaded
+     *
+     * ((AsyncResult)(onLoaded.obj)).result is the byte[]
+     *
+     */
+    public void loadEFLinearFixed(int fileid, String path, int recordNum, Message onLoaded) {
+        String efPath = (path == null) ? getEFPath(fileid) : path;
+        Message response
+                = obtainMessage(EVENT_GET_RECORD_SIZE_DONE,
+                        new LoadLinearFixedContext(fileid, recordNum, efPath, onLoaded));
+
+        mCi.iccIOForApp(COMMAND_GET_RESPONSE, fileid, efPath,
+                        0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, mAid, response);
+    }
+
+    /**
+     * Load a record from a SIM Linear Fixed EF
+     *
+     * @param fileid EF id
      * @param recordNum 1-based (not 0-based) record number
      * @param onLoaded
      *
@@ -147,12 +187,7 @@
      *
      */
     public void loadEFLinearFixed(int fileid, int recordNum, Message onLoaded) {
-        Message response
-            = obtainMessage(EVENT_GET_RECORD_SIZE_DONE,
-                        new LoadLinearFixedContext(fileid, recordNum, onLoaded));
-
-        mCi.iccIOForApp(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),
-                        0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, mAid, response);
+        loadEFLinearFixed(fileid, getEFPath(fileid), recordNum, onLoaded);
     }
 
     /**
@@ -179,17 +214,51 @@
      * get record size for a linear fixed EF
      *
      * @param fileid EF id
+     * @param path Path of the EF on the card
+     * @param onLoaded ((AsnyncResult)(onLoaded.obj)).result is the recordSize[]
+     *        int[0] is the record length int[1] is the total length of the EF
+     *        file int[3] is the number of records in the EF file So int[0] *
+     *        int[3] = int[1]
+     */
+    public void getEFLinearRecordSize(int fileid, String path, Message onLoaded) {
+        String efPath = (path == null) ? getEFPath(fileid) : path;
+        Message response
+                = obtainMessage(EVENT_GET_EF_LINEAR_RECORD_SIZE_DONE,
+                        new LoadLinearFixedContext(fileid, efPath, onLoaded));
+        mCi.iccIOForApp(COMMAND_GET_RESPONSE, fileid, efPath,
+                    0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, mAid, response);
+    }
+
+    /**
+     * get record size for a linear fixed EF
+     *
+     * @param fileid EF id
      * @param onLoaded ((AsnyncResult)(onLoaded.obj)).result is the recordSize[]
      *        int[0] is the record length int[1] is the total length of the EF
      *        file int[3] is the number of records in the EF file So int[0] *
      *        int[3] = int[1]
      */
     public void getEFLinearRecordSize(int fileid, Message onLoaded) {
-        Message response
-                = obtainMessage(EVENT_GET_EF_LINEAR_RECORD_SIZE_DONE,
-                        new LoadLinearFixedContext(fileid, onLoaded));
-        mCi.iccIOForApp(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),
-                    0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, mAid, response);
+        getEFLinearRecordSize(fileid, getEFPath(fileid), onLoaded);
+    }
+
+    /**
+     * Load all records from a SIM Linear Fixed EF
+     *
+     * @param fileid EF id
+     * @param path Path of the EF on the card
+     * @param onLoaded
+     *
+     * ((AsyncResult)(onLoaded.obj)).result is an ArrayList<byte[]>
+     *
+     */
+    public void loadEFLinearFixedAll(int fileid, String path, Message onLoaded) {
+        String efPath = (path == null) ? getEFPath(fileid) : path;
+        Message response = obtainMessage(EVENT_GET_RECORD_SIZE_DONE,
+                        new LoadLinearFixedContext(fileid, efPath, onLoaded));
+
+        mCi.iccIOForApp(COMMAND_GET_RESPONSE, fileid, efPath,
+                        0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, mAid, response);
     }
 
     /**
@@ -202,11 +271,7 @@
      *
      */
     public void loadEFLinearFixedAll(int fileid, Message onLoaded) {
-        Message response = obtainMessage(EVENT_GET_RECORD_SIZE_DONE,
-                        new LoadLinearFixedContext(fileid,onLoaded));
-
-        mCi.iccIOForApp(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),
-                        0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, mAid, response);
+        loadEFLinearFixedAll(fileid, getEFPath(fileid), onLoaded);
     }
 
     /**
@@ -277,6 +342,24 @@
     /**
      * Update a record in a linear fixed EF
      * @param fileid EF id
+     * @param path Path of the EF on the card
+     * @param recordNum 1-based (not 0-based) record number
+     * @param data must be exactly as long as the record in the EF
+     * @param pin2 for CHV2 operations, otherwist must be null
+     * @param onComplete onComplete.obj will be an AsyncResult
+     *                   onComplete.obj.userObj will be a IccIoResult on success
+     */
+    public void updateEFLinearFixed(int fileid, String path, int recordNum, byte[] data,
+            String pin2, Message onComplete) {
+        String efPath = (path == null) ? getEFPath(fileid) : path;
+        mCi.iccIOForApp(COMMAND_UPDATE_RECORD, fileid, efPath,
+                        recordNum, READ_RECORD_MODE_ABSOLUTE, data.length,
+                        IccUtils.bytesToHexString(data), pin2, mAid, onComplete);
+    }
+
+    /**
+     * Update a record in a linear fixed EF
+     * @param fileid EF id
      * @param recordNum 1-based (not 0-based) record number
      * @param data must be exactly as long as the record in the EF
      * @param pin2 for CHV2 operations, otherwist must be null
@@ -348,6 +431,7 @@
         int size;
         int fileid;
         int recordSize[];
+        String path = null;
 
         try {
             switch (msg.what) {
@@ -389,6 +473,7 @@
                 }
 
                 data = result.payload;
+                path = lc.mPath;
 
                 if (TYPE_EF != data[RESPONSE_DATA_FILE_TYPE]) {
                     throw new IccFileTypeMismatch();
@@ -409,7 +494,10 @@
                      lc.results = new ArrayList<byte[]>(lc.mCountRecords);
                  }
 
-                 mCi.iccIOForApp(COMMAND_READ_RECORD, lc.mEfid, getEFPath(lc.mEfid),
+                 if (path == null) {
+                     path = getEFPath(lc.mEfid);
+                 }
+                 mCi.iccIOForApp(COMMAND_READ_RECORD, lc.mEfid, path,
                          lc.mRecordNum,
                          READ_RECORD_MODE_ABSOLUTE,
                          lc.mRecordSize, null, null, mAid,
@@ -452,6 +540,7 @@
                 lc = (LoadLinearFixedContext) ar.userObj;
                 result = (IccIoResult) ar.result;
                 response = lc.mOnLoaded;
+                path = lc.mPath;
 
                 if (processException(response, (AsyncResult) msg.obj)) {
                     break;
@@ -467,7 +556,11 @@
                     if (lc.mRecordNum > lc.mCountRecords) {
                         sendResult(response, lc.results, null);
                     } else {
-                        mCi.iccIOForApp(COMMAND_READ_RECORD, lc.mEfid, getEFPath(lc.mEfid),
+                        if (path == null) {
+                            path = getEFPath(lc.mEfid);
+                        }
+
+                        mCi.iccIOForApp(COMMAND_READ_RECORD, lc.mEfid, path,
                                     lc.mRecordNum,
                                     READ_RECORD_MODE_ABSOLUTE,
                                     lc.mRecordSize, null, null, mAid,
diff --git a/src/java/com/android/internal/telephony/uicc/IccRecords.java b/src/java/com/android/internal/telephony/uicc/IccRecords.java
index 77de1fa..d91c4e7 100644
--- a/src/java/com/android/internal/telephony/uicc/IccRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IccRecords.java
@@ -23,12 +23,17 @@
 import android.os.Registrant;
 import android.os.RegistrantList;
 
+import android.telephony.Rlog;
 import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.uicc.IccCardApplicationStatus.AppState;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -36,6 +41,7 @@
  */
 public abstract class IccRecords extends Handler implements IccConstants {
     protected static final boolean DBG = true;
+    protected static final boolean VDBG = false; // STOPSHIP if true
 
     // ***** Instance Variables
     protected AtomicBoolean mDestroyed = new AtomicBoolean(false);
@@ -78,6 +84,8 @@
     private String mSpn;
 
     protected String mGid1;
+    protected String mGid2;
+    protected String mPrefLang;
 
     private final Object mLock = new Object();
 
@@ -124,7 +132,7 @@
                 + " newVoiceMailNum=" + mNewVoiceMailNum
                 + " newVoiceMailTag=" + mNewVoiceMailTag
                 + " isVoiceMailFixed=" + mIsVoiceMailFixed
-                + " mImsi=" + mImsi
+                + (VDBG ? (" mImsi=" + mImsi) : "")
                 + " mncLength=" + mMncLength
                 + " mailboxIndex=" + mMailboxIndex
                 + " spn=" + mSpn;
@@ -284,6 +292,14 @@
     }
 
     /**
+     * Get the Group Identifier Level 2 (GID2) on a SIM.
+     * @return null if SIM is not yet ready
+     */
+    public String getGid2() {
+        return null;
+    }
+
+    /**
      * Set subscriber number to SIM record
      *
      * The subscriber number is stored in EF_MSISDN (TS 51.011)
@@ -481,6 +497,49 @@
         }
     }
 
+    /**
+     * Returns the SIM language derived from the EF-LI and EF-PL sim records.
+     */
+    public String getSimLanguage() {
+        return mPrefLang;
+    }
+
+    protected void setSimLanguage(byte[] efLi, byte[] efPl) {
+        String[] locales = mContext.getAssets().getLocales();
+        try {
+            mPrefLang = findBestLanguage(efLi, locales);
+        } catch (UnsupportedEncodingException uee) {
+            log("Unable to parse EF-LI: " + Arrays.toString(efLi));
+        }
+
+        if (mPrefLang == null) {
+            try {
+                mPrefLang = findBestLanguage(efPl, locales);
+            } catch (UnsupportedEncodingException uee) {
+                log("Unable to parse EF-PL: " + Arrays.toString(efLi));
+            }
+        }
+    }
+
+    protected static String findBestLanguage(byte[] languages, String[] locales)
+            throws UnsupportedEncodingException {
+        if ((languages == null) || (locales == null)) return null;
+
+        // Each 2-bytes consists of one language
+        for (int i = 0; (i + 1) < languages.length; i += 2) {
+            String lang = new String(languages, i, 2, "ISO-8859-1");
+            for (int j = 0; j < locales.length; j++) {
+                if (locales[j] != null && locales[j].length() >= 2 &&
+                        locales[j].substring(0, 2).equalsIgnoreCase(lang)) {
+                    return lang;
+                }
+            }
+        }
+
+        // no match found. return null
+        return null;
+    }
+
     protected abstract void onRecordLoaded();
 
     protected abstract void onAllRecordsLoaded();
@@ -656,14 +715,18 @@
         pw.println(" mRecordsToLoad=" + mRecordsToLoad);
         pw.println(" mRdnCache=" + mAdnCache);
         pw.println(" iccid=" + mIccId);
-        pw.println(" mMsisdn=" + mMsisdn);
+        if (TextUtils.isEmpty(mMsisdn)) {
+            pw.println(" mMsisdn=null");
+        } else {
+            pw.println(" mMsisdn=" + (VDBG ? mMsisdn : "XXX"));
+        }
         pw.println(" mMsisdnTag=" + mMsisdnTag);
         pw.println(" mVoiceMailNum=" + mVoiceMailNum);
         pw.println(" mVoiceMailTag=" + mVoiceMailTag);
         pw.println(" mNewVoiceMailNum=" + mNewVoiceMailNum);
         pw.println(" mNewVoiceMailTag=" + mNewVoiceMailTag);
         pw.println(" mIsVoiceMailFixed=" + mIsVoiceMailFixed);
-        pw.println(" mImsi=" + mImsi);
+        if (VDBG) pw.println(" mImsi=" + mImsi);
         pw.println(" mMncLength=" + mMncLength);
         pw.println(" mMailboxIndex=" + mMailboxIndex);
         pw.println(" mSpn=" + mSpn);
diff --git a/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java b/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
index ff6edae..057674d 100644
--- a/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/IsimUiccRecords.java
@@ -47,7 +47,8 @@
     protected static final String LOG_TAG = "IsimUiccRecords";
 
     private static final boolean DBG = true;
-    private static final boolean DUMP_RECORDS = true;   // Note: PII is logged when this is true
+    private static final boolean DUMP_RECORDS = false;  // Note: PII is logged when this is true
+                                                        // STOPSHIP if true
     public static final String INTENT_ISIM_REFRESH = "com.android.intent.isim_refresh";
 
     private static final int EVENT_APP_READY = 1;
@@ -58,8 +59,8 @@
     private String mIsimImpi;               // IMS private user identity
     private String mIsimDomain;             // IMS home network domain name
     private String[] mIsimImpu;             // IMS public user identity(s)
-    private String mIsimIst;             // IMS Service Table
-    private String[] mIsimPcscf;             // IMS Proxy Call Session Control Function
+    private String mIsimIst;                // IMS Service Table
+    private String[] mIsimPcscf;            // IMS Proxy Call Session Control Function
     private String auth_rsp;
 
     private final Object mLock = new Object();
@@ -69,11 +70,11 @@
     @Override
     public String toString() {
         return "IsimUiccRecords: " + super.toString()
-                + " mIsimImpi=" + mIsimImpi
+                + (DUMP_RECORDS ? (" mIsimImpi=" + mIsimImpi
                 + " mIsimDomain=" + mIsimDomain
                 + " mIsimImpu=" + mIsimImpu
                 + " mIsimIst=" + mIsimIst
-                + " mIsimPcscf=" + mIsimPcscf;
+                + " mIsimPcscf=" + mIsimPcscf) : "");
     }
 
     public IsimUiccRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
@@ -500,11 +501,13 @@
         pw.println("IsimRecords: " + this);
         pw.println(" extends:");
         super.dump(fd, pw, args);
-        pw.println(" mIsimImpi=" + mIsimImpi);
-        pw.println(" mIsimDomain=" + mIsimDomain);
-        pw.println(" mIsimImpu[]=" + Arrays.toString(mIsimImpu));
-        pw.println(" mIsimIst" + mIsimIst);
-        pw.println(" mIsimPcscf"+mIsimPcscf);
+        if (DUMP_RECORDS) {
+            pw.println(" mIsimImpi=" + mIsimImpi);
+            pw.println(" mIsimDomain=" + mIsimDomain);
+            pw.println(" mIsimImpu[]=" + Arrays.toString(mIsimImpu));
+            pw.println(" mIsimIst" + mIsimIst);
+            pw.println(" mIsimPcscf" + mIsimPcscf);
+        }
         pw.flush();
     }
 
diff --git a/src/java/com/android/internal/telephony/uicc/RuimRecords.java b/src/java/com/android/internal/telephony/uicc/RuimRecords.java
index 3b91de4..965fd63 100644
--- a/src/java/com/android/internal/telephony/uicc/RuimRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/RuimRecords.java
@@ -29,7 +29,6 @@
 import android.os.SystemProperties;
 import android.telephony.SubscriptionManager;
 import android.telephony.Rlog;
-import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
 import android.content.res.Resources;
@@ -373,7 +372,7 @@
         @Override
         public void onRecordLoaded(AsyncResult ar) {
             byte[] data = (byte[]) ar.result;
-            if (DBG) log("CSIM_IMSIM=" + IccUtils.bytesToHexString(data));
+            if (VDBG) log("CSIM_IMSIM=" + IccUtils.bytesToHexString(data));
             // C.S0065 section 5.2.2 for IMSI_M encoding
             // C.S0005 section 2.3.1 for MIN encoding in IMSI_M.
             boolean provisioned = ((data[7] & 0x80) == 0x80);
@@ -738,56 +737,6 @@
         return localeLangs;
     }
 
-    private String findBestLanguage(byte[] languages) {
-        final String[] assetLanguages = getAssetLanguages(mContext);
-
-        if ((languages == null) || (assetLanguages == null)) return null;
-
-        // Each 2-bytes consists of one language
-        for (int i = 0; (i + 1) < languages.length; i += 2) {
-            final String lang;
-            try {
-                lang = new String(languages, i, 2, "ISO-8859-1");
-            } catch(java.io.UnsupportedEncodingException e) {
-                log("Failed to parse SIM language records");
-                continue;
-            }
-
-            for (int j = 0; j < assetLanguages.length; j++) {
-                if (assetLanguages[j].equals(lang)) {
-                    return lang;
-                }
-            }
-        }
-
-        // no match found. return null
-        return null;
-    }
-
-    private void setLocaleFromCsim() {
-        String prefLang = null;
-        // check EFli then EFpl
-        prefLang = findBestLanguage(mEFli);
-
-        if (prefLang == null) {
-            prefLang = findBestLanguage(mEFpl);
-        }
-
-        if (prefLang != null) {
-            // check country code from SIM
-            String imsi = getIMSI();
-            String country = null;
-            if (imsi != null) {
-                country = MccTable.countryCodeForMcc(
-                                    Integer.parseInt(imsi.substring(0,3)));
-            }
-            log("Setting locale to " + prefLang + "_" + country);
-            MccTable.setSystemLocale(mContext, prefLang, country);
-        } else {
-            log ("No suitable CSIM selected locale");
-        }
-    }
-
     @Override
     protected void onRecordLoaded() {
         // One record loaded successfully or failed, In either case
@@ -823,7 +772,7 @@
             }
 
             if (!TextUtils.isEmpty(mImsi)) {
-                log("onAllRecordsLoaded set mcc imsi=" + mImsi);
+                log("onAllRecordsLoaded set mcc imsi=" + (VDBG ? ("=" + mImsi) : ""));
                 mTelephonyManager.setSimCountryIsoForPhone(
                         mParentApp.getPhoneId(),
                         MccTable.countryCodeForMcc(
@@ -833,7 +782,11 @@
             }
         }
 
-        setLocaleFromCsim();
+        Resources resource = Resources.getSystem();
+        if (resource.getBoolean(com.android.internal.R.bool.config_use_sim_language_file)) {
+            setSimLanguage(mEFli, mEFpl);
+        }
+
         mRecordsLoadedRegistrants.notifyRegistrants(
             new AsyncResult(null, null, null));
 
@@ -869,16 +822,13 @@
                 obtainMessage(EVENT_GET_ICCID_DONE));
         mRecordsToLoad++;
 
-        Resources resource = Resources.getSystem();
-        if (resource.getBoolean(com.android.internal.R.bool.config_use_sim_language_file)) {
-            mFh.loadEFTransparent(EF_PL,
-                    obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfPlLoaded()));
-            mRecordsToLoad++;
+        mFh.loadEFTransparent(EF_PL,
+                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfPlLoaded()));
+        mRecordsToLoad++;
 
-            mFh.loadEFTransparent(EF_CSIM_LI,
-                    obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimLiLoaded()));
-            mRecordsToLoad++;
-        }
+        mFh.loadEFTransparent(EF_CSIM_LI,
+                obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimLiLoaded()));
+        mRecordsToLoad++;
 
         mFh.loadEFTransparent(EF_CSIM_SPN,
                 obtainMessage(EVENT_GET_ICC_RECORD_DONE, new EfCsimSpnLoaded()));
diff --git a/src/java/com/android/internal/telephony/uicc/SIMFileHandler.java b/src/java/com/android/internal/telephony/uicc/SIMFileHandler.java
index c1db5f3..8412b4e 100644
--- a/src/java/com/android/internal/telephony/uicc/SIMFileHandler.java
+++ b/src/java/com/android/internal/telephony/uicc/SIMFileHandler.java
@@ -55,6 +55,7 @@
         case EF_SST:
         case EF_CFIS:
         case EF_GID1:
+        case EF_GID2:
             return MF_SIM + DF_GSM;
 
         case EF_MAILBOX_CPHS:
diff --git a/src/java/com/android/internal/telephony/uicc/SIMRecords.java b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
index f43ab8e..3acbd4d 100644
--- a/src/java/com/android/internal/telephony/uicc/SIMRecords.java
+++ b/src/java/com/android/internal/telephony/uicc/SIMRecords.java
@@ -163,6 +163,7 @@
     private static final int EVENT_GET_CSP_CPHS_DONE = 33;
     private static final int EVENT_GET_GID1_DONE = 34;
     private static final int EVENT_APP_LOCKED = 35;
+    private static final int EVENT_GET_GID2_DONE = 36;
 
     // Lookup table for carriers known to produce SIMs which incorrectly indicate MNC length.
 
@@ -242,6 +243,7 @@
         mSpdiNetworks = null;
         mPnnHomeName = null;
         mGid1 = null;
+        mGid2 = null;
 
         mAdnCache.reset();
 
@@ -279,10 +281,32 @@
     }
 
     @Override
+    public String getGid2() {
+        return mGid2;
+    }
+
+    @Override
     public UsimServiceTable getUsimServiceTable() {
         return mUsimServiceTable;
     }
 
+    private int getExtFromEf(int ef) {
+        int ext;
+        switch (ef) {
+            case EF_MSISDN:
+                /* For USIM apps use EXT5. (TS 31.102 Section 4.2.37) */
+                if (mParentApp.getType() == AppType.APPTYPE_USIM) {
+                    ext = EF_EXT5;
+                } else {
+                    ext = EF_EXT1;
+                }
+                break;
+            default:
+                ext = EF_EXT1;
+        }
+        return ext;
+    }
+
     /**
      * Set subscriber number to SIM record
      *
@@ -309,10 +333,9 @@
 
         if(DBG) log("Set MSISDN: " + mNewMsisdnTag + " " + /*mNewMsisdn*/ "xxxxxxx");
 
-
         AdnRecord adn = new AdnRecord(mNewMsisdnTag, mNewMsisdn);
 
-        new AdnRecordLoader(mFh).updateEF(adn, EF_MSISDN, EF_EXT1, 1, null,
+        new AdnRecordLoader(mFh).updateEF(adn, EF_MSISDN, getExtFromEf(EF_MSISDN), 1, null,
                 obtainMessage(EVENT_SET_MSISDN_DONE, onComplete));
     }
 
@@ -1196,6 +1219,22 @@
 
                 break;
 
+            case EVENT_GET_GID2_DONE:
+                isRecordLoadResponse = true;
+
+                ar = (AsyncResult)msg.obj;
+                data =(byte[])ar.result;
+
+                if (ar.exception != null) {
+                    loge("Exception in get GID2 " + ar.exception);
+                    mGid2 = null;
+                    break;
+                }
+                mGid2 = IccUtils.bytesToHexString(data);
+                log("GID2: " + mGid2);
+
+                break;
+
             default:
                 super.handleMessage(msg);   // IccRecords handles generic record load responses
 
@@ -1254,6 +1293,24 @@
                 if (DBG) log("SIM Refresh called for EF_FDN");
                 mParentApp.queryFdn();
                 break;
+            case EF_MSISDN:
+                mRecordsToLoad++;
+                log("SIM Refresh called for EF_MSISDN");
+                new AdnRecordLoader(mFh).loadFromEF(EF_MSISDN, getExtFromEf(EF_MSISDN), 1,
+                        obtainMessage(EVENT_GET_MSISDN_DONE));
+                break;
+            case EF_CFIS:
+                mRecordsToLoad++;
+                log("SIM Refresh called for EF_CFIS");
+                mFh.loadEFLinearFixed(EF_CFIS,
+                        1, obtainMessage(EVENT_GET_CFIS_DONE));
+                break;
+            case EF_CFF_CPHS:
+                mRecordsToLoad++;
+                log("SIM Refresh called for EF_CFF_CPHS");
+                mFh.loadEFTransparent(EF_CFF_CPHS,
+                        obtainMessage(EVENT_GET_CFF_DONE));
+                break;
             default:
                 // For now, fetch all records if this is not a
                 // voicemail number.
@@ -1362,56 +1419,6 @@
         }
     }
 
-    private String findBestLanguage(byte[] languages) {
-        String bestMatch = null;
-        String[] locales = mContext.getAssets().getLocales();
-
-        if ((languages == null) || (locales == null)) return null;
-
-        // Each 2-bytes consists of one language
-        for (int i = 0; (i + 1) < languages.length; i += 2) {
-            try {
-                String lang = new String(languages, i, 2, "ISO-8859-1");
-                if (DBG) log ("languages from sim = " + lang);
-                for (int j = 0; j < locales.length; j++) {
-                    if (locales[j] != null && locales[j].length() >= 2 &&
-                            locales[j].substring(0, 2).equalsIgnoreCase(lang)) {
-                        return lang;
-                    }
-                }
-                if (bestMatch != null) break;
-            } catch(java.io.UnsupportedEncodingException e) {
-                log ("Failed to parse USIM language records" + e);
-            }
-        }
-        // no match found. return null
-        return null;
-    }
-
-    private void setLocaleFromUsim() {
-        String prefLang = null;
-        // check EFli then EFpl
-        prefLang = findBestLanguage(mEfLi);
-
-        if (prefLang == null) {
-            prefLang = findBestLanguage(mEfPl);
-        }
-
-        if (prefLang != null) {
-            // check country code from SIM
-            String imsi = getIMSI();
-            String country = null;
-            if (imsi != null) {
-                country = MccTable.countryCodeForMcc(
-                                    Integer.parseInt(imsi.substring(0,3)));
-            }
-            if (DBG) log("Setting locale to " + prefLang + "_" + country);
-            MccTable.setSystemLocale(mContext, prefLang, country);
-        } else {
-            if (DBG) log ("No suitable USIM selected locale");
-        }
-    }
-
     @Override
     protected void onRecordLoaded() {
         // One record loaded successfully or failed, In either case
@@ -1431,7 +1438,12 @@
     protected void onAllRecordsLoaded() {
         if (DBG) log("record load complete");
 
-        setLocaleFromUsim();
+        Resources resource = Resources.getSystem();
+        if (resource.getBoolean(com.android.internal.R.bool.config_use_sim_language_file)) {
+            setSimLanguage(mEfLi, mEfPl);
+        } else {
+            if (DBG) log ("Not using EF LI/EF PL");
+        }
 
         if (mParentApp.getState() == AppState.APPSTATE_PIN ||
                mParentApp.getState() == AppState.APPSTATE_PUK) {
@@ -1457,7 +1469,7 @@
         }
 
         if (!TextUtils.isEmpty(mImsi)) {
-            log("onAllRecordsLoaded set mcc imsi=" + mImsi);
+            log("onAllRecordsLoaded set mcc imsi" + (VDBG ? ("=" + mImsi) : ""));
             mTelephonyManager.setSimCountryIsoForPhone(
                     mParentApp.getPhoneId(), MccTable.countryCodeForMcc(
                     Integer.parseInt(mImsi.substring(0,3))));
@@ -1502,12 +1514,6 @@
     }
 
     private void loadEfLiAndEfPl() {
-        Resources resource = Resources.getSystem();
-        if (!resource.getBoolean(com.android.internal.R.bool.config_use_sim_language_file)) {
-            if (DBG) log ("Not using EF LI/EF PL");
-            return;
-        }
-
         if (mParentApp.getType() == AppType.APPTYPE_USIM) {
             mRecordsRequested = true;
             mFh.loadEFTransparent(EF_LI,
@@ -1533,7 +1539,7 @@
 
         // FIXME should examine EF[MSISDN]'s capability configuration
         // to determine which is the voice/data/fax line
-        new AdnRecordLoader(mFh).loadFromEF(EF_MSISDN, EF_EXT1, 1,
+        new AdnRecordLoader(mFh).loadFromEF(EF_MSISDN, getExtFromEf(EF_MSISDN), 1,
                     obtainMessage(EVENT_GET_MSISDN_DONE));
         mRecordsToLoad++;
 
@@ -1586,6 +1592,9 @@
         mFh.loadEFTransparent(EF_GID1, obtainMessage(EVENT_GET_GID1_DONE));
         mRecordsToLoad++;
 
+        mFh.loadEFTransparent(EF_GID2, obtainMessage(EVENT_GET_GID2_DONE));
+        mRecordsToLoad++;
+
         loadEfLiAndEfPl();
 
         // XXX should seek instead of examining them all
@@ -1920,6 +1929,7 @@
         pw.println(" mPnnHomeName=" + mPnnHomeName);
         pw.println(" mUsimServiceTable=" + mUsimServiceTable);
         pw.println(" mGid1=" + mGid1);
+        pw.println(" mGid2=" + mGid2);
         pw.flush();
     }
 }
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCard.java b/src/java/com/android/internal/telephony/uicc/UiccCard.java
index f77a16c..f40e7a8 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCard.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCard.java
@@ -27,6 +27,7 @@
 import android.content.pm.Signature;
 import android.content.res.Resources;
 import android.os.AsyncResult;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.Message;
 import android.os.PowerManager;
@@ -36,6 +37,7 @@
 import android.telephony.Rlog;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
+import android.util.LocalLog;
 import android.view.WindowManager;
 
 import com.android.internal.telephony.CommandsInterface;
@@ -94,6 +96,8 @@
     private static final int EVENT_SIM_IO_DONE = 19;
     private static final int EVENT_CARRIER_PRIVILIGES_LOADED = 20;
 
+    private static final LocalLog mLocalLog = new LocalLog(100);
+
     private int mPhoneId;
 
     public UiccCard(Context c, CommandsInterface ci, IccCardStatus ics) {
@@ -364,8 +368,8 @@
                 case EVENT_SIM_IO_DONE:
                     AsyncResult ar = (AsyncResult)msg.obj;
                     if (ar.exception != null) {
-                       if (DBG)
-                         log("Error in SIM access with exception" + ar.exception);
+                        loglocal("Exception: " + ar.exception);
+                        log("Error in SIM access with exception" + ar.exception);
                     }
                     AsyncResult.forMessage((Message)ar.userObj, ar.result, ar.exception);
                     ((Message)ar.userObj).sendToTarget();
@@ -483,6 +487,8 @@
      * Exposes {@link CommandsInterface.iccOpenLogicalChannel}
      */
     public void iccOpenLogicalChannel(String AID, Message response) {
+        loglocal("Open Logical Channel: " + AID + " by pid:" + Binder.getCallingPid()
+                + " uid:" + Binder.getCallingUid());
         mCi.iccOpenLogicalChannel(AID,
                 mHandler.obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE, response));
     }
@@ -491,6 +497,7 @@
      * Exposes {@link CommandsInterface.iccCloseLogicalChannel}
      */
     public void iccCloseLogicalChannel(int channel, Message response) {
+        loglocal("Close Logical Channel: " + channel);
         mCi.iccCloseLogicalChannel(channel,
                 mHandler.obtainMessage(EVENT_CLOSE_LOGICAL_CHANNEL_DONE, response));
     }
@@ -639,6 +646,10 @@
         Rlog.e(LOG_TAG, msg);
     }
 
+    private void loglocal(String msg) {
+        if (DBG) mLocalLog.log(msg);
+    }
+
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("UiccCard:");
         pw.println(" mCi=" + mCi);
@@ -699,5 +710,8 @@
                     + ((Registrant)mCarrierPrivilegeRegistrants.get(i)).getHandler());
         }
         pw.flush();
+        pw.println("mLocalLog:");
+        mLocalLog.dump(fd, pw, args);
+        pw.flush();
     }
 }
diff --git a/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java b/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
index 0aec4d1..aa0bd64 100644
--- a/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
+++ b/src/java/com/android/internal/telephony/uicc/UiccCarrierPrivilegeRules.java
@@ -29,6 +29,7 @@
 import android.telephony.Rlog;
 import android.telephony.TelephonyManager;
 
+import com.android.internal.telephony.CommandException;
 import com.android.internal.telephony.CommandsInterface;
 import com.android.internal.telephony.uicc.IccUtils;
 
@@ -68,6 +69,7 @@
  */
 public class UiccCarrierPrivilegeRules extends Handler {
     private static final String LOG_TAG = "UiccCarrierPrivilegeRules";
+    private static final boolean DBG = false;
 
     private static final String AID = "A00000015141434C00";
     private static final int CLA = 0x80;
@@ -117,6 +119,10 @@
     private static final int STATE_LOADED   = 1;
     private static final int STATE_ERROR    = 2;
 
+    // Max number of retries for open logical channel, interval is 10s.
+    private static final int MAX_RETRY = 1;
+    private static final int RETRY_INTERVAL_MS = 10000;
+
     // Describes a single rule.
     private static class AccessRule {
         public byte[] certificateHash;
@@ -169,12 +175,12 @@
                 length = Integer.parseInt(data.substring(offset + 2, offset + 2 + numBytes * 2), 16) * 2;
                 lengthBytes = data.substring(offset, offset + 2 + numBytes * 2);
             }
-            Rlog.d(LOG_TAG, "TLV parseLength length=" + length + "lenghtBytes: " + lengthBytes);
+            log("TLV parseLength length=" + length + "lenghtBytes: " + lengthBytes);
             return lengthBytes;
         }
 
         public String parse(String data, boolean shouldConsumeAll) {
-            Rlog.d(LOG_TAG, "Parse TLV: " + tag);
+            log("Parse TLV: " + tag);
             if (!data.startsWith(tag)) {
                 throw new IllegalArgumentException("Tags don't match.");
             }
@@ -186,7 +192,7 @@
             parseLength(data);
             index += lengthBytes.length();
 
-            Rlog.d(LOG_TAG, "index="+index+" length="+length+"data.length="+data.length());
+            log("index="+index+" length="+length+"data.length="+data.length());
             int remainingLength = data.length() - (index + length);
             if (remainingLength < 0) {
                 throw new IllegalArgumentException("Not enough data.");
@@ -196,7 +202,7 @@
             }
             value = data.substring(index, index + length);
 
-            Rlog.d(LOG_TAG, "Got TLV: " + tag + "," + length + "," + value);
+            log("Got TLV: " + tag + "," + length + "," + value);
 
             return data.substring(index + length);
         }
@@ -209,18 +215,29 @@
     private Message mLoadedCallback;
     private String mStatusMessage;  // Only used for debugging.
     private int mChannelId; // Channel Id for communicating with UICC.
+    private int mRetryCount;  // Number of retries for open logical channel.
+    private final Runnable mRetryRunnable = new Runnable() {
+        @Override
+        public void run() {
+            openChannel();
+        }
+    };
+
+    private void openChannel() {
+        // Send open logical channel request.
+        mUiccCard.iccOpenLogicalChannel(AID,
+            obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE, null));
+    }
 
     public UiccCarrierPrivilegeRules(UiccCard uiccCard, Message loadedCallback) {
-        Rlog.d(LOG_TAG, "Creating UiccCarrierPrivilegeRules");
+        log("Creating UiccCarrierPrivilegeRules");
         mUiccCard = uiccCard;
         mState = new AtomicInteger(STATE_LOADING);
         mStatusMessage = "Not loaded.";
         mLoadedCallback = loadedCallback;
         mRules = "";
 
-        // Start loading the rules.
-        mUiccCard.iccOpenLogicalChannel(AID,
-            obtainMessage(EVENT_OPEN_LOGICAL_CHANNEL_DONE, null));
+        openChannel();
     }
 
     /**
@@ -238,29 +255,25 @@
      * @return Access status.
      */
     public int getCarrierPrivilegeStatus(Signature signature, String packageName) {
-        Rlog.d(LOG_TAG, "hasCarrierPrivileges: " + signature + " : " + packageName);
+        log("hasCarrierPrivileges: " + signature + " : " + packageName);
         int state = mState.get();
         if (state == STATE_LOADING) {
-            Rlog.d(LOG_TAG, "Rules not loaded.");
+            log("Rules not loaded.");
             return TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
         } else if (state == STATE_ERROR) {
-            Rlog.d(LOG_TAG, "Error loading rules.");
+            log("Error loading rules.");
             return TelephonyManager.CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES;
         }
 
         // SHA-1 is for backward compatible support only, strongly discouraged for new use.
         byte[] certHash = getCertHash(signature, "SHA-1");
         byte[] certHash256 = getCertHash(signature, "SHA-256");
-        Rlog.d(LOG_TAG, "Checking SHA1: " + IccUtils.bytesToHexString(certHash) + " : " + packageName);
-        Rlog.d(LOG_TAG, "Checking SHA256: " + IccUtils.bytesToHexString(certHash256) + " : " + packageName);
         for (AccessRule ar : mAccessRules) {
             if (ar.matches(certHash, packageName) || ar.matches(certHash256, packageName)) {
-                Rlog.d(LOG_TAG, "Match found!");
                 return TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
             }
         }
 
-        Rlog.d(LOG_TAG, "No matching rule found. Returning false.");
         return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
     }
 
@@ -273,8 +286,11 @@
      */
     public int getCarrierPrivilegeStatus(PackageManager packageManager, String packageName) {
         try {
+            // Include DISABLED_UNTIL_USED components. This facilitates cases where a carrier app
+            // is disabled by default, and some other component wants to enable it when it has
+            // gained carrier privileges (as an indication that a matching SIM has been inserted).
             PackageInfo pInfo = packageManager.getPackageInfo(packageName,
-                PackageManager.GET_SIGNATURES);
+                PackageManager.GET_SIGNATURES | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS);
             Signature[] signatures = pInfo.signatures;
             for (Signature sig : signatures) {
                 int accessStatus = getCarrierPrivilegeStatus(sig, pInfo.packageName);
@@ -361,19 +377,29 @@
         switch (msg.what) {
 
           case EVENT_OPEN_LOGICAL_CHANNEL_DONE:
-              Rlog.d(LOG_TAG, "EVENT_OPEN_LOGICAL_CHANNEL_DONE");
+              log("EVENT_OPEN_LOGICAL_CHANNEL_DONE");
               ar = (AsyncResult) msg.obj;
               if (ar.exception == null && ar.result != null) {
                   mChannelId = ((int[]) ar.result)[0];
                   mUiccCard.iccTransmitApduLogicalChannel(mChannelId, CLA, COMMAND, P1, P2, P3, DATA,
                       obtainMessage(EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE, new Integer(mChannelId)));
               } else {
-                  updateState(STATE_ERROR, "Error opening channel");
+                  // MISSING_RESOURCE could be due to logical channels temporarily unavailable,
+                  // so we retry up to MAX_RETRY times, with an interval of RETRY_INTERVAL_MS.
+                  if (ar.exception instanceof CommandException && mRetryCount < MAX_RETRY &&
+                      ((CommandException) (ar.exception)).getCommandError() ==
+                              CommandException.Error.MISSING_RESOURCE) {
+                      mRetryCount++;
+                      removeCallbacks(mRetryRunnable);
+                      postDelayed(mRetryRunnable, RETRY_INTERVAL_MS);
+                  } else {
+                      updateState(STATE_ERROR, "Error opening channel: " + ar.exception);
+                  }
               }
               break;
 
           case EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE:
-              Rlog.d(LOG_TAG, "EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE");
+              log("EVENT_TRANSMIT_LOGICAL_CHANNEL_DONE");
               ar = (AsyncResult) msg.obj;
               if (ar.exception == null && ar.result != null) {
                   IccIoResult response = (IccIoResult) ar.result;
@@ -409,7 +435,7 @@
               break;
 
           case EVENT_CLOSE_LOGICAL_CHANNEL_DONE:
-              Rlog.d(LOG_TAG, "EVENT_CLOSE_LOGICAL_CHANNEL_DONE");
+              log("EVENT_CLOSE_LOGICAL_CHANNEL_DONE");
               break;
 
           default:
@@ -422,17 +448,17 @@
      * For long payload, we need to fetch it repeatly before start parsing it.
      */
     private boolean isDataComplete() {
-        Rlog.d(LOG_TAG, "isDataComplete mRules:" + mRules);
+        log("isDataComplete mRules:" + mRules);
         if (mRules.startsWith(TAG_ALL_REF_AR_DO)) {
             TLV allRules = new TLV(TAG_ALL_REF_AR_DO);
             String lengthBytes = allRules.parseLength(mRules);
-            Rlog.d(LOG_TAG, "isDataComplete lengthBytes: " + lengthBytes);
+            log("isDataComplete lengthBytes: " + lengthBytes);
             if (mRules.length() == TAG_ALL_REF_AR_DO.length() + lengthBytes.length() +
                                    allRules.length) {
-                Rlog.d(LOG_TAG, "isDataComplete yes");
+                log("isDataComplete yes");
                 return true;
             } else {
-                Rlog.d(LOG_TAG, "isDataComplete no");
+                log("isDataComplete no");
                 return false;
             }
         } else {
@@ -444,7 +470,7 @@
      * Parses the rules from the input string.
      */
     private static List<AccessRule> parseRules(String rules) {
-        Rlog.d(LOG_TAG, "Got rules: " + rules);
+        log("Got rules: " + rules);
 
         TLV allRefArDo = new TLV(TAG_ALL_REF_AR_DO); //FF40
         allRefArDo.parse(rules, true);
@@ -468,7 +494,7 @@
      * Parses a single rule.
      */
     private static AccessRule parseRefArdo(String rule) {
-        Rlog.d(LOG_TAG, "Got rule: " + rule);
+        log("Got rule: " + rule);
 
         String certificateHash = null;
         String packageName = null;
@@ -510,18 +536,14 @@
 
                 TLV permDo = new TLV(TAG_PERM_AR_DO); //DB
                 permDo.parse(arDo.value, true);
-                Rlog.e(LOG_TAG, permDo.value);
             } else  {
                 // Spec requires it must be either TAG_REF_DO or TAG_AR_DO.
                 throw new RuntimeException("Invalid Rule type");
             }
         }
 
-        Rlog.e(LOG_TAG, "Adding: " + certificateHash + " : " + packageName + " : " + accessType);
-
         AccessRule accessRule = new AccessRule(IccUtils.hexStringToBytes(certificateHash),
             packageName, accessType);
-        Rlog.e(LOG_TAG, "Parsed rule: " + accessRule);
         return accessRule;
     }
 
@@ -548,7 +570,10 @@
         }
 
         mStatusMessage = statusMessage;
-        Rlog.e(LOG_TAG, mStatusMessage);
+    }
+
+    private static void log(String msg) {
+        if (DBG) Rlog.d(LOG_TAG, msg);
     }
 
     /**
diff --git a/src/java/com/android/internal/telephony/uicc/UsimFileHandler.java b/src/java/com/android/internal/telephony/uicc/UsimFileHandler.java
index 913aa7c..bd3357e 100644
--- a/src/java/com/android/internal/telephony/uicc/UsimFileHandler.java
+++ b/src/java/com/android/internal/telephony/uicc/UsimFileHandler.java
@@ -36,6 +36,7 @@
     protected String getEFPath(int efid) {
         switch(efid) {
         case EF_SMS:
+        case EF_EXT5:
         case EF_EXT6:
         case EF_MWIS:
         case EF_MBI:
@@ -58,6 +59,7 @@
         case EF_INFO_CPHS:
         case EF_CSP_CPHS:
         case EF_GID1:
+        case EF_GID2:
         case EF_LI:
             return MF_SIM + DF_ADF;
 
diff --git a/src/java/com/google/android/mms/pdu/PduParser.java b/src/java/com/google/android/mms/pdu/PduParser.java
index acd1d04..9fbdb66 100755
--- a/src/java/com/google/android/mms/pdu/PduParser.java
+++ b/src/java/com/google/android/mms/pdu/PduParser.java
@@ -1774,6 +1774,9 @@
                     return THE_FIRST_PART;
                 }
             }
+            // This is not the first part, so append to end (keeping the original order)
+            // Check b/19607294 for details of this change
+            return THE_LAST_PART;
         }
 
         /* check part's content-type */
diff --git a/tests/Android.mk b/tests/Android.mk
new file mode 100644
index 0000000..2027fef
--- /dev/null
+++ b/tests/Android.mk
@@ -0,0 +1,21 @@
+# Copyright (C) 2015 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Include subdirectory makefiles
+# ============================================================
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/telephonytests/Android.mk b/tests/telephonytests/Android.mk
index 13363dc..96c5a9d 100644
--- a/tests/telephonytests/Android.mk
+++ b/tests/telephonytests/Android.mk
@@ -8,6 +8,7 @@
 #LOCAL_STATIC_JAVA_LIBRARIES := librilproto-java
 
 LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common
+LOCAL_STATIC_JAVA_LIBRARIES := mockito-target
 
 LOCAL_PACKAGE_NAME := FrameworksTelephonyTests
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ApnSettingTest.java b/tests/telephonytests/src/com/android/internal/telephony/ApnSettingTest.java.broken
similarity index 95%
rename from tests/telephonytests/src/com/android/internal/telephony/ApnSettingTest.java
rename to tests/telephonytests/src/com/android/internal/telephony/ApnSettingTest.java.broken
index 6e40760..4e5419a 100755
--- a/tests/telephonytests/src/com/android/internal/telephony/ApnSettingTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ApnSettingTest.java.broken
@@ -58,7 +58,7 @@
             assertEquals(a1.types[i], a2.types[i]);
         }
         assertEquals(a1.carrierEnabled, a2.carrierEnabled);
-        assertEquals(a1.bearer, a2.bearer);
+        assertEquals(a1.bearerBitmask, a2.bearerBitmask);
         assertEquals(a1.profileId, a2.profileId);
         assertEquals(a1.modemCognitive, a2.modemCognitive);
         assertEquals(a1.maxConns, a2.maxConns);
@@ -81,7 +81,7 @@
         testString = "Vodafone IT,web.omnitel.it,,,,,,,,,222,10,,DUN";
         expectedApn = new ApnSetting(
                 -1, "22210", "Vodafone IT", "web.omnitel.it", "", "",
-                "", "", "", "", "", 0, dunTypes, "IP", "IP",true, 0,
+                "", "", "", "", "", 0, dunTypes, "IP", "IP",true, 0, 0,
                 0, false, 0, 0, 0, 0, "", "");
         assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
 
@@ -89,7 +89,7 @@
         testString = "[ApnSettingV2] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,14";
         expectedApn = new ApnSetting(
                 -1, "12345", "Name", "apn", "", "",
-                "", "", "", "", "", 0, mmsTypes, "IPV6", "IP", true, 14,
+                "", "", "", "", "", 0, mmsTypes, "IPV6", "IP", true, 14, 0,
                 0, false, 0, 0, 0, 0, "", "");
         assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
 
@@ -97,7 +97,7 @@
         testString = "[ApnSettingV2] Name,apn, ,,,,,,,,123,45,,mms|*,IPV4V6, IP,true,14";
         expectedApn = new ApnSetting(
                 -1, "12345", "Name", "apn", "", "",
-                "", "", "", "", "", 0, mmsTypes, "IPV4V6", "IP", true, 14,
+                "", "", "", "", "", 0, mmsTypes, "IPV4V6", "IP", true, 14, 0,
                 0, false, 0, 0, 0, 0, "", "");
         assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
 
@@ -105,7 +105,7 @@
         testString = "[ApnSettingV3] Name,apn,,,,,,,,,123,45,,mms|*,IPV6,IP,true,14,,,,,,,spn,testspn";
         expectedApn = new ApnSetting(
                 -1, "12345", "Name", "apn", "", "", "", "", "", "", "", 0, mmsTypes, "IPV4V6",
-                "IP",true, 14, 0, false, 0, 0, 0, 0, "spn", "testSpn");
+                "IP",true, 14, 0, 0, false, 0, 0, 0, 0, "spn", "testSpn");
         assertApnSettingEqual(expectedApn, ApnSetting.fromString(testString));
 
         // Return no apn if insufficient fields given.
@@ -141,7 +141,7 @@
         ApnSetting apn =  new ApnSetting(
                 99, "12345", "Name", "apn", "proxy", "port",
                 "mmsc", "mmsproxy", "mmsport", "user", "password", 0,
-                types, "IPV4V6", "IP", true, 14, 0, false, 0, 0, 0, 0, "", "");
+                types, "IPV4V6", "IP", true, 14, 0, 0, false, 0, 0, 0, 0, "", "");
         String expected = "[ApnSettingV2] Name, 99, 12345, apn, proxy, " +
                 "mmsc, mmsproxy, mmsport, port, 0, default | *, " +
                 "IPV4V6, IP, true, 14, 0, false, 0, 0, 0, 0, , ";
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierAppUtilsTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierAppUtilsTest.java
new file mode 100644
index 0000000..61e5b4f
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierAppUtilsTest.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.internal.telephony;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.telephony.TelephonyManager;
+import android.test.InstrumentationTestCase;
+
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+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 int USER_ID = 12345;
+    private static final String CALLING_PACKAGE = "phone";
+
+    @Mock private IPackageManager mPackageManager;
+    @Mock private TelephonyManager mTelephonyManager;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        System.setProperty("dexmaker.dexcache",
+                getInstrumentation().getTargetContext().getCacheDir().getPath());
+        Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
+        MockitoAnnotations.initMocks(this);
+    }
+
+    /** No apps configured - should do nothing. */
+    public void testDisableCarrierAppsUntilPrivileged_EmptyList() {
+        CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+                mTelephonyManager, USER_ID, new String[0]);
+        Mockito.verifyNoMoreInteractions(mPackageManager, mTelephonyManager);
+    }
+
+    /** Configured app is missing - should do nothing. */
+    public void testDisableCarrierAppsUntilPrivileged_MissingApp() throws Exception {
+        Mockito.when(mPackageManager.getApplicationInfo("com.example.missing.app",
+                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(null);
+        CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+                mTelephonyManager, USER_ID, new String[] { "com.example.missing.app" });
+        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
+                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
+                Mockito.anyString());
+        Mockito.verify(mPackageManager, Mockito.never())
+                .grantDefaultPermissionsToEnabledCarrierApps(Mockito.any(String[].class),
+                        Mockito.anyInt());
+        Mockito.verifyNoMoreInteractions(mTelephonyManager);
+    }
+
+    /** Configured app is not bundled with the system - should do nothing. */
+    public void testDisableCarrierAppsUntilPrivileged_NonSystemApp() throws Exception {
+        ApplicationInfo appInfo = new ApplicationInfo();
+        Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
+                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+        CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+                mTelephonyManager, USER_ID, CARRIER_APPS);
+        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
+                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
+                Mockito.anyString());
+        Mockito.verify(mPackageManager, Mockito.never())
+                .grantDefaultPermissionsToEnabledCarrierApps(
+                        Mockito.any(String[].class), Mockito.anyInt());
+        Mockito.verifyNoMoreInteractions(mTelephonyManager);
+    }
+
+    /**
+     * Configured app has privileges, but was disabled by the user - should only grant
+     * permissions.
+     */
+    public void testDisableCarrierAppsUntilPrivileged_HasPrivileges_DisabledUser()
+            throws Exception {
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.packageName = CARRIER_APP;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
+        Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
+                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+        Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
+                .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
+        CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+                mTelephonyManager, USER_ID, CARRIER_APPS);
+        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
+                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
+                Mockito.anyString());
+        Mockito.verify(mPackageManager).grantDefaultPermissionsToEnabledCarrierApps(
+                new String[] {appInfo.packageName}, USER_ID);
+    }
+
+    /** Configured app has privileges, but was disabled - should only grant permissions. */
+    public void testDisableCarrierAppsUntilPrivileged_HasPrivileges_Disabled() throws Exception {
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.packageName = CARRIER_APP;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+        Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
+                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+        Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
+                .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
+        CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+                mTelephonyManager, USER_ID, CARRIER_APPS);
+        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
+                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
+                Mockito.anyString());
+        Mockito.verify(mPackageManager).grantDefaultPermissionsToEnabledCarrierApps(
+                new String[] {appInfo.packageName}, USER_ID);
+    }
+
+    /** Configured app has privileges, and is already enabled - should only grant permissions. */
+    public void testDisableCarrierAppsUntilPrivileged_HasPrivileges_Enabled() throws Exception {
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.packageName = CARRIER_APP;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+        Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
+                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+        Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
+                .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
+        CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+                mTelephonyManager, USER_ID, CARRIER_APPS);
+        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
+                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
+                Mockito.anyString());
+        Mockito.verify(mPackageManager).grantDefaultPermissionsToEnabledCarrierApps(
+                new String[] {appInfo.packageName}, USER_ID);
+    }
+
+    /** Configured /data app has privileges - should only grant permissions. */
+    public void testDisableCarrierAppsUntilPrivileged_HasPrivileges_UpdatedApp() throws Exception {
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.packageName = CARRIER_APP;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+        appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+        Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
+                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+        Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
+                .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
+        CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+                mTelephonyManager, USER_ID, CARRIER_APPS);
+        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
+                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
+                Mockito.anyString());
+        Mockito.verify(mPackageManager).grantDefaultPermissionsToEnabledCarrierApps(
+                new String[] {appInfo.packageName}, USER_ID);
+    }
+
+    /** Configured app has privileges, and is in the default state - should enable. */
+    public void testDisableCarrierAppsUntilPrivileged_HasPrivileges_Default() throws Exception {
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.packageName = CARRIER_APP;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+        Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
+                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+        Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
+                .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
+        CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+                mTelephonyManager, USER_ID, CARRIER_APPS);
+        Mockito.verify(mPackageManager).setApplicationEnabledSetting(
+                CARRIER_APP, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+                PackageManager.DONT_KILL_APP, USER_ID, CALLING_PACKAGE);
+        Mockito.verify(mPackageManager).grantDefaultPermissionsToEnabledCarrierApps(
+                new String[] {appInfo.packageName}, USER_ID);
+    }
+
+    /** Configured app has privileges, and is disabled until used - should enable. */
+    public void testDisableCarrierAppsUntilPrivileged_HasPrivileges_DisabledUntilUsed()
+            throws Exception {
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.packageName = CARRIER_APP;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
+        Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
+                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+        Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
+                .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
+        CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+                mTelephonyManager, USER_ID, CARRIER_APPS);
+        Mockito.verify(mPackageManager).setApplicationEnabledSetting(
+                CARRIER_APP, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+                PackageManager.DONT_KILL_APP, USER_ID, CALLING_PACKAGE);
+        Mockito.verify(mPackageManager).grantDefaultPermissionsToEnabledCarrierApps(
+                new String[] {appInfo.packageName}, USER_ID);
+    }
+
+    /** Configured app has no privileges, and was disabled by the user - should do nothing. */
+    public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_DisabledUser() throws Exception {
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.packageName = CARRIER_APP;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
+        Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
+                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+        Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
+                .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
+        CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+                mTelephonyManager, USER_ID, CARRIER_APPS);
+        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
+                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
+                Mockito.anyString());
+        Mockito.verify(mPackageManager, Mockito.never())
+                .grantDefaultPermissionsToEnabledCarrierApps(
+                        Mockito.any(String[].class), Mockito.anyInt());
+    }
+
+    /** Configured app has no privileges, and was disabled - should do nothing. */
+    public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_Disabled() throws Exception {
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.packageName = CARRIER_APP;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+        Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
+                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+        Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
+                .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
+        CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+                mTelephonyManager, USER_ID, CARRIER_APPS);
+        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
+                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
+                Mockito.anyString());
+        Mockito.verify(mPackageManager, Mockito.never())
+                .grantDefaultPermissionsToEnabledCarrierApps(
+                        Mockito.any(String[].class), Mockito.anyInt());
+    }
+
+    /** Configured app has no privileges, and is explicitly enabled - should do nothing. */
+    public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_Enabled() throws Exception {
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.packageName = CARRIER_APP;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+        Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
+                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+        Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
+                .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
+        CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+                mTelephonyManager, USER_ID, CARRIER_APPS);
+        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
+                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
+                Mockito.anyString());
+        Mockito.verify(mPackageManager, Mockito.never())
+                .grantDefaultPermissionsToEnabledCarrierApps(
+                        Mockito.any(String[].class), Mockito.anyInt());
+    }
+
+    /** Configured /data app has no privileges - should do nothing. */
+    public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_UpdatedApp() throws Exception {
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.packageName = CARRIER_APP;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+        appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+        Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
+                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+        Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
+                .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
+        CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+                mTelephonyManager, USER_ID, CARRIER_APPS);
+        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
+                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
+                Mockito.anyString());
+        Mockito.verify(mPackageManager, Mockito.never())
+                .grantDefaultPermissionsToEnabledCarrierApps(
+                        Mockito.any(String[].class), Mockito.anyInt());
+    }
+
+    /** Configured app has no privileges, and is in the default state - should disable until use. */
+    public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_Default() throws Exception {
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.packageName = CARRIER_APP;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+        Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
+                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+        Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
+                .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
+        CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+                mTelephonyManager, USER_ID, CARRIER_APPS);
+        Mockito.verify(mPackageManager).setApplicationEnabledSetting(
+                CARRIER_APP, PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, 0, USER_ID,
+                CALLING_PACKAGE);
+        Mockito.verify(mPackageManager, Mockito.never())
+                .grantDefaultPermissionsToEnabledCarrierApps(
+                        Mockito.any(String[].class), Mockito.anyInt());
+    }
+
+    /** Configured app has no privileges, and is disabled until used - should do nothing. */
+    public void testDisableCarrierAppsUntilPrivileged_NoPrivileges_DisabledUntilUsed()
+            throws Exception {
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.packageName = CARRIER_APP;
+        appInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+        appInfo.enabledSetting = PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
+        Mockito.when(mPackageManager.getApplicationInfo(CARRIER_APP,
+                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, USER_ID)).thenReturn(appInfo);
+        Mockito.when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(CARRIER_APP))
+                .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
+        CarrierAppUtils.disableCarrierAppsUntilPrivileged(CALLING_PACKAGE, mPackageManager,
+                mTelephonyManager, USER_ID, CARRIER_APPS);
+        Mockito.verify(mPackageManager, Mockito.never()).setApplicationEnabledSetting(
+                Mockito.anyString(), Mockito.anyInt(), Mockito.anyInt(), Mockito.anyInt(),
+                Mockito.anyString());
+        Mockito.verify(mPackageManager, Mockito.never())
+                .grantDefaultPermissionsToEnabledCarrierApps(
+                        Mockito.any(String[].class), Mockito.anyInt());
+    }
+}
+
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SMSDispatcherTest.java b/tests/telephonytests/src/com/android/internal/telephony/SMSDispatcherTest.java.broken
similarity index 100%
rename from tests/telephonytests/src/com/android/internal/telephony/SMSDispatcherTest.java
rename to tests/telephonytests/src/com/android/internal/telephony/SMSDispatcherTest.java.broken
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SimSmsTest.java b/tests/telephonytests/src/com/android/internal/telephony/SimSmsTest.java
index 8b9fd2e..7dc5777 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SimSmsTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SimSmsTest.java
@@ -34,7 +34,9 @@
         ISms sms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
         assertNotNull(sms);
 
-        List<SmsRawData> records = sms.getAllMessagesFromIccEf(ActivityThread.currentPackageName());
+        int preferredSmsSubscription = sms.getPreferredSmsSubscription();
+        List<SmsRawData> records = sms.getAllMessagesFromIccEfForSubscriber(
+                preferredSmsSubscription, ActivityThread.currentPackageName());
         assertNotNull(records);
         assertTrue(records.size() >= 0);
 
diff --git a/tests/telephonytests/src/com/android/internal/telephony/TestPhoneNotifier.java b/tests/telephonytests/src/com/android/internal/telephony/TestPhoneNotifier.java.broken
similarity index 100%
rename from tests/telephonytests/src/com/android/internal/telephony/TestPhoneNotifier.java
rename to tests/telephonytests/src/com/android/internal/telephony/TestPhoneNotifier.java.broken
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java.broken
similarity index 100%
rename from tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java
rename to tests/telephonytests/src/com/android/internal/telephony/gsm/GSMPhoneTest.java.broken
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMTestHandler.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/GSMTestHandler.java.broken
similarity index 100%
rename from tests/telephonytests/src/com/android/internal/telephony/gsm/GSMTestHandler.java
rename to tests/telephonytests/src/com/android/internal/telephony/gsm/GSMTestHandler.java.broken
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java.broken
similarity index 100%
rename from tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java
rename to tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadCommands.java.broken
diff --git a/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadTest.java b/tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadTest.java.broken
similarity index 100%
rename from tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadTest.java
rename to tests/telephonytests/src/com/android/internal/telephony/gsm/UsimDataDownloadTest.java.broken